diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d798ccd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+MIT License
+
+Copyright (c) 2023 liujing04
+Copyright (c) 2023 源文雨
+Copyright (c) 2023 Ftps
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/MIT协议暨相关引用库协议 b/MIT协议暨相关引用库协议
new file mode 100644
index 0000000..dbb6c6d
--- /dev/null
+++ b/MIT协议暨相关引用库协议
@@ -0,0 +1,45 @@
+本软件及其相关代码以MIT协议开源,作者不对软件具备任何控制力,使用软件者、传播软件导出的声音者自负全责。
+如不认可该条款,则不能使用或引用软件包内任何代码和文件。
+
+特此授予任何获得本软件和相关文档文件(以下简称“软件”)副本的人免费使用、复制、修改、合并、出版、分发、再授权和/或销售本软件的权利,以及授予本软件所提供的人使用本软件的权利,但须符合以下条件:
+上述版权声明和本许可声明应包含在软件的所有副本或实质部分中。
+软件是“按原样”提供的,没有任何明示或暗示的保证,包括但不限于适销性、适用于特定目的和不侵权的保证。在任何情况下,作者或版权持有人均不承担因软件或软件的使用或其他交易而产生、产生或与之相关的任何索赔、损害赔偿或其他责任,无论是在合同诉讼、侵权诉讼还是其他诉讼中。
+
+
+The LICENCEs for related libraries are as follows.
+相关引用库协议如下:
+
+ContentVec
+https://github.com/auspicious3000/contentvec/blob/main/LICENSE
+MIT License
+
+VITS
+https://github.com/jaywalnut310/vits/blob/main/LICENSE
+MIT License
+
+HIFIGAN
+https://github.com/jik876/hifi-gan/blob/master/LICENSE
+MIT License
+
+gradio
+https://github.com/gradio-app/gradio/blob/main/LICENSE
+Apache License 2.0
+
+ffmpeg
+https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.LGPLv3
+https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2021-02-28-12-32/ffmpeg-n4.3.2-160-gfbb9368226-win64-lgpl-4.3.zip
+LPGLv3 License
+MIT License
+
+ultimatevocalremovergui
+https://github.com/Anjok07/ultimatevocalremovergui/blob/master/LICENSE
+https://github.com/yang123qwe/vocal_separation_by_uvr5
+MIT License
+
+audio-slicer
+https://github.com/openvpi/audio-slicer/blob/main/LICENSE
+MIT License
+
+PySimpleGUI
+https://github.com/PySimpleGUI/PySimpleGUI/blob/master/license.txt
+LPGLv3 License
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c86746f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,203 @@
+
+
+
Retrieval-based-Voice-Conversion-WebUI
+一个基于VITS的简单易用的变声框架
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+[**更新日志**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/docs/Changelog_CN.md) | [**常见问题解答**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94) | [**AutoDL·5毛钱训练AI歌手**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/Autodl%E8%AE%AD%E7%BB%83RVC%C2%B7AI%E6%AD%8C%E6%89%8B%E6%95%99%E7%A8%8B) | [**对照实验记录**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/Autodl%E8%AE%AD%E7%BB%83RVC%C2%B7AI%E6%AD%8C%E6%89%8B%E6%95%99%E7%A8%8B](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/%E5%AF%B9%E7%85%A7%E5%AE%9E%E9%AA%8C%C2%B7%E5%AE%9E%E9%AA%8C%E8%AE%B0%E5%BD%95)) | [**在线演示**](https://modelscope.cn/studios/FlowerCry/RVCv2demo)
+
+[**English**](./docs/en/README.en.md) | [**中文简体**](./README.md) | [**日本語**](./docs/jp/README.ja.md) | [**한국어**](./docs/kr/README.ko.md) ([**韓國語**](./docs/kr/README.ko.han.md)) | [**Français**](./docs/fr/README.fr.md) | [**Türkçe**](./docs/tr/README.tr.md) | [**Português**](./docs/pt/README.pt.md)
+
+
+
+> 底模使用接近50小时的开源高质量VCTK训练集训练,无版权方面的顾虑,请大家放心使用
+
+> 请期待RVCv3的底模,参数更大,数据更大,效果更好,基本持平的推理速度,需要训练数据量更少。
+
+
+
+ | 训练推理界面 |
+ 实时变声界面 |
+
+
+  |
+  |
+
+
+ | go-webui.bat |
+ go-realtime_gui.bat |
+
+
+ | 可以自由选择想要执行的操作。 |
+ 我们已经实现端到端170ms延迟。如使用ASIO输入输出设备,已能实现端到端90ms延迟,但非常依赖硬件驱动支持。 |
+
+
+
+## 简介
+本仓库具有以下特点
++ 使用top1检索替换输入源特征为训练集特征来杜绝音色泄漏
++ 即便在相对较差的显卡上也能快速训练
++ 使用少量数据进行训练也能得到较好结果(推荐至少收集10分钟低底噪语音数据)
++ 可以通过模型融合来改变音色(借助ckpt处理选项卡中的ckpt-merge)
++ 简单易用的网页界面
++ 可调用UVR5模型来快速分离人声和伴奏
++ 使用最先进的[人声音高提取算法InterSpeech2023-RMVPE](#参考项目)根绝哑音问题,速度快、资源占用小
++ A卡I卡加速支持
+
+点此查看我们的[演示视频](https://www.bilibili.com/video/BV1pm4y1z7Gm/) !
+
+## 环境配置
+以下指令需在 Python 版本大于3.8的环境中执行。
+
+### Windows/Linux/MacOS等平台通用方法
+下列方法任选其一。
+#### 1. 通过 pip 安装依赖
+1. 安装Pytorch及其核心依赖,若已安装则跳过。参考自: https://pytorch.org/get-started/locally/
+```bash
+pip install torch torchvision torchaudio
+```
+2. 如果是 win 系统 + Nvidia Ampere 架构(RTX30xx),根据 #21 的经验,需要指定 pytorch 对应的 cuda 版本
+```bash
+pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+```
+3. 根据自己的显卡安装对应依赖
+- N卡
+```bash
+pip install -r requirements.txt
+```
+- A卡/I卡
+```bash
+pip install -r requirements-dml.txt
+```
+- A卡ROCM(Linux)
+```bash
+pip install -r requirements-amd.txt
+```
+#### 2. 通过 poetry 来安装依赖
+安装 Poetry 依赖管理工具,若已安装则跳过。参考自: https://python-poetry.org/docs/#installation
+```bash
+curl -sSL https://install.python-poetry.org | python3 -
+```
+
+通过 Poetry 安装依赖时,python 建议使用 3.7-3.10 版本,其余版本在安装 llvmlite==0.39.0 时会出现冲突
+```bash
+poetry init -n
+poetry env use "path to your python.exe"
+poetry run pip install -r requirments.txt
+```
+
+### MacOS
+可以通过 `run.sh` 来安装依赖
+```bash
+sh ./run.sh
+```
+
+## 其他预模型准备
+RVC需要其他一些预模型来推理和训练。
+
+你可以从我们的[Hugging Face space](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)下载到这些模型。
+
+### 1. 下载 assets
+以下是一份清单,包括了所有RVC所需的预模型和其他文件的名称。你可以在`tools`文件夹找到下载它们的脚本。
+
+- ./assets/hubert_base
+
+- ./assets/pretrained
+
+- ./assets/uvr5_weights
+
+想使用v2版本模型的话,需要额外下载
+
+- ./assets/pretrained_v2
+
+### 2. 安装 ffmpeg
+若ffmpeg和ffprobe已安装则跳过。
+
+#### Ubuntu/Debian 用户
+```bash
+sudo apt install ffmpeg
+```
+#### MacOS 用户
+```bash
+brew install ffmpeg
+```
+#### Windows 用户
+下载后放置在根目录。
+- 下载[ffmpeg.exe](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffmpeg.exe)
+
+- 下载[ffprobe.exe](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffprobe.exe)
+
+### 3. 下载 rmvpe 人声音高提取算法所需文件
+
+如果你想使用最新的RMVPE人声音高提取算法,则你需要下载音高提取模型参数并放置于RVC根目录。
+
+- 下载[rmvpe.pt](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.pt)
+
+#### 下载 rmvpe 的 dml 环境(可选, A卡/I卡用户)
+
+- 下载[rmvpe.onnx](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.onnx)
+
+### 4. AMD显卡Rocm(可选, 仅Linux)
+
+如果你想基于AMD的Rocm技术在Linux系统上运行RVC,请先在[这里](https://rocm.docs.amd.com/en/latest/deploy/linux/os-native/install.html)安装所需的驱动。
+
+若你使用的是Arch Linux,可以使用pacman来安装所需驱动:
+````
+pacman -S rocm-hip-sdk rocm-opencl-sdk
+````
+对于某些型号的显卡,你可能需要额外配置如下的环境变量(如:RX6700XT):
+````
+export ROCM_PATH=/opt/rocm
+export HSA_OVERRIDE_GFX_VERSION=10.3.0
+````
+同时确保你的当前用户处于`render`与`video`用户组内:
+````
+sudo usermod -aG render $USERNAME
+sudo usermod -aG video $USERNAME
+````
+
+## 开始使用
+### 直接启动
+使用以下指令来启动 WebUI
+```bash
+python webui.py
+```
+
+若先前使用 Poetry 安装依赖,则可以通过以下方式启动WebUI
+```bash
+poetry run python webui.py
+```
+
+### 使用整合包
+下载并解压`RVC-beta.7z`
+#### Windows 用户
+双击`go-webui.bat`
+#### MacOS 用户
+```bash
+sh ./run.sh
+```
+## 参考项目
++ [ContentVec](https://github.com/auspicious3000/contentvec/)
++ [VITS](https://github.com/jaywalnut310/vits)
++ [HIFIGAN](https://github.com/jik876/hifi-gan)
++ [Gradio](https://github.com/gradio-app/gradio)
++ [FFmpeg](https://github.com/FFmpeg/FFmpeg)
++ [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
++ [audio-slicer](https://github.com/openvpi/audio-slicer)
++ [Vocal pitch extraction:RMVPE](https://github.com/Dream-High/RMVPE)
+ + The pretrained model is trained and tested by [yxlllc](https://github.com/yxlllc/RMVPE) and [RVC-Boss](https://github.com/RVC-Boss).
+
+## 感谢所有贡献者作出的努力
+
+
+
diff --git a/configs/config.json b/configs/config.json
new file mode 100644
index 0000000..eeb8456
--- /dev/null
+++ b/configs/config.json
@@ -0,0 +1 @@
+{"pth_path": "assets/weights/kikiV1.pth", "index_path": "logs/kikiV1.index", "sg_hostapi": "MME", "sg_wasapi_exclusive": false, "sg_input_device": "VoiceMeeter Output (VB-Audio Vo", "sg_output_device": "VoiceMeeter Aux Input (VB-Audio", "sr_type": "sr_device", "threhold": -60.0, "pitch": 12.0, "rms_mix_rate": 0.5, "index_rate": 0.0, "block_time": 0.13, "crossfade_length": 0.08, "extra_time": 2.0, "f0method": "rmvpe"}
\ No newline at end of file
diff --git a/configs/config.py b/configs/config.py
new file mode 100644
index 0000000..a60868f
--- /dev/null
+++ b/configs/config.py
@@ -0,0 +1,277 @@
+import argparse
+import os
+import re
+import sys
+import json
+from multiprocessing import cpu_count
+from pathlib import Path
+from tools.file_io import read_text
+
+import torch
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+# Keep device/precision eligibility in one place. This follows the GPU rules
+# used by GPT-SoVITS: GPUs below 4 GiB or SM 5.3 are not selected, Pascal
+# SM 6.1 and GTX 16-series cards use fp32, and newer CUDA GPUs use fp16.
+def get_device_dtype_sm(idx) :
+ cpu = torch.device("cpu")
+ if not torch.cuda.is_available() or idx < 0 or idx >= torch.cuda.device_count():
+ return cpu, torch.float32, 0.0, 0.0
+
+ try:
+ cuda = torch.device(f"cuda:{idx}")
+ major, minor = torch.cuda.get_device_capability(idx)
+ gpu_name = torch.cuda.get_device_name(idx)
+ mem_bytes = torch.cuda.get_device_properties(idx).total_memory
+ except Exception:
+ logger.exception("Unable to inspect CUDA device %s", idx)
+ return cpu, torch.float32, 0.0, 0.0
+
+ mem_gb = mem_bytes / (1024**3) + 0.4
+ sm_version = major + minor / 10.0
+ is_16_series = bool(re.search(r"16\d{2}", gpu_name)) and sm_version == 7.5
+ if mem_gb < 4 or sm_version < 5.3:
+ return cpu, torch.float32, 0.0, 0.0
+ if sm_version == 6.1 or is_16_series:
+ return cuda, torch.float32, sm_version, mem_gb
+ if sm_version > 6.1:
+ return cuda, torch.float16, sm_version, mem_gb
+ return cpu, torch.float32, 0.0, 0.0
+
+
+def get_training_dtype() :
+ """Select one shared training dtype from the visible CUDA devices."""
+ if not torch.cuda.is_available():
+ return torch.float32
+
+ profiles = [get_device_dtype_sm(i) for i in range(torch.cuda.device_count())]
+ unsupported = [
+ i for i, profile in enumerate(profiles) if profile[0].type != "cuda"
+ ]
+ if unsupported:
+ raise RuntimeError(
+ "Selected CUDA device(s) do not satisfy the GPU rule "
+ f"(minimum 4 GiB and SM 5.3): {unsupported}"
+ )
+
+ # DDP uses one shared precision. A mixed Pascal/newer-GPU setup therefore
+ # uses fp32 unless every visible device is eligible for fp16.
+ if profiles and all(profile[1] == torch.float16 for profile in profiles):
+ return torch.float16
+ return torch.float32
+
+
+CUDA_AVAILABLE = torch.cuda.is_available()
+GPU_COUNT = torch.cuda.device_count() if CUDA_AVAILABLE else 0
+GPU_PROFILES = [get_device_dtype_sm(i) for i in range(GPU_COUNT)]
+GPU_INFOS = [
+ f"{device.index}\t{torch.cuda.get_device_name(device.index)}"
+ for device, _, _, _ in GPU_PROFILES
+ if device.type == "cuda"
+]
+GPU_INDEX = {
+ device.index for device, _, _, _ in GPU_PROFILES if device.type == "cuda"
+}
+GPU_MEMORY = {
+ device.index: mem
+ for device, _, _, mem in GPU_PROFILES
+ if device.type == "cuda"
+}
+CPU_INFO = "0\tCPU (CPU training is slower)"
+IS_GPU = bool(GPU_INFOS)
+
+
+def _detect_directml():
+ try:
+ import torch_directml
+
+ device = torch_directml.device(torch_directml.default_device())
+ # Device construction alone can succeed without a usable adapter.
+ probe = torch.ones(1, dtype=torch.float32).to(device)
+ _ = (probe + 1).cpu()
+ return True, device
+ except Exception:
+ return False, None
+
+
+DML_AVAILABLE, DML_DEVICE = _detect_directml()
+
+if GPU_PROFILES:
+ infer_device, infer_dtype, _, infer_gpu_mem = max(
+ GPU_PROFILES, key=lambda profile: (profile[2], profile[3])
+ )
+else:
+ infer_device, infer_dtype, infer_gpu_mem = (
+ torch.device("cpu"),
+ torch.float32,
+ 0.0,
+ )
+
+# Do not expose an unsupported CUDA device as the inference default.
+if infer_device.type != "cuda":
+ if DML_AVAILABLE:
+ infer_device, infer_dtype, infer_gpu_mem = (
+ DML_DEVICE,
+ torch.float32,
+ 0.0,
+ )
+ else:
+ infer_device, infer_dtype, infer_gpu_mem = (
+ torch.device("cpu"),
+ torch.float32,
+ 0.0,
+ )
+
+
+CONFIGS_DIR = Path(__file__).resolve().parent
+MODEL_CONFIG_FILES = (
+ "v1/32k.json",
+ "v1/40k.json",
+ "v1/48k.json",
+ "v2/48k.json",
+ "v2/32k.json",
+)
+
+
+def singleton_variable(func):
+ def wrapper(*args, **kwargs):
+ if not wrapper.instance:
+ wrapper.instance = func(*args, **kwargs)
+ return wrapper.instance
+
+ wrapper.instance = None
+ return wrapper
+
+
+@singleton_variable
+class Config:
+ def __init__(self):
+ self.device = str(infer_device)
+ self.dtype = infer_dtype
+ self.is_half = infer_dtype == torch.float16
+ self.n_cpu = 0
+ self.gpu_name = None
+ self.json_config = self.load_config_json()
+ self.gpu_mem = None
+ (
+ self.python_cmd,
+ self.listen_port,
+ self.iscolab,
+ self.noparallel,
+ self.noautoopen,
+ self.dml,
+ ) = self.arg_parse()
+ # DML is an automatic fallback when no CUDA device satisfies the rule.
+ self.dml = self.dml or (infer_device.type == "privateuseone")
+ self.instead = ""
+ self.preprocess_per = 3.7
+ self.x_pad, self.x_query, self.x_center, self.x_max = self.device_config()
+
+ @staticmethod
+ def load_config_json() :
+ d = {}
+ for config_file in MODEL_CONFIG_FILES:
+ d[config_file] = json.loads(read_text(CONFIGS_DIR / config_file))
+ return d
+
+ @staticmethod
+ def arg_parse() :
+ exe = sys.executable or "python"
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--port", type=int, default=7865, help="Listen port")
+ parser.add_argument("--pycmd", type=str, default=exe, help="Python command")
+ parser.add_argument("--colab", action="store_true", help="Launch in colab")
+ parser.add_argument(
+ "--noparallel", action="store_true", help="Disable parallel processing"
+ )
+ parser.add_argument(
+ "--noautoopen",
+ action="store_true",
+ help="Do not open in browser automatically",
+ )
+ parser.add_argument(
+ "--dml",
+ action="store_true",
+ help="torch_dml",
+ )
+ cmd_opts = parser.parse_args()
+
+ cmd_opts.port = cmd_opts.port if 0 <= cmd_opts.port <= 65535 else 7865
+
+ return (
+ cmd_opts.pycmd,
+ cmd_opts.port,
+ cmd_opts.colab,
+ cmd_opts.noparallel,
+ cmd_opts.noautoopen,
+ cmd_opts.dml,
+ )
+
+ def device_config(self) :
+ if infer_device.type == "cuda":
+ i_device = infer_device.index
+ self.device = str(infer_device)
+ self.dtype = infer_dtype
+ self.is_half = infer_dtype == torch.float16
+ self.gpu_name = torch.cuda.get_device_name(i_device)
+ self.gpu_mem = int(infer_gpu_mem)
+ logger.info(
+ "Selected GPU %s (%s, SM %.1f, %.1f GiB)",
+ i_device,
+ self.gpu_name,
+ torch.cuda.get_device_capability(i_device)[0]
+ + torch.cuda.get_device_capability(i_device)[1] / 10.0,
+ infer_gpu_mem,
+ )
+ if not self.is_half:
+ logger.info("GPU rule selected fp32 for %s", self.gpu_name)
+ self.preprocess_per = 3.0
+ if self.gpu_mem <= 4:
+ self.preprocess_per = 3.0
+ else:
+ logger.info("No supported Nvidia GPU found")
+ self.device = self.instead = "cpu"
+ self.dtype = torch.float32
+ self.is_half = False
+ self.preprocess_per = 3.0
+
+ if self.n_cpu == 0:
+ self.n_cpu = cpu_count()
+
+ if self.is_half:
+ # 6G显存配置
+ x_pad = 3
+ x_query = 10
+ x_center = 60
+ x_max = 65
+ else:
+ # 5G显存配置
+ x_pad = 1
+ x_query = 6
+ x_center = 38
+ x_max = 41
+
+ if self.gpu_mem is not None and self.gpu_mem <= 4:
+ x_pad = 1
+ x_query = 5
+ x_center = 30
+ x_max = 32
+ if self.dml:
+ logger.info("Use DirectML instead")
+ import torch_directml
+
+ self.device = torch_directml.device(torch_directml.default_device())
+ self.dtype = torch.float32
+ self.is_half = False
+ self.preprocess_per = 3.0
+ else:
+ if self.instead:
+ logger.info(f"Use {self.instead} instead")
+ logger.info(
+ "Half-precision floating-point: %s, device: %s"
+ % (self.is_half, self.device)
+ )
+ return x_pad, x_query, x_center, x_max
diff --git a/configs/v1/32k.json b/configs/v1/32k.json
new file mode 100644
index 0000000..5cd5319
--- /dev/null
+++ b/configs/v1/32k.json
@@ -0,0 +1,45 @@
+{
+ "train": {
+ "log_interval": 200,
+ "seed": 1234,
+ "epochs": 20000,
+ "learning_rate": 1e-4,
+ "betas": [0.8, 0.99],
+ "eps": 1e-9,
+ "batch_size": 4,
+ "lr_decay": 0.999875,
+ "segment_size": 12800,
+ "init_lr_ratio": 1,
+ "warmup_epochs": 0,
+ "c_mel": 45,
+ "c_kl": 1.0
+ },
+ "data": {
+ "max_wav_value": 32768.0,
+ "sampling_rate": 32000,
+ "filter_length": 1024,
+ "hop_length": 320,
+ "win_length": 1024,
+ "n_mel_channels": 80,
+ "mel_fmin": 0.0,
+ "mel_fmax": null
+ },
+ "model": {
+ "inter_channels": 192,
+ "hidden_channels": 192,
+ "filter_channels": 768,
+ "n_heads": 2,
+ "n_layers": 6,
+ "kernel_size": 3,
+ "p_dropout": 0,
+ "resblock": "1",
+ "resblock_kernel_sizes": [3,7,11],
+ "resblock_dilation_sizes": [[1,3,5], [1,3,5], [1,3,5]],
+ "upsample_rates": [10,4,2,2,2],
+ "upsample_initial_channel": 512,
+ "upsample_kernel_sizes": [16,16,4,4,4],
+ "use_spectral_norm": false,
+ "gin_channels": 256,
+ "spk_embed_dim": 109
+ }
+}
diff --git a/configs/v1/40k.json b/configs/v1/40k.json
new file mode 100644
index 0000000..ead661b
--- /dev/null
+++ b/configs/v1/40k.json
@@ -0,0 +1,45 @@
+{
+ "train": {
+ "log_interval": 200,
+ "seed": 1234,
+ "epochs": 20000,
+ "learning_rate": 1e-4,
+ "betas": [0.8, 0.99],
+ "eps": 1e-9,
+ "batch_size": 4,
+ "lr_decay": 0.999875,
+ "segment_size": 12800,
+ "init_lr_ratio": 1,
+ "warmup_epochs": 0,
+ "c_mel": 45,
+ "c_kl": 1.0
+ },
+ "data": {
+ "max_wav_value": 32768.0,
+ "sampling_rate": 40000,
+ "filter_length": 2048,
+ "hop_length": 400,
+ "win_length": 2048,
+ "n_mel_channels": 125,
+ "mel_fmin": 0.0,
+ "mel_fmax": null
+ },
+ "model": {
+ "inter_channels": 192,
+ "hidden_channels": 192,
+ "filter_channels": 768,
+ "n_heads": 2,
+ "n_layers": 6,
+ "kernel_size": 3,
+ "p_dropout": 0,
+ "resblock": "1",
+ "resblock_kernel_sizes": [3,7,11],
+ "resblock_dilation_sizes": [[1,3,5], [1,3,5], [1,3,5]],
+ "upsample_rates": [10,10,2,2],
+ "upsample_initial_channel": 512,
+ "upsample_kernel_sizes": [16,16,4,4],
+ "use_spectral_norm": false,
+ "gin_channels": 256,
+ "spk_embed_dim": 109
+ }
+}
diff --git a/configs/v1/48k.json b/configs/v1/48k.json
new file mode 100644
index 0000000..4722927
--- /dev/null
+++ b/configs/v1/48k.json
@@ -0,0 +1,45 @@
+{
+ "train": {
+ "log_interval": 200,
+ "seed": 1234,
+ "epochs": 20000,
+ "learning_rate": 1e-4,
+ "betas": [0.8, 0.99],
+ "eps": 1e-9,
+ "batch_size": 4,
+ "lr_decay": 0.999875,
+ "segment_size": 11520,
+ "init_lr_ratio": 1,
+ "warmup_epochs": 0,
+ "c_mel": 45,
+ "c_kl": 1.0
+ },
+ "data": {
+ "max_wav_value": 32768.0,
+ "sampling_rate": 48000,
+ "filter_length": 2048,
+ "hop_length": 480,
+ "win_length": 2048,
+ "n_mel_channels": 128,
+ "mel_fmin": 0.0,
+ "mel_fmax": null
+ },
+ "model": {
+ "inter_channels": 192,
+ "hidden_channels": 192,
+ "filter_channels": 768,
+ "n_heads": 2,
+ "n_layers": 6,
+ "kernel_size": 3,
+ "p_dropout": 0,
+ "resblock": "1",
+ "resblock_kernel_sizes": [3,7,11],
+ "resblock_dilation_sizes": [[1,3,5], [1,3,5], [1,3,5]],
+ "upsample_rates": [10,6,2,2,2],
+ "upsample_initial_channel": 512,
+ "upsample_kernel_sizes": [16,16,4,4,4],
+ "use_spectral_norm": false,
+ "gin_channels": 256,
+ "spk_embed_dim": 109
+ }
+}
diff --git a/configs/v2/32k.json b/configs/v2/32k.json
new file mode 100644
index 0000000..09788b0
--- /dev/null
+++ b/configs/v2/32k.json
@@ -0,0 +1,45 @@
+{
+ "train": {
+ "log_interval": 200,
+ "seed": 1234,
+ "epochs": 20000,
+ "learning_rate": 1e-4,
+ "betas": [0.8, 0.99],
+ "eps": 1e-9,
+ "batch_size": 4,
+ "lr_decay": 0.999875,
+ "segment_size": 12800,
+ "init_lr_ratio": 1,
+ "warmup_epochs": 0,
+ "c_mel": 45,
+ "c_kl": 1.0
+ },
+ "data": {
+ "max_wav_value": 32768.0,
+ "sampling_rate": 32000,
+ "filter_length": 1024,
+ "hop_length": 320,
+ "win_length": 1024,
+ "n_mel_channels": 80,
+ "mel_fmin": 0.0,
+ "mel_fmax": null
+ },
+ "model": {
+ "inter_channels": 192,
+ "hidden_channels": 192,
+ "filter_channels": 768,
+ "n_heads": 2,
+ "n_layers": 6,
+ "kernel_size": 3,
+ "p_dropout": 0,
+ "resblock": "1",
+ "resblock_kernel_sizes": [3,7,11],
+ "resblock_dilation_sizes": [[1,3,5], [1,3,5], [1,3,5]],
+ "upsample_rates": [10,8,2,2],
+ "upsample_initial_channel": 512,
+ "upsample_kernel_sizes": [20,16,4,4],
+ "use_spectral_norm": false,
+ "gin_channels": 256,
+ "spk_embed_dim": 109
+ }
+}
diff --git a/configs/v2/48k.json b/configs/v2/48k.json
new file mode 100644
index 0000000..fbe734e
--- /dev/null
+++ b/configs/v2/48k.json
@@ -0,0 +1,45 @@
+{
+ "train": {
+ "log_interval": 200,
+ "seed": 1234,
+ "epochs": 20000,
+ "learning_rate": 1e-4,
+ "betas": [0.8, 0.99],
+ "eps": 1e-9,
+ "batch_size": 4,
+ "lr_decay": 0.999875,
+ "segment_size": 17280,
+ "init_lr_ratio": 1,
+ "warmup_epochs": 0,
+ "c_mel": 45,
+ "c_kl": 1.0
+ },
+ "data": {
+ "max_wav_value": 32768.0,
+ "sampling_rate": 48000,
+ "filter_length": 2048,
+ "hop_length": 480,
+ "win_length": 2048,
+ "n_mel_channels": 128,
+ "mel_fmin": 0.0,
+ "mel_fmax": null
+ },
+ "model": {
+ "inter_channels": 192,
+ "hidden_channels": 192,
+ "filter_channels": 768,
+ "n_heads": 2,
+ "n_layers": 6,
+ "kernel_size": 3,
+ "p_dropout": 0,
+ "resblock": "1",
+ "resblock_kernel_sizes": [3,7,11],
+ "resblock_dilation_sizes": [[1,3,5], [1,3,5], [1,3,5]],
+ "upsample_rates": [12,10,2,2],
+ "upsample_initial_channel": 512,
+ "upsample_kernel_sizes": [24,20,4,4],
+ "use_spectral_norm": false,
+ "gin_channels": 256,
+ "spk_embed_dim": 109
+ }
+}
diff --git a/docs/cn/Changelog_CN.md b/docs/cn/Changelog_CN.md
new file mode 100644
index 0000000..c3bcf92
--- /dev/null
+++ b/docs/cn/Changelog_CN.md
@@ -0,0 +1,108 @@
+### 20231006更新
+
+我们制作了一个用于实时变声的界面go-realtime_gui.bat/realtime_gui.py(事实上早就存在了),本次更新重点也优化了实时变声的性能。对比0813版:
+ - 1、优优化界面操作:参数热更新(调整参数不需要中止再启动),懒加载模型(已加载过的模型不需要重新加载),增加响度因子参数(响度向输入音频靠近)
+ - 2、优化自带降噪效果与速度
+ - 3、大幅优化推理速度
+
+注意输入输出设备应该选择同种类型,例如都选MME类型。
+
+1006版本整体的更新为:
+ - 1、继续提升rmvpe音高提取算法效果,对于男低音有更大的提升
+ - 2、优化推理界面布局
+
+### 20230813更新
+1-常规bug修复
+ - 保存频率总轮数最低改为1 总轮数最低改为2
+ - 修复无pretrain模型训练报错
+ - 增加伴奏人声分离完毕清理显存
+ - faiss保存路径绝对路径改为相对路径
+ - 支持路径包含空格(训练集路径+实验名称均支持,不再会报错)
+ - filelist取消强制utf8编码
+ - 解决实时变声中开启索引导致的CPU极大占用问题
+
+2-重点更新
+ - 训练出当前最强开源人声音高提取模型RMVPE,并用于RVC的训练、离线/实时推理,支持pytorch/onnx/DirectML
+ - 通过pytorch-dml支持A卡和I卡的
+(1)实时变声(2)推理(3)人声伴奏分离(4)训练暂未支持,会切换至CPU训练;通过onnx_dml支持rmvpe_gpu的推理
+
+### 20230618更新
+- v2增加32k和48k两个新预训练模型
+- 修复非f0模型推理报错
+- 对于超过一小时的训练集的索引建立环节,自动kmeans缩小特征处理以加速索引训练、加入和查询
+- 附送一个人声转吉他玩具仓库
+- 数据处理剔除异常值切片
+- onnx导出选项卡
+
+失败的实验:
+- ~~特征检索增加时序维度:寄,没啥效果~~
+- ~~特征检索增加PCAR降维可选项:寄,数据大用kmeans缩小数据量,数据小降维操作耗时比省下的匹配耗时还多~~
+- ~~支持onnx推理(附带仅推理的小压缩包):寄,生成nsf还是需要pytorch~~
+- ~~训练时在音高、gender、eq、噪声等方面对输入进行随机增强:寄,没啥效果~~
+- ~~接入小型声码器调研:寄,效果变差~~
+
+todolist:
+- ~~训练集音高识别支持crepe:已经被RMVPE取代,不需要~~
+- ~~多进程harvest推理:已经被RMVPE取代,不需要~~
+- ~~crepe的精度支持和RVC-config同步:已经被RMVPE取代,不需要。支持这个还要同步torchcrepe的库,麻烦~~
+- 对接F0编辑器
+
+
+### 20230528更新
+- 增加v2的jupyter notebook,韩文changelog,增加一些环境依赖
+- 增加呼吸、清辅音、齿音保护模式
+- 支持crepe-full推理
+- UVR5人声伴奏分离加上3个去延迟模型和MDX-Net去混响模型,增加HP3人声提取模型
+- 索引名称增加版本和实验名称
+- 人声伴奏分离、推理批量导出增加音频导出格式选项
+- 废弃32k模型的训练
+
+### 20230513更新
+- 清除一键包内部老版本runtime内残留的lib.infer_pack和uvr5_pack
+- 修复训练集预处理伪多进程的bug
+- 增加harvest识别音高可选通过中值滤波削弱哑音现象,可调整中值滤波半径
+- 导出音频增加后处理重采样
+- 训练n_cpu进程数从"仅调整f0提取"改为"调整数据预处理和f0提取"
+- 自动检测logs文件夹下的index路径,提供下拉列表功能
+- tab页增加"常见问题解答"(也可参考github-rvc-wiki)
+- 相同路径的输入音频推理增加了音高缓存(用途:使用harvest音高提取,整个pipeline会经历漫长且重复的音高提取过程,如果不使用缓存,实验不同音色、索引、音高中值滤波半径参数的用户在第一次测试后的等待结果会非常痛苦)
+
+### 20230514更新
+- 音量包络对齐输入混合(可以缓解“输入静音输出小幅度噪声”的问题。如果输入音频背景底噪大则不建议开启,默认不开启(值为1可视为不开启))
+- 支持按照指定频率保存提取的小模型(假如你想尝试不同epoch下的推理效果,但是不想保存所有大checkpoint并且每次都要ckpt手工处理提取小模型,这项功能会非常实用)
+- 通过设置环境变量解决服务端开了系统全局代理导致浏览器连接错误的问题
+- 支持v2预训练模型(目前只公开了40k版本进行测试,另外2个采样率还没有训练完全)
+- 推理前限制超过1的过大音量
+- 微调数据预处理参数
+
+
+### 20230409更新
+- 修正训练参数,提升显卡平均利用率,A100最高从25%提升至90%左右,V100:50%->90%左右,2060S:60%->85%左右,P40:25%->95%左右,训练速度显著提升
+- 修正参数:总batch_size改为每张卡的batch_size
+- 修正total_epoch:最大限制100解锁至1000;默认10提升至默认20
+- 修复ckpt提取识别是否带音高错误导致推理异常的问题
+- 修复分布式训练每个rank都保存一次ckpt的问题
+- 特征提取进行nan特征过滤
+- 修复静音输入输出随机辅音or噪声的问题(老版模型需要重做训练集重训)
+
+### 20230416更新
+- 新增本地实时变声迷你GUI,双击go-realtime_gui.bat启动
+- 训练推理均对<50Hz的频段进行滤波过滤
+- 训练推理音高提取pyworld最低音高从默认80下降至50,50-80hz间的男声低音不会哑
+- WebUI支持根据系统区域变更语言(现支持en_US,ja_JP,zh_CN,zh_HK,zh_SG,zh_TW,不支持的默认en_US)
+- 修正部分显卡识别(例如V100-16G识别失败,P4识别失败)
+
+### 20230428更新
+- 升级faiss索引设置,速度更快,质量更高
+- 取消total_npy依赖,后续分享模型不再需要填写total_npy
+- 解锁16系限制。4G显存GPU给到4G的推理设置。
+- 修复部分音频格式下UVR5人声伴奏分离的bug
+- 实时变声迷你gui增加对非40k与不懈怠音高模型的支持
+
+### 后续计划:
+功能:
+- 支持多人训练选项卡(至多4人)
+
+底模:
+- 收集呼吸wav加入训练集修正呼吸变声电音的问题
+- 我们正在训练增加了歌声训练集的底模,未来会公开
diff --git a/docs/cn/faq.md b/docs/cn/faq.md
new file mode 100644
index 0000000..f91b48f
--- /dev/null
+++ b/docs/cn/faq.md
@@ -0,0 +1,108 @@
+## Q1:ffmpeg error/utf8 error.
+
+大概率不是ffmpeg问题,而是音频路径问题;
+ffmpeg读取路径带空格、()等特殊符号,可能出现ffmpeg error;训练集音频带中文路径,在写入filelist.txt的时候可能出现utf8 error;
+
+## Q2:一键训练结束没有索引
+
+显示"Training is done. The program is closed."则模型训练成功,后续紧邻的报错是假的;
+
+一键训练结束完成没有added开头的索引文件,可能是因为训练集太大卡住了添加索引的步骤;已通过批处理add索引解决内存add索引对内存需求过大的问题。临时可尝试再次点击"训练索引"按钮。
+
+## Q3:训练结束推理没看到训练集的音色
+点刷新音色再看看,如果还没有看看训练有没有报错,控制台和webui的截图,logs/实验名下的log,都可以发给开发者看看。
+
+## Q4:如何分享模型
+ rvc_root/logs/实验名 下面存储的pth不是用来分享模型用来推理的,而是为了存储实验状态供复现,以及继续训练用的。用来分享的模型应该是weights文件夹下大小为60+MB的pth文件;
+ 后续将把weights/exp_name.pth和logs/exp_name/added_xxx.index合并打包成weights/exp_name.zip省去填写index的步骤,那么zip文件用来分享,不要分享pth文件,除非是想换机器继续训练;
+ 如果你把logs文件夹下的几百MB的pth文件复制/分享到weights文件夹下强行用于推理,可能会出现f0,tgt_sr等各种key不存在的报错。你需要用ckpt选项卡最下面,手工或自动(本地logs下如果能找到相关信息则会自动)选择是否携带音高、目标音频采样率的选项后进行ckpt小模型提取(输入路径填G开头的那个),提取完在weights文件夹下会出现60+MB的pth文件,刷新音色后可以选择使用。
+
+## Q5:Connection Error.
+也许你关闭了控制台(黑色窗口)。
+
+## Q6:WebUI弹出Expecting value: line 1 column 1 (char 0).
+请关闭系统局域网代理/全局代理。
+
+这个不仅是客户端的代理,也包括服务端的代理(例如你使用autodl设置了http_proxy和https_proxy学术加速,使用时也需要unset关掉)
+
+## Q7:不用WebUI如何通过命令训练推理
+训练脚本:
+可先跑通WebUI,消息窗内会显示数据集处理和训练用命令行;
+
+推理脚本:
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/myinfer.py
+
+例子:
+
+runtime\python.exe myinfer.py 0 "E:\codes\py39\RVC-beta\todo-songs\1111.wav" "E:\codes\py39\logs\mi-test\added_IVF677_Flat_nprobe_7.index" harvest "test.wav" "weights/mi-test.pth" 0.6 cuda:0 True
+
+f0up_key=sys.argv[1]
+input_path=sys.argv[2]
+index_path=sys.argv[3]
+f0method=sys.argv[4]#harvest or pm
+opt_path=sys.argv[5]
+model_path=sys.argv[6]
+index_rate=float(sys.argv[7])
+device=sys.argv[8]
+is_half=bool(sys.argv[9])
+
+## Q8:Cuda error/Cuda out of memory.
+小概率是cuda配置问题、设备不支持;大概率是显存不够(out of memory);
+
+训练的话缩小batch size(如果缩小到1还不够只能更换显卡训练),推理的话酌情缩小config.py结尾的x_pad,x_query,x_center,x_max。4G以下显存(例如1060(3G)和各种2G显卡)可以直接放弃,4G显存显卡还有救。
+
+## Q9:total_epoch调多少比较好
+
+如果训练集音质差底噪大,20~30足够了,调太高,底模音质无法带高你的低音质训练集
+如果训练集音质高底噪低时长多,可以调高,200是ok的(训练速度很快,既然你有条件准备高音质训练集,显卡想必条件也不错,肯定不在乎多一些训练时间)
+
+## Q10:需要多少训练集时长
+ 推荐10min至50min
+ 保证音质高底噪低的情况下,如果有个人特色的音色统一,则多多益善
+ 高水平的训练集(精简+音色有特色),5min至10min也是ok的,仓库作者本人就经常这么玩
+ 也有人拿1min至2min的数据来训练并且训练成功的,但是成功经验是其他人不可复现的,不太具备参考价值。这要求训练集音色特色非常明显(比如说高频气声较明显的萝莉少女音),且音质高;
+ 1min以下时长数据目前没见有人尝试(成功)过。不建议进行这种鬼畜行为。
+
+## Q11:index rate干嘛用的,怎么调(科普)
+ 如果底模和推理源的音质高于训练集的音质,他们可以带高推理结果的音质,但代价可能是音色往底模/推理源的音色靠,这种现象叫做"音色泄露";
+ index rate用来削减/解决音色泄露问题。调到1,则理论上不存在推理源的音色泄露问题,但音质更倾向于训练集。如果训练集音质比推理源低,则index rate调高可能降低音质。调到0,则不具备利用检索混合来保护训练集音色的效果;
+ 如果训练集优质时长多,可调高total_epoch,此时模型本身不太会引用推理源和底模的音色,很少存在"音色泄露"问题,此时index_rate不重要,你甚至可以不建立/分享index索引文件。
+
+## Q11:推理怎么选gpu
+config.py文件里device cuda:后面选择卡号;
+卡号和显卡的映射关系,在训练选项卡的显卡信息栏里能看到。
+
+## Q12:如何推理训练中间保存的pth
+通过ckpt选项卡最下面提取小模型。
+
+
+## Q13:如何中断和继续训练
+现阶段只能关闭WebUI控制台双击go-webui.bat重启程序。网页参数也要刷新重新填写;
+继续训练:相同网页参数点训练模型,就会接着上次的checkpoint继续训练。
+
+## Q14:训练时出现文件页面/内存error
+进程开太多了,内存炸了。你可能可以通过如下方式解决
+1、"提取音高和处理数据使用的CPU进程数" 酌情拉低;
+2、训练集音频手工切一下,不要太长。
+
+
+## Q15:如何中途加数据训练
+1、所有数据新建一个实验名;
+2、拷贝上一次的最新的那个G和D文件(或者你想基于哪个中间ckpt训练,也可以拷贝中间的)到新实验名;下
+3、一键训练新实验名,他会继续上一次的最新进度训练。
+
+## Q16: error about llvmlite.dll
+
+OSError: Could not load shared object file: llvmlite.dll
+
+FileNotFoundError: Could not find module lib\site-packages\llvmlite\binding\llvmlite.dll (or one of its dependencies). Try using the full path with constructor syntax.
+
+win平台会报这个错,装上https://aka.ms/vs/17/release/vc_redist.x64.exe这个再重启WebUI就好了。
+
+## Q17: RuntimeError: The expanded size of the tensor (17280) must match the existing size (0) at non-singleton dimension 1. Target sizes: [1, 17280]. Tensor sizes: [0]
+
+wavs16k文件夹下,找到文件大小显著比其他都小的一些音频文件,删掉,点击训练模型,就不会报错了,不过由于一键流程中断了你训练完模型还要点训练索引。
+
+## Q18: RuntimeError: The size of tensor a (24) must match the size of tensor b (16) at non-singleton dimension 2
+
+不要中途变更采样率继续训练。如果一定要变更,应更换实验名从头训练。当然你也可以把上次提取的音高和特征(0/1/2/2b folders)拷贝过去加速训练流程。
diff --git a/docs/en/Changelog_EN.md b/docs/en/Changelog_EN.md
new file mode 100644
index 0000000..fa30319
--- /dev/null
+++ b/docs/en/Changelog_EN.md
@@ -0,0 +1,105 @@
+### 2023-10-06
+- We have created a GUI for real-time voice change: go-realtime_gui.bat/realtime_gui.py (Note that you should choose the same type of input and output device, e.g. MME and MME).
+- We trained a better pitch extract RMVPE model.
+- Optimize inference GUI layout.
+
+### 2023-08-13
+1-Regular bug fix
+- Change the minimum total epoch number to 1, and change the minimum total epoch number to 2
+- Fix training errors of not using pre-train models
+- After accompaniment vocals separation, clear graphics memory
+- Change faiss save path absolute path to relative path
+- Support path containing spaces (both training set path and experiment name are supported, and errors will no longer be reported)
+- Filelist cancels mandatory utf8 encoding
+- Solve the CPU consumption problem caused by faiss searching during real-time voice changes
+
+2-Key updates
+- Train the current strongest open-source vocal pitch extraction model RMVPE, and use it for RVC training, offline/real-time inference, supporting PyTorch/Onnx/DirectML
+- Support for AMD and Intel graphics cards through Pytorch_DML
+
+(1) Real time voice change (2) Inference (3) Separation of vocal accompaniment (4) Training not currently supported, will switch to CPU training; supports RMVPE inference of gpu by Onnx_Dml
+
+
+### 2023-06-18
+- New pretrained v2 models: 32k and 48k
+- Fix non-f0 model inference errors
+- For training-set exceeding 1 hour, do automatic minibatch-kmeans to reduce feature shape, so that index training, adding, and searching will be much faster.
+- Provide a toy vocal2guitar huggingface space
+- Auto delete outlier short cut training-set audios
+- Onnx export tab
+
+Failed experiments:
+- ~~Feature retrieval: add temporal feature retrieval: not effective~~
+- ~~Feature retrieval: add PCAR dimensionality reduction: searching is even slower~~
+- ~~Random data augmentation when training: not effective~~
+
+todolist:
+- ~~Vocos-RVC (tiny vocoder): not effective~~
+- ~~Crepe support for training:replaced by RMVPE~~
+- ~~Half precision crepe inference:replaced by RMVPE. And hard to achive.~~
+- F0 editor support
+
+### 2023-05-28
+- Add v2 jupyter notebook, korean changelog, fix some environment requirments
+- Add voiceless consonant and breath protection mode
+- Support crepe-full pitch detect
+- UVR5 vocal separation: support dereverb models and de-echo models
+- Add experiment name and version on the name of index
+- Support users to manually select export format of output audios when batch voice conversion processing and UVR5 vocal separation
+- v1 32k model training is no more supported
+
+### 2023-05-13
+- Clear the redundant codes in the old version of runtime in the one-click-package: lib.infer_pack and uvr5_pack
+- Fix pseudo multiprocessing bug in training set preprocessing
+- Adding median filtering radius adjustment for harvest pitch recognize algorithm
+- Support post processing resampling for exporting audio
+- Multi processing "n_cpu" setting for training is changed from "f0 extraction" to "data preprocessing and f0 extraction"
+- Automatically detect the index paths under the logs folder and provide a drop-down list function
+- Add "Frequently Asked Questions and Answers" on the tab page (you can also refer to github RVC wiki)
+- When inference, harvest pitch is cached when using same input audio path (purpose: using harvest pitch extraction, the entire pipeline will go through a long and repetitive pitch extraction process. If caching is not used, users who experiment with different timbre, index, and pitch median filtering radius settings will experience a very painful waiting process after the first inference)
+
+### 2023-05-14
+- Use volume envelope of input to mix or replace the volume envelope of output (can alleviate the problem of "input muting and output small amplitude noise". If the input audio background noise is high, it is not recommended to turn it on, and it is not turned on by default (1 can be considered as not turned on)
+- Support saving extracted small models at a specified frequency (if you want to see the performance under different epochs, but do not want to save all large checkpoints and manually extract small models by ckpt-processing every time, this feature will be very practical)
+- Resolve the issue of "connection errors" caused by the server's global proxy by setting environment variables
+- Supports pre-trained v2 models (currently only 40k versions are publicly available for testing, and the other two sampling rates have not been fully trained yet)
+- Limit excessive volume exceeding 1 before inference
+- Slightly adjusted the settings of training-set preprocessing
+
+
+#######################
+
+History changelogs:
+
+### 2023-04-09
+- Fixed training parameters to improve GPU utilization rate: A100 increased from 25% to around 90%, V100: 50% to around 90%, 2060S: 60% to around 85%, P40: 25% to around 95%; significantly improved training speed
+- Changed parameter: total batch_size is now per GPU batch_size
+- Changed total_epoch: maximum limit increased from 100 to 1000; default increased from 10 to 20
+- Fixed issue of ckpt extraction recognizing pitch incorrectly, causing abnormal inference
+- Fixed issue of distributed training saving ckpt for each rank
+- Applied nan feature filtering for feature extraction
+- Fixed issue with silent input/output producing random consonants or noise (old models need to retrain with a new dataset)
+
+### 2023-04-16 Update
+- Added local real-time voice changing mini-GUI, start by double-clicking go-realtime_gui.bat
+- Applied filtering for frequency bands below 50Hz during training and inference
+- Lowered the minimum pitch extraction of pyworld from the default 80 to 50 for training and inference, allowing male low-pitched voices between 50-80Hz not to be muted
+- WebUI supports changing languages according to system locale (currently supporting en_US, ja_JP, zh_CN, zh_HK, zh_SG, zh_TW; defaults to en_US if not supported)
+- Fixed recognition of some GPUs (e.g., V100-16G recognition failure, P4 recognition failure)
+
+### 2023-04-28 Update
+- Upgraded faiss index settings for faster speed and higher quality
+- Removed dependency on total_npy; future model sharing will not require total_npy input
+- Unlocked restrictions for the 16-series GPUs, providing 4GB inference settings for 4GB VRAM GPUs
+- Fixed bug in UVR5 vocal accompaniment separation for certain audio formats
+- Real-time voice changing mini-GUI now supports non-40k and non-lazy pitch models
+
+### Future Plans:
+Features:
+- Add option: extract small models for each epoch save
+- Add option: export additional mp3 to the specified path during inference
+- Support multi-person training tab (up to 4 people)
+
+Base model:
+- Collect breathing wav files to add to the training dataset to fix the issue of distorted breath sounds
+- We are currently training a base model with an extended singing dataset, which will be released in the future
diff --git a/docs/en/README.en.md b/docs/en/README.en.md
new file mode 100644
index 0000000..630ab1e
--- /dev/null
+++ b/docs/en/README.en.md
@@ -0,0 +1,201 @@
+
+
+
Retrieval-based-Voice-Conversion-WebUI
+An easy-to-use Voice Conversion framework based on VITS.
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+[**Changelog**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/docs/Changelog_EN.md) | [**FAQ (Frequently Asked Questions)**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/FAQ-(Frequently-Asked-Questions))
+
+[**English**](../en/README.en.md) | [**中文简体**](../../README.md) | [**日本語**](../jp/README.ja.md) | [**한국어**](../kr/README.ko.md) ([**韓國語**](../kr/README.ko.han.md)) | [**Français**](../fr/README.fr.md) | [**Türkçe**](../tr/README.tr.md) | [**Português**](../pt/README.pt.md)
+
+
+
+> Check out our [Demo Video](https://www.bilibili.com/video/BV1pm4y1z7Gm/) here!
+
+
+
+ | Training and inference Webui |
+ Real-time voice changing GUI |
+
+
+  |
+  |
+
+
+ | go-webui.bat |
+ go-realtime_gui.bat |
+
+
+ | You can freely choose the action you want to perform. |
+ We have achieved an end-to-end latency of 170ms. With the use of ASIO input and output devices, we have managed to achieve an end-to-end latency of 90ms, but it is highly dependent on hardware driver support. |
+
+
+
+> The dataset for the pre-training model uses nearly 50 hours of high quality audio from the VCTK open source dataset.
+
+> High quality licensed song datasets will be added to the training-set often for your use, without having to worry about copyright infringement.
+
+> Please look forward to the pretrained base model of RVCv3, which has larger parameters, more training data, better results, unchanged inference speed, and requires less training data for training.
+
+## Features:
++ Reduce tone leakage by replacing the source feature to training-set feature using top1 retrieval;
++ Easy + fast training, even on poor graphics cards;
++ Training with a small amounts of data (>=10min low noise speech recommended);
++ Model fusion to change timbres (using ckpt processing tab->ckpt merge);
++ Easy-to-use WebUI;
++ UVR5 model to quickly separate vocals and instruments;
++ High-pitch Voice Extraction Algorithm [InterSpeech2023-RMVPE](#Credits) to prevent a muted sound problem. Provides the best results (significantly) and is faster with lower resource consumption than Crepe_full;
++ AMD/Intel graphics cards acceleration supported;
+
+## Preparing the environment
+The following commands need to be executed with Python 3.8 or higher.
+
+(Windows/Linux)
+First install the main dependencies through pip:
+```bash
+# Install PyTorch-related core dependencies, skip if installed
+# Reference: https://pytorch.org/get-started/locally/
+pip install torch torchvision torchaudio
+
+#For Windows + Nvidia Ampere Architecture(RTX30xx), you need to specify the cuda version corresponding to pytorch according to the experience of https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/issues/21
+#pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+
+#For Linux + AMD Cards, you need to use the following pytorch versions:
+#pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2
+```
+
+Then can use poetry to install the other dependencies:
+```bash
+# Install the Poetry dependency management tool, skip if installed
+# Reference: https://python-poetry.org/docs/#installation
+curl -sSL https://install.python-poetry.org | python3 -
+
+# Install the project dependencies
+poetry install
+```
+
+You can also use pip to install them:
+```bash
+
+for Nvidia graphics cards
+ pip install -r requirements.txt
+
+for AMD/Intel graphics cards on Windows (DirectML):
+ pip install -r requirements-dml.txt
+
+for AMD graphics cards on Linux (ROCm):
+ pip install -r requirements-amd.txt
+```
+
+------
+Mac users can install dependencies via `run.sh`:
+```bash
+sh ./run.sh
+```
+
+## Preparation of other Pre-models
+RVC requires other pre-models to infer and train.
+
+Download them from our [Huggingface space](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/).
+
+Here's a list of Pre-models and other files that RVC needs:
+```bash
+./assets/hubert_base
+
+./assets/pretrained
+
+./assets/uvr5_weights
+
+Additional downloads are required if you want to test the v2 version of the model.
+
+./assets/pretrained_v2
+
+If you want to test the v2 version model (the v2 version model has changed the input from the 256 dimensional feature of 9-layer Hubert+final_proj to the 768 dimensional feature of 12-layer Hubert, and has added 3 period discriminators), you will need to download additional features
+
+./assets/pretrained_v2
+
+If you want to use the latest SOTA RMVPE vocal pitch extraction algorithm, you need to download the RMVPE weights and place them in the RVC root directory
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.pt
+
+ For AMD/Intel graphics cards users you need download:
+
+ https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.onnx
+
+```
+
+### 2. Install FFmpeg
+If you have FFmpeg and FFprobe installed on your computer, you can skip this step.
+
+#### For Ubuntu/Debian users
+```bash
+sudo apt install ffmpeg
+```
+#### For MacOS users
+```bash
+brew install ffmpeg
+```
+#### For Windwos users
+Download these files and place them in the root folder:
+- [ffmpeg.exe](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffmpeg.exe)
+
+- [ffprobe.exe](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffprobe.exe)
+
+## ROCm Support for AMD graphic cards (Linux only)
+To use ROCm on Linux install all required drivers as described [here](https://rocm.docs.amd.com/en/latest/deploy/linux/os-native/install.html).
+
+On Arch use pacman to install the driver:
+````
+pacman -S rocm-hip-sdk rocm-opencl-sdk
+````
+
+You might also need to set these environment variables (e.g. on a RX6700XT):
+````
+export ROCM_PATH=/opt/rocm
+export HSA_OVERRIDE_GFX_VERSION=10.3.0
+````
+Make sure your user is part of the `render` and `video` group:
+````
+sudo usermod -aG render $USERNAME
+sudo usermod -aG video $USERNAME
+````
+
+## Get started
+### start up directly
+Use the following command to start WebUI:
+```bash
+python webui.py
+```
+### Use the integration package
+Download and extract file `RVC-beta.7z`, then follow the steps below according to your system:
+#### For Windows users
+双击`go-webui.bat`
+#### For MacOS users
+```bash
+sh ./run.sh
+```
+## Credits
++ [ContentVec](https://github.com/auspicious3000/contentvec/)
++ [VITS](https://github.com/jaywalnut310/vits)
++ [HIFIGAN](https://github.com/jik876/hifi-gan)
++ [Gradio](https://github.com/gradio-app/gradio)
++ [FFmpeg](https://github.com/FFmpeg/FFmpeg)
++ [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
++ [audio-slicer](https://github.com/openvpi/audio-slicer)
++ [Vocal pitch extraction:RMVPE](https://github.com/Dream-High/RMVPE)
+ + The pretrained model is trained and tested by [yxlllc](https://github.com/yxlllc/RMVPE) and [RVC-Boss](https://github.com/RVC-Boss).
+
+## Thanks to all contributors for their efforts
+
+
+
diff --git a/docs/en/faiss_tips_en.md b/docs/en/faiss_tips_en.md
new file mode 100644
index 0000000..aafad6e
--- /dev/null
+++ b/docs/en/faiss_tips_en.md
@@ -0,0 +1,102 @@
+faiss tuning TIPS
+==================
+# about faiss
+faiss is a library of neighborhood searches for dense vectors, developed by facebook research, which efficiently implements many approximate neighborhood search methods.
+Approximate Neighbor Search finds similar vectors quickly while sacrificing some accuracy.
+
+## faiss in RVC
+In RVC, for the embedding of features converted by HuBERT, we search for embeddings similar to the embedding generated from the training data and mix them to achieve a conversion that is closer to the original speech. However, since this search takes time if performed naively, high-speed conversion is realized by using approximate neighborhood search.
+
+# implementation overview
+In '/logs/your-experiment/3_feature256' where the model is located, features extracted by HuBERT from each voice data are located.
+From here we read the npy files in order sorted by filename and concatenate the vectors to create big_npy. (This vector has shape [N, 256].)
+After saving big_npy as /logs/your-experiment/total_fea.npy, train it with faiss.
+
+In this article, I will explain the meaning of these parameters.
+
+# Explanation of the method
+## index factory
+An index factory is a unique faiss notation that expresses a pipeline that connects multiple approximate neighborhood search methods as a string.
+This allows you to try various approximate neighborhood search methods simply by changing the index factory string.
+In RVC it is used like this:
+
+```python
+index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)
+```
+Among the arguments of index_factory, the first is the number of dimensions of the vector, the second is the index factory string, and the third is the distance to use.
+
+For more detailed notation
+https://github.com/facebookresearch/faiss/wiki/The-index-factory
+
+## index for distance
+There are two typical indexes used as similarity of embedding as follows.
+
+- Euclidean distance (METRIC_L2)
+- inner product (METRIC_INNER_PRODUCT)
+
+Euclidean distance takes the squared difference in each dimension, sums the differences in all dimensions, and then takes the square root. This is the same as the distance in 2D and 3D that we use on a daily basis.
+The inner product is not used as an index of similarity as it is, and the cosine similarity that takes the inner product after being normalized by the L2 norm is generally used.
+
+Which is better depends on the case, but cosine similarity is often used in embedding obtained by word2vec and similar image retrieval models learned by ArcFace. If you want to do l2 normalization on vector X with numpy, you can do it with the following code with eps small enough to avoid 0 division.
+
+```python
+X_normed = X / np.maximum(eps, np.linalg.norm(X, ord=2, axis=-1, keepdims=True))
+```
+
+Also, for the index factory, you can change the distance index used for calculation by choosing the value to pass as the third argument.
+
+```python
+index = faiss.index_factory(dimention, text, faiss.METRIC_INNER_PRODUCT)
+```
+
+## IVF
+IVF (Inverted file indexes) is an algorithm similar to the inverted index in full-text search.
+During learning, the search target is clustered with kmeans, and Voronoi partitioning is performed using the cluster center. Each data point is assigned a cluster, so we create a dictionary that looks up the data points from the clusters.
+
+For example, if clusters are assigned as follows
+|index|Cluster|
+|-----|-------|
+|1|A|
+|2|B|
+|3|A|
+|4|C|
+|5|B|
+
+The resulting inverted index looks like this:
+
+|cluster|index|
+|-------|-----|
+|A|1, 3|
+|B|2, 5|
+|C|4|
+
+When searching, we first search n_probe clusters from the clusters, and then calculate the distances for the data points belonging to each cluster.
+
+# recommend parameter
+There are official guidelines on how to choose an index, so I will explain accordingly.
+https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index
+
+For datasets below 1M, 4bit-PQ is the most efficient method available in faiss as of April 2023.
+Combining this with IVF, narrowing down the candidates with 4bit-PQ, and finally recalculating the distance with an accurate index can be described by using the following index factory.
+
+```python
+index = faiss.index_factory(256, "IVF1024,PQ128x4fs,RFlat")
+```
+
+## Recommended parameters for IVF
+Consider the case of too many IVFs. For example, if coarse quantization by IVF is performed for the number of data, this is the same as a naive exhaustive search and is inefficient.
+For 1M or less, IVF values are recommended between 4*sqrt(N) ~ 16*sqrt(N) for N number of data points.
+
+Since the calculation time increases in proportion to the number of n_probes, please consult with the accuracy and choose appropriately. Personally, I don't think RVC needs that much accuracy, so n_probe = 1 is fine.
+
+## FastScan
+FastScan is a method that enables high-speed approximation of distances by Cartesian product quantization by performing them in registers.
+Cartesian product quantization performs clustering independently for each d dimension (usually d = 2) during learning, calculates the distance between clusters in advance, and creates a lookup table. At the time of prediction, the distance of each dimension can be calculated in O(1) by looking at the lookup table.
+So the number you specify after PQ usually specifies half the dimension of the vector.
+
+For a more detailed description of FastScan, please refer to the official documentation.
+https://github.com/facebookresearch/faiss/wiki/Fast-accumulation-of-PQ-and-AQ-codes-(FastScan)
+
+## RFlat
+RFlat is an instruction to recalculate the rough distance calculated by FastScan with the exact distance specified by the third argument of index factory.
+When getting k neighbors, k*k_factor points are recalculated.
diff --git a/docs/en/faq_en.md b/docs/en/faq_en.md
new file mode 100644
index 0000000..063c0e9
--- /dev/null
+++ b/docs/en/faq_en.md
@@ -0,0 +1,119 @@
+## Q1:ffmpeg error/utf8 error.
+It is most likely not a FFmpeg issue, but rather an audio path issue;
+
+FFmpeg may encounter an error when reading paths containing special characters like spaces and (), which may cause an FFmpeg error; and when the training set's audio contains Chinese paths, writing it into filelist.txt may cause a utf8 error.
+
+## Q2:Cannot find index file after "One-click Training".
+If it displays "Training is done. The program is closed," then the model has been trained successfully, and the subsequent errors are fake;
+
+The lack of an 'added' index file after One-click training may be due to the training set being too large, causing the addition of the index to get stuck; this has been resolved by using batch processing to add the index, which solves the problem of memory overload when adding the index. As a temporary solution, try clicking the "Train Index" button again.
+
+## Q3:Cannot find the model in “Inferencing timbre” after training
+Click “Refresh timbre list” and check again; if still not visible, check if there are any errors during training and send screenshots of the console, web UI, and logs/experiment_name/*.log to the developers for further analysis.
+
+## Q4:How to share a model/How to use others' models?
+The pth files stored in rvc_root/logs/experiment_name are not meant for sharing or inference, but for storing the experiment checkpoits for reproducibility and further training. The model to be shared should be the 60+MB pth file in the weights folder;
+
+In the future, weights/exp_name.pth and logs/exp_name/added_xxx.index will be merged into a single weights/exp_name.zip file to eliminate the need for manual index input; so share the zip file, not the pth file, unless you want to continue training on a different machine;
+
+Copying/sharing the several hundred MB pth files from the logs folder to the weights folder for forced inference may result in errors such as missing f0, tgt_sr, or other keys. You need to use the ckpt tab at the bottom to manually or automatically (if the information is found in the logs/exp_name), select whether to include pitch infomation and target audio sampling rate options and then extract the smaller model. After extraction, there will be a 60+ MB pth file in the weights folder, and you can refresh the voices to use it.
+
+## Q5:Connection Error.
+You may have closed the console (black command line window).
+
+## Q6:WebUI popup 'Expecting value: line 1 column 1 (char 0)'.
+Please disable system LAN proxy/global proxy and then refresh.
+
+## Q7:How to train and infer without the WebUI?
+Training script:
+You can run training in WebUI first, and the command-line versions of dataset preprocessing and training will be displayed in the message window.
+
+Inference script:
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/myinfer.py
+
+
+e.g.
+
+runtime\python.exe myinfer.py 0 "E:\codes\py39\RVC-beta\todo-songs\1111.wav" "E:\codes\py39\logs\mi-test\added_IVF677_Flat_nprobe_7.index" harvest "test.wav" "weights/mi-test.pth" 0.6 cuda:0 True
+
+
+f0up_key=sys.argv[1]
+input_path=sys.argv[2]
+index_path=sys.argv[3]
+f0method=sys.argv[4]#harvest or pm
+opt_path=sys.argv[5]
+model_path=sys.argv[6]
+index_rate=float(sys.argv[7])
+device=sys.argv[8]
+is_half=bool(sys.argv[9])
+
+## Q8:Cuda error/Cuda out of memory.
+There is a small chance that there is a problem with the CUDA configuration or the device is not supported; more likely, there is not enough memory (out of memory).
+
+For training, reduce the batch size (if reducing to 1 is still not enough, you may need to change the graphics card); for inference, adjust the x_pad, x_query, x_center, and x_max settings in the config.py file as needed. 4G or lower memory cards (e.g. 1060(3G) and various 2G cards) can be abandoned, while 4G memory cards still have a chance.
+
+## Q9:How many total_epoch are optimal?
+If the training dataset's audio quality is poor and the noise floor is high, 20-30 epochs are sufficient. Setting it too high won't improve the audio quality of your low-quality training set.
+
+If the training set audio quality is high, the noise floor is low, and there is sufficient duration, you can increase it. 200 is acceptable (since training is fast, and if you're able to prepare a high-quality training set, your GPU likely can handle a longer training duration without issue).
+
+## Q10:How much training set duration is needed?
+
+A dataset of around 10min to 50min is recommended.
+
+With guaranteed high sound quality and low bottom noise, more can be added if the dataset's timbre is uniform.
+
+For a high-level training set (lean + distinctive tone), 5min to 10min is fine.
+
+There are some people who have trained successfully with 1min to 2min data, but the success is not reproducible by others and is not very informative.
This requires that the training set has a very distinctive timbre (e.g. a high-frequency airy anime girl sound) and the quality of the audio is high;
+Data of less than 1min duration has not been successfully attempted so far. This is not recommended.
+
+
+## Q11:What is the index rate for and how to adjust it?
+If the tone quality of the pre-trained model and inference source is higher than that of the training set, they can bring up the tone quality of the inference result, but at the cost of a possible tone bias towards the tone of the underlying model/inference source rather than the tone of the training set, which is generally referred to as "tone leakage".
+
+The index rate is used to reduce/resolve the timbre leakage problem. If the index rate is set to 1, theoretically there is no timbre leakage from the inference source and the timbre quality is more biased towards the training set. If the training set has a lower sound quality than the inference source, then a higher index rate may reduce the sound quality. Turning it down to 0 does not have the effect of using retrieval blending to protect the training set tones.
+
+If the training set has good audio quality and long duration, turn up the total_epoch, when the model itself is less likely to refer to the inferred source and the pretrained underlying model, and there is little "tone leakage", the index_rate is not important and you can even not create/share the index file.
+
+## Q12:How to choose the gpu when inferring?
+In the config.py file, select the card number after "device cuda:".
+
+The mapping between card number and graphics card can be seen in the graphics card information section of the training tab.
+
+## Q13:How to use the model saved in the middle of training?
+Save via model extraction at the bottom of the ckpt processing tab.
+
+## Q14:File/memory error(when training)?
+Too many processes and your memory is not enough. You may fix it by:
+
+1、decrease the input in field "Threads of CPU".
+
+2、pre-cut trainset to shorter audio files.
+
+## Q15: How to continue training using more data
+
+step1: put all wav data to path2.
+
+step2: exp_name2+path2 -> process dataset and extract feature.
+
+step3: copy the latest G and D file of exp_name1 (your previous experiment) into exp_name2 folder.
+
+step4: click "train the model", and it will continue training from the beginning of your previous exp model epoch.
+
+## Q16: error about llvmlite.dll
+
+OSError: Could not load shared object file: llvmlite.dll
+
+FileNotFoundError: Could not find module lib\site-packages\llvmlite\binding\llvmlite.dll (or one of its dependencies). Try using the full path with constructor syntax.
+
+The issue will happen in windows, install https://aka.ms/vs/17/release/vc_redist.x64.exe and it will be fixed.
+
+## Q17: RuntimeError: The expanded size of the tensor (17280) must match the existing size (0) at non-singleton dimension 1. Target sizes: [1, 17280]. Tensor sizes: [0]
+
+Delete the wav files whose size is significantly smaller than others, and that won't happen again. Than click "train the model"and "train the index".
+
+## Q18: RuntimeError: The size of tensor a (24) must match the size of tensor b (16) at non-singleton dimension 2
+
+Do not change the sampling rate and then continue training. If it is necessary to change, the exp name should be changed and the model will be trained from scratch. You can also copy the pitch and features (0/1/2/2b folders) extracted last time to accelerate the training process.
+
diff --git a/docs/en/training_tips_en.md b/docs/en/training_tips_en.md
new file mode 100644
index 0000000..ab9b1f8
--- /dev/null
+++ b/docs/en/training_tips_en.md
@@ -0,0 +1,65 @@
+Instructions and tips for RVC training
+======================================
+This TIPS explains how data training is done.
+
+# Training flow
+I will explain along the steps in the training tab of the GUI.
+
+## step1
+Set the experiment name here.
+
+You can also set here whether the model should take pitch into account.
+If the model doesn't consider pitch, the model will be lighter, but not suitable for singing.
+
+Data for each experiment is placed in `/logs/your-experiment-name/`.
+
+## step2a
+Loads and preprocesses audio.
+
+### load audio
+If you specify a folder with audio, the audio files in that folder will be read automatically.
+For example, if you specify `C:Users\hoge\voices`, `C:Users\hoge\voices\voice.mp3` will be loaded, but `C:Users\hoge\voices\dir\voice.mp3` will Not loaded.
+
+Since ffmpeg is used internally for reading audio, if the extension is supported by ffmpeg, it will be read automatically.
+After converting to int16 with ffmpeg, convert to float32 and normalize between -1 to 1.
+
+### denoising
+The audio is smoothed by scipy's filtfilt.
+
+### Audio Split
+First, the input audio is divided by detecting parts of silence that last longer than a certain period (max_sil_kept=5 seconds?). After splitting the audio on silence, split the audio every 4 seconds with an overlap of 0.3 seconds. For audio separated within 4 seconds, after normalizing the volume, convert the wav file to `/logs/your-experiment-name/0_gt_wavs` and then convert it to 16k sampling rate to `/logs/your-experiment-name/1_16k_wavs ` as a wav file.
+
+## step2b
+### Extract pitch
+Extract pitch information from wav files. Extract the pitch information (=f0) using the method built into parselmouth or pyworld and save it in `/logs/your-experiment-name/2a_f0`. Then logarithmically convert the pitch information to an integer between 1 and 255 and save it in `/logs/your-experiment-name/2b-f0nsf`.
+
+### Extract feature_print
+Convert the wav file to embedding in advance using HuBERT. Read the wav file saved in `/logs/your-experiment-name/1_16k_wavs`, convert the wav file to 256-dimensional features with HuBERT, and save in npy format in `/logs/your-experiment-name/3_feature256`.
+
+## step3
+train the model.
+### Glossary for Beginners
+In deep learning, the data set is divided and the learning proceeds little by little. In one model update (step), batch_size data are retrieved and predictions and error corrections are performed. Doing this once for a dataset counts as one epoch.
+
+Therefore, the learning time is the learning time per step x (the number of data in the dataset / batch size) x the number of epochs. In general, the larger the batch size, the more stable the learning becomes (learning time per step ÷ batch size) becomes smaller, but it uses more GPU memory. GPU RAM can be checked with the nvidia-smi command. Learning can be done in a short time by increasing the batch size as much as possible according to the machine of the execution environment.
+
+### Specify pretrained model
+RVC starts training the model from pretrained weights instead of from 0, so it can be trained with a small dataset.
+
+By default
+
+- If you consider pitch, it loads `rvc-location/pretrained/f0G40k.pth` and `rvc-location/pretrained/f0D40k.pth`.
+- If you don't consider pitch, it loads `rvc-location/pretrained/f0G40k.pth` and `rvc-location/pretrained/f0D40k.pth`.
+
+When learning, model parameters are saved in `logs/your-experiment-name/G_{}.pth` and `logs/your-experiment-name/D_{}.pth` for each save_every_epoch, but by specifying this path, you can start learning. You can restart or start training from model weights learned in a different experiment.
+
+### learning index
+RVC saves the HuBERT feature values used during training, and during inference, searches for feature values that are similar to the feature values used during learning to perform inference. In order to perform this search at high speed, the index is learned in advance.
+For index learning, we use the approximate neighborhood search library faiss. Read the feature value of `logs/your-experiment-name/3_feature256` and use it to learn the index, and save it as `logs/your-experiment-name/add_XXX.index`.
+
+(From the 20230428update version, it is read from the index, and saving / specifying is no longer necessary.)
+
+### Button description
+- Train model: After executing step2b, press this button to train the model.
+- Train feature index: After training the model, perform index learning.
+- One-click training: step2b, model training and feature index training all at once.
\ No newline at end of file
diff --git a/docs/fr/Changelog_FR.md b/docs/fr/Changelog_FR.md
new file mode 100644
index 0000000..b5f4acd
--- /dev/null
+++ b/docs/fr/Changelog_FR.md
@@ -0,0 +1,102 @@
+### 2023-08-13
+1-Corrections régulières de bugs
+- Modification du nombre total d'époques minimum à 1 et changement du nombre total d'époques minimum à 2
+- Correction des erreurs d'entraînement sans utiliser de modèles pré-entraînés
+- Après la séparation des voix d'accompagnement, libération de la mémoire graphique
+- Changement du chemin absolu d'enregistrement de faiss en chemin relatif
+- Prise en charge des chemins contenant des espaces (le chemin du jeu de données d'entraînement et le nom de l'expérience sont pris en charge, et aucune erreur ne sera signalée)
+- La liste de fichiers annule l'encodage utf8 obligatoire
+- Résolution du problème de consommation de CPU causé par la recherche faiss lors des changements de voix en temps réel
+
+2-Mises à jour clés
+- Entraînement du modèle d'extraction de hauteur vocale open-source le plus puissant actuel, RMVPE, et utilisation pour l'entraînement, l'inférence hors ligne/en temps réel de RVC, supportant PyTorch/Onnx/DirectML
+- Prise en charge des cartes graphiques AMD et Intel via Pytorch_DML
+
+(1) Changement de voix en temps réel (2) Inférence (3) Séparation de l'accompagnement vocal (4) L'entraînement n'est pas actuellement pris en charge, passera à l'entraînement CPU; prend en charge l'inférence RMVPE de la GPU par Onnx_Dml
+
+### 2023-06-18
+- Nouveaux modèles pré-entraînés v2 : 32k et 48k
+- Correction des erreurs d'inférence du modèle non-f0
+- Pour un jeu de données d'entraînement dépassant 1 heure, réalisation automatique de minibatch-kmeans pour réduire la forme des caractéristiques, afin que l'entraînement, l'ajout et la recherche d'index soient beaucoup plus rapides.
+- Fourniture d'un espace huggingface vocal2guitar jouet
+- Suppression automatique des audios de jeu de données d'entraînement court-circuitant les valeurs aberrantes
+- Onglet d'exportation Onnx
+
+Expériences échouées:
+- ~~Récupération de caractéristiques : ajout de la récupération de caractéristiques temporelles : non efficace~~
+- ~~Récupération de caractéristiques : ajout de la réduction de dimensionnalité PCAR : la recherche est encore plus lente~~
+- ~~Augmentation aléatoire des données lors de l'entraînement : non efficace~~
+
+Liste de tâches:
+- ~~Vocos-RVC (vocodeur minuscule) : non efficace~~
+- ~~Support de Crepe pour l'entraînement : remplacé par RMVPE~~
+- ~~Inférence de précision à moitié crepe : remplacée par RMVPE. Et difficile à réaliser.~~
+- Support de l'éditeur F0
+
+### 2023-05-28
+- Ajout d'un cahier v2, changelog coréen, correction de certaines exigences environnementales
+- Ajout d'un mode de protection des consonnes muettes et de la respiration
+- Support de la détection de hauteur crepe-full
+- Séparation vocale UVR5 : support des modèles de déréverbération et de désécho
+- Ajout du nom de l'expérience et de la version sur le nom de l'index
+- Support pour les utilisateurs de sélectionner manuellement le format d'exportation des audios de sortie lors du traitement de conversion vocale en lots et de la séparation vocale UVR5
+- L'entraînement du modèle v1 32k n'est plus pris en charge
+
+### 2023-05-13
+- Nettoyage des codes redondants de l'ancienne version du runtime dans le package en un clic : lib.infer_pack et uvr5_pack
+- Correction du bug de multiprocessus pseudo dans la préparation du jeu de données d'entraînement
+- Ajout de l'ajustement du rayon de filtrage médian pour l'algorithme de reconnaissance de hauteur de récolte
+- Prise en charge du rééchantillonnage post-traitement pour l'exportation audio
+- Réglage de multi-traitement "n_cpu" pour l'entraînement est passé de "extraction f0" à "prétraitement des données et extraction f0"
+- Détection automatique des chemins d'index sous le dossier de logs et fourniture d'une fonction de liste déroulante
+- Ajout de "Questions fréquemment posées et réponses" sur la page d'onglet (vous pouvez également consulter le wiki github RVC)
+- Lors de l'inférence, la hauteur de la récolte est mise en cache lors de l'utilisation du même chemin d'accès audio d'entrée (objectif : en utilisant l'extraction de
+
+ la hauteur de la récolte, l'ensemble du pipeline passera par un long processus d'extraction de la hauteur répétitif. Si la mise en cache n'est pas utilisée, les utilisateurs qui expérimentent différents timbres, index, et réglages de rayon de filtrage médian de hauteur connaîtront un processus d'attente très douloureux après la première inférence)
+
+### 2023-05-14
+- Utilisation de l'enveloppe de volume de l'entrée pour mixer ou remplacer l'enveloppe de volume de la sortie (peut atténuer le problème du "muet en entrée et bruit de faible amplitude en sortie". Si le bruit de fond de l'audio d'entrée est élevé, il n'est pas recommandé de l'activer, et il n'est pas activé par défaut (1 peut être considéré comme n'étant pas activé)
+- Prise en charge de la sauvegarde des modèles extraits à une fréquence spécifiée (si vous voulez voir les performances sous différentes époques, mais que vous ne voulez pas sauvegarder tous les grands points de contrôle et extraire manuellement les petits modèles par ckpt-processing à chaque fois, cette fonctionnalité sera très pratique)
+- Résolution du problème des "erreurs de connexion" causées par le proxy global du serveur en définissant des variables d'environnement
+- Prise en charge des modèles pré-entraînés v2 (actuellement, seule la version 40k est disponible au public pour les tests, et les deux autres taux d'échantillonnage n'ont pas encore été entièrement entraînés)
+- Limite le volume excessif dépassant 1 avant l'inférence
+- Réglages légèrement ajustés de la préparation du jeu de données d'entraînement
+
+#######################
+
+Historique des changelogs:
+
+### 2023-04-09
+- Correction des paramètres d'entraînement pour améliorer le taux d'utilisation du GPU : A100 est passé de 25% à environ 90%, V100 : de 50% à environ 90%, 2060S : de 60% à environ 85%, P40 : de 25% à environ 95% ; amélioration significative de la vitesse d'entraînement
+- Changement de paramètre : la taille de batch_size totale est maintenant la taille de batch_size par GPU
+- Changement de total_epoch : la limite maximale est passée de 100 à 1000 ; la valeur par défaut est passée de 10 à 20
+- Correction du problème d'extraction de ckpt reconnaissant la hauteur de manière incorrecte, causant une inférence anormale
+- Correction du problème d'entraînement distribué sauvegardant ckpt pour chaque rang
+- Application du filtrage des caractéristiques nan pour l'extraction des caractéristiques
+- Correction du problème d'entrée/sortie silencieuse produisant des consonnes aléatoires ou du bruit (les anciens modèles doivent être réentraînés avec un nouveau jeu de données)
+
+### 2023-04-16 Mise à jour
+- Ajout d'une mini-interface graphique pour le changement de voix en temps réel, démarrage par double-clic sur go-realtime_gui.bat
+- Application d'un filtrage pour les bandes de fréquences inférieures à 50Hz pendant l'entraînement et l'inférence
+- Abaissement de l'extraction de hauteur minimale de pyworld du défaut 80 à 50 pour l'entraînement et l'inférence, permettant aux voix masculines graves entre 50-80Hz de ne pas être mises en sourdine
+- WebUI prend en charge le changement de langue en fonction des paramètres régionaux du système (prise en charge actuelle de en_US, ja_JP, zh_CN, zh_HK, zh_SG, zh_TW ; défaut à en_US si non pris en charge)
+- Correction de la reconnaissance de certains GPU (par exemple, échec de reconnaissance V100-16G, échec de reconnaissance P4)
+
+### 2023-04-28 Mise à jour
+- Mise à niveau des paramètres d'index de faiss pour une vitesse plus rapide et une meilleure qualité
+- Suppression de la dépendance à total_npy ; le partage futur de modèles ne nécessitera pas d'entrée total
+
+_npy
+- Levée des restrictions pour les GPU de la série 16, fournissant des paramètres d'inférence de 4 Go pour les GPU VRAM de 4 Go
+- Correction d'un bug dans la séparation vocale d'accompagnement UVR5 pour certains formats audio
+- La mini-interface de changement de voix en temps réel prend maintenant en charge les modèles de hauteur non-40k et non-lazy
+
+### Plans futurs :
+Fonctionnalités :
+- Ajouter une option : extraire de petits modèles pour chaque sauvegarde d'époque
+- Ajouter une option : exporter un mp3 supplémentaire vers le chemin spécifié pendant l'inférence
+- Prise en charge de l'onglet d'entraînement multi-personnes (jusqu'à 4 personnes)
+
+Modèle de base :
+- Collecter des fichiers wav de respiration pour les ajouter au jeu de données d'entraînement pour résoudre le problème des sons de respiration déformés
+- Nous entraînons actuellement un modèle de base avec un jeu de données de chant étendu, qui sera publié à l'avenir
diff --git a/docs/fr/README.fr.md b/docs/fr/README.fr.md
new file mode 100644
index 0000000..039a592
--- /dev/null
+++ b/docs/fr/README.fr.md
@@ -0,0 +1,169 @@
+
+
+
Retrieval-based-Voice-Conversion-WebUI
+Un framework simple et facile à utiliser pour la conversion vocale (modificateur de voix) basé sur VITS
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+[**Journal de mise à jour**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/docs/Changelog_CN.md) | [**FAQ**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94) | [**AutoDL·Formation d'un chanteur AI pour 5 centimes**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/Autodl%E8%AE%AD%E7%BB%83RVC%C2%B7AI%E6%AD%8C%E6%89%8B%E6%95%99%E7%A8%8B) | [**Enregistrement des expériences comparatives**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/%E5%AF%B9%E7%85%A7%E5%AE%9E%E9%AA%8C%C2%B7%E5%AE%9E%E9%AA%8C%E8%AE%B0%E5%BD%95) | [**Démonstration en ligne**](https://huggingface.co/spaces/Ricecake123/RVC-demo)
+
+
+
+------
+
+[**English**](../en/README.en.md) | [ **中文简体**](../../README.md) | [**日本語**](../jp/README.ja.md) | [**한국어**](../kr/README.ko.md) ([**韓國語**](../kr/README.ko.han.md)) | [**Français**](../fr/README.fr.md) | [**Turc**](../tr/README.tr.md) | [**Português**](../pt/README.pt.md)
+
+Cliquez ici pour voir notre [vidéo de démonstration](https://www.bilibili.com/video/BV1pm4y1z7Gm/) !
+
+> Conversion vocale en temps réel avec RVC : [w-okada/voice-changer](https://github.com/w-okada/voice-changer)
+
+> Le modèle de base est formé avec près de 50 heures de données VCTK de haute qualité et open source. Aucun souci concernant les droits d'auteur, n'hésitez pas à l'utiliser.
+
+> Attendez-vous au modèle de base RVCv3 : plus de paramètres, plus de données, de meilleurs résultats, une vitesse d'inférence presque identique, et nécessite moins de données pour la formation.
+
+## Introduction
+Ce dépôt a les caractéristiques suivantes :
++ Utilise le top1 pour remplacer les caractéristiques de la source d'entrée par les caractéristiques de l'ensemble d'entraînement pour éliminer les fuites de timbre vocal.
++ Peut être formé rapidement même sur une carte graphique relativement moins performante.
++ Obtient de bons résultats même avec peu de données pour la formation (il est recommandé de collecter au moins 10 minutes de données vocales avec un faible bruit de fond).
++ Peut changer le timbre vocal en fusionnant des modèles (avec l'aide de l'onglet ckpt-merge).
++ Interface web simple et facile à utiliser.
++ Peut appeler le modèle UVR5 pour séparer rapidement la voix et l'accompagnement.
++ Utilise l'algorithme de pitch vocal le plus avancé [InterSpeech2023-RMVPE](#projets-référencés) pour éliminer les problèmes de voix muette. Meilleurs résultats, plus rapide que crepe_full, et moins gourmand en ressources.
++ Support d'accélération pour les cartes AMD et Intel.
+
+## Configuration de l'environnement
+Exécutez les commandes suivantes dans un environnement Python de version 3.8 ou supérieure.
+
+(Windows/Linux)
+Installez d'abord les dépendances principales via pip :
+```bash
+# Installez Pytorch et ses dépendances essentielles, sautez si déjà installé.
+# Voir : https://pytorch.org/get-started/locally/
+pip install torch torchvision torchaudio
+
+# Pour les utilisateurs de Windows avec une architecture Nvidia Ampere (RTX30xx), en se basant sur l'expérience #21, spécifiez la version CUDA correspondante pour Pytorch.
+pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+
+# Pour Linux + carte AMD, utilisez cette version de Pytorch:
+pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2
+```
+
+Vous pouvez utiliser poetry pour installer les dépendances :
+```bash
+# Installez l'outil de gestion des dépendances Poetry, sautez si déjà installé.
+# Voir : https://python-poetry.org/docs/#installation
+curl -sSL https://install.python-poetry.org | python3 -
+
+# Installez les dépendances avec poetry.
+poetry install
+```
+
+Ou vous pouvez utiliser pip pour installer les dépendances :
+```bash
+# Cartes Nvidia :
+pip install -r requirements.txt
+
+# Cartes AMD/Intel :
+pip install -r requirements-dml.txt
+
+# Cartes AMD sur Linux (ROCm)
+pip install -r requirements-amd.txt
+```
+
+------
+Les utilisateurs de Mac peuvent exécuter `run.sh` pour installer les dépendances :
+```bash
+sh ./run.sh
+```
+
+## Préparation d'autres modèles pré-entraînés
+RVC nécessite d'autres modèles pré-entraînés pour l'inférence et la formation.
+
+Vous pouvez télécharger ces modèles depuis notre [espace Hugging Face](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/).
+
+Voici une liste des modèles et autres fichiers requis par RVC :
+```bash
+./assets/hubert_base
+
+./assets/pretrained
+
+./assets/uvr5_weights
+
+# Pour tester la version v2 du modèle, téléchargez également :
+
+./assets/pretrained_v2
+
+# Si vous utilisez Windows, vous pourriez avoir besoin de ces fichiers pour ffmpeg et ffprobe, sautez cette étape si vous avez déjà installé ffmpeg et ffprobe. Les utilisateurs d'ubuntu/debian peuvent installer ces deux bibliothèques avec apt install ffmpeg. Les utilisateurs de Mac peuvent les installer avec brew install ffmpeg (prérequis : avoir installé brew).
+
+# ./ffmpeg
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffmpeg.exe
+
+# ./ffprobe
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffprobe.exe
+
+# Si vous souhaitez utiliser le dernier algorithme RMVPE de pitch vocal, téléchargez les paramètres du modèle de pitch et placez-les dans le répertoire racine de RVC.
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.pt
+
+ # Les utilisateurs de cartes AMD/Intel nécessitant l'environnement DML doivent télécharger :
+
+ https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.onnx
+
+```
+Ensuite, exécutez la commande suivante pour démarrer WebUI :
+```bash
+python webui.py
+```
+
+Si vous utilisez Windows ou macOS, vous pouvez télécharger et extraire `RVC-beta.7z`. Les utilisateurs de Windows peuvent exécuter `go-webui.bat` pour démarrer WebUI, tandis que les utilisateurs de macOS peuvent exécuter `sh ./run.sh`.
+
+## Compatibilité ROCm pour les cartes AMD (seulement Linux)
+Installez tous les pilotes décrits [ici](https://rocm.docs.amd.com/en/latest/deploy/linux/os-native/install.html).
+
+Sur Arch utilisez pacman pour installer le pilote:
+````
+pacman -S rocm-hip-sdk rocm-opencl-sdk
+````
+
+Vous devrez peut-être créer ces variables d'environnement (par exemple avec RX6700XT):
+````
+export ROCM_PATH=/opt/rocm
+export HSA_OVERRIDE_GFX_VERSION=10.3.0
+````
+Assurez-vous que votre utilisateur est dans les groupes `render` et `video`:
+````
+sudo usermod -aG render $USERNAME
+sudo usermod -aG video $USERNAME
+````
+Enfin vous pouvez exécuter WebUI:
+```bash
+python webui.py
+```
+
+## Crédits
++ [ContentVec](https://github.com/auspicious3000/contentvec/)
++ [VITS](https://github.com/jaywalnut310/vits)
++ [HIFIGAN](https://github.com/jik876/hifi-gan)
++ [Gradio](https://github.com/gradio-app/gradio)
++ [FFmpeg](https://github.com/FFmpeg/FFmpeg)
++ [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
++ [audio-slicer](https://github.com/openvpi/audio-slicer)
++ [Extraction de la hauteur vocale : RMVPE](https://github.com/Dream-High/RMVPE)
+ + Le modèle pré-entraîné a été formé et testé par [yxlllc](https://github.com/yxlllc/RMVPE) et [RVC-Boss](https://github.com/RVC-Boss).
+
+## Remerciements à tous les contributeurs pour leurs efforts
+
+
+
diff --git a/docs/fr/faiss_tips_fr.md b/docs/fr/faiss_tips_fr.md
new file mode 100644
index 0000000..7fde76a
--- /dev/null
+++ b/docs/fr/faiss_tips_fr.md
@@ -0,0 +1,105 @@
+Conseils de réglage pour faiss
+==================
+# À propos de faiss
+faiss est une bibliothèque de recherches de voisins pour les vecteurs denses, développée par Facebook Research, qui implémente efficacement de nombreuses méthodes de recherche de voisins approximatifs.
+La recherche de voisins approximatifs trouve rapidement des vecteurs similaires tout en sacrifiant une certaine précision.
+
+## faiss dans RVC
+Dans RVC, pour l'incorporation des caractéristiques converties par HuBERT, nous recherchons des incorporations similaires à l'incorporation générée à partir des données d'entraînement et les mixons pour obtenir une conversion plus proche de la parole originale. Cependant, cette recherche serait longue si elle était effectuée de manière naïve, donc une conversion à haute vitesse est réalisée en utilisant une recherche de voisinage approximatif.
+
+# Vue d'ensemble de la mise en œuvre
+Dans '/logs/votre-expérience/3_feature256' où le modèle est situé, les caractéristiques extraites par HuBERT de chaque donnée vocale sont situées.
+À partir de là, nous lisons les fichiers npy dans un ordre trié par nom de fichier et concaténons les vecteurs pour créer big_npy. (Ce vecteur a la forme [N, 256].)
+Après avoir sauvegardé big_npy comme /logs/votre-expérience/total_fea.npy, nous l'entraînons avec faiss.
+
+Dans cet article, j'expliquerai la signification de ces paramètres.
+
+# Explication de la méthode
+## Usine d'index
+Une usine d'index est une notation unique de faiss qui exprime un pipeline qui relie plusieurs méthodes de recherche de voisinage approximatif sous forme de chaîne.
+Cela vous permet d'essayer diverses méthodes de recherche de voisinage approximatif simplement en changeant la chaîne de l'usine d'index.
+Dans RVC, elle est utilisée comme ceci :
+
+```python
+index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)
+```
+
+Parmi les arguments de index_factory, le premier est le nombre de dimensions du vecteur, le second est la chaîne de l'usine d'index, et le troisième est la distance à utiliser.
+
+Pour une notation plus détaillée :
+https://github.com/facebookresearch/faiss/wiki/The-index-factory
+
+## Index pour la distance
+Il existe deux index typiques utilisés comme similarité de l'incorporation comme suit :
+
+- Distance euclidienne (METRIC_L2)
+- Produit intérieur (METRIC_INNER_PRODUCT)
+
+La distance euclidienne prend la différence au carré dans chaque dimension, somme les différences dans toutes les dimensions, puis prend la racine carrée. C'est la même chose que la distance en 2D et 3D que nous utilisons au quotidien.
+Le produit intérieur n'est pas utilisé comme index de similarité tel quel, et la similarité cosinus qui prend le produit intérieur après avoir été normalisé par la norme L2 est généralement utilisée.
+
+Lequel est le mieux dépend du cas, mais la similarité cosinus est souvent utilisée dans l'incorporation obtenue par word2vec et des modèles de récupération d'images similaires appris par ArcFace. Si vous voulez faire une normalisation l2 sur le vecteur X avec numpy, vous pouvez le faire avec le code suivant avec eps suffisamment petit pour éviter une division par 0.
+
+```python
+X_normed = X / np.maximum(eps, np.linalg.norm(X, ord=2, axis=-1, keepdims=True))
+```
+
+De plus, pour l'usine d'index, vous pouvez changer l'index de distance utilisé pour le calcul en choisissant la valeur à passer comme troisième argument.
+
+```python
+index = faiss.index_factory(dimention, texte, faiss.METRIC_INNER_PRODUCT)
+```
+
+## IVF
+IVF (Inverted file indexes) est un algorithme similaire à l'index inversé dans la recherche en texte intégral.
+Lors de l'apprentissage, la cible de recherche est regroupée avec kmeans, et une partition de Voronoi est effectuée en utilisant le centre du cluster. Chaque point de données est attribué à un cluster, nous créons donc un dictionnaire qui permet de rechercher les points de données à partir des clusters.
+
+Par exemple, si des clusters sont attribués comme suit :
+|index|Cluster|
+|-----|-------|
+|1|A|
+|2|B|
+|3|A|
+|4|C|
+|5|B|
+
+L'index inversé résultant ressemble à ceci :
+
+|cluster|index|
+|-------|-----|
+|A|1, 3|
+|B|2, 5|
+|C|4|
+
+Lors de la recherche, nous recherchons d'abord n_probe clusters parmi les clusters, puis nous calculons les distances pour les points de données appartenant à chaque cluster.
+
+# Recommandation de paramètre
+Il existe des directives officielles sur la façon de choisir un index, je vais donc expliquer en conséquence.
+https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index
+
+Pour les ensembles de données inférieurs à 1M, 4bit-PQ est la méthode la plus efficace disponible dans faiss en avril 2023.
+En combinant cela avec IVF, en réduisant les candidats avec 4bit-PQ, et enfin en recalculant la distance avec un index précis, on peut le décrire en utilisant l'usine d'index suivante.
+
+```python
+index = faiss.index_factory(256, "IVF1024,PQ128x4fs,RFlat")
+```
+
+## Paramètres recommandés pour IVF
+Considérez le cas de trop d'IVF. Par exemple, si une quantification grossière par IVF est effectuée pour le nombre de données, cela revient à une recherche exhaustive naïve et est inefficace.
+Pour 1M ou moins, les valeurs IVF sont recommandées entre 4*sqrt(N) ~ 16*sqrt(N) pour N nombre de points de données.
+
+Comme le temps de calcul augmente proportionnellement au nombre de n_probes, veuillez consulter la précision et choisir de manière appropriée. Personnellement, je ne pense pas que RVC ait besoin de tant de précision, donc n_probe = 1 est bien.
+
+## FastScan
+FastScan est une méthode qui permet d'approximer rapidement les distances par quantification de produit cartésien en les effectuant dans les registres.
+La quantification du produit cartésien effectue un regroupement indépendamment
+
+ pour chaque dimension d (généralement d = 2) pendant l'apprentissage, calcule la distance entre les clusters à l'avance, et crée une table de recherche. Au moment de la prédiction, la distance de chaque dimension peut être calculée en O(1) en consultant la table de recherche.
+Le nombre que vous spécifiez après PQ spécifie généralement la moitié de la dimension du vecteur.
+
+Pour une description plus détaillée de FastScan, veuillez consulter la documentation officielle.
+https://github.com/facebookresearch/faiss/wiki/Fast-accumulation-of-PQ-and-AQ-codes-(FastScan)
+
+## RFlat
+RFlat est une instruction pour recalculer la distance approximative calculée par FastScan avec la distance exacte spécifiée par le troisième argument de l'usine d'index.
+Lors de l'obtention de k voisins, k*k_factor points sont recalculés.
diff --git a/docs/fr/faq_fr.md b/docs/fr/faq_fr.md
new file mode 100644
index 0000000..52ebafc
--- /dev/null
+++ b/docs/fr/faq_fr.md
@@ -0,0 +1,169 @@
+## Q1: Erreur ffmpeg/erreur utf8.
+Il s'agit très probablement non pas d'un problème lié à FFmpeg, mais d'un problème lié au chemin de l'audio ;
+
+FFmpeg peut rencontrer une erreur lors de la lecture de chemins contenant des caractères spéciaux tels que des espaces et (), ce qui peut provoquer une erreur FFmpeg ; et lorsque l'audio du jeu d'entraînement contient des chemins en chinois, l'écrire dans filelist.txt peut provoquer une erreur utf8.
+
+## Q2: Impossible de trouver le fichier index après "Entraînement en un clic".
+Si l'affichage indique "L'entraînement est terminé. Le programme est fermé", alors le modèle a été formé avec succès, et les erreurs subséquentes sont fausses ;
+
+L'absence d'un fichier index 'ajouté' après un entraînement en un clic peut être due au fait que le jeu d'entraînement est trop grand, ce qui bloque l'ajout de l'index ; cela a été résolu en utilisant un traitement par lots pour ajouter l'index, ce qui résout le problème de surcharge de mémoire lors de l'ajout de l'index. Comme solution temporaire, essayez de cliquer à nouveau sur le bouton "Entraîner l'index".
+
+## Q3: Impossible de trouver le modèle dans “Inférence du timbre” après l'entraînement
+Cliquez sur “Actualiser la liste des timbres” et vérifiez à nouveau ; si vous ne le voyez toujours pas, vérifiez s'il y a des erreurs pendant l'entraînement et envoyez des captures d'écran de la console, de l'interface utilisateur web, et des logs/nom_de_l'expérience/*.log aux développeurs pour une analyse plus approfondie.
+
+## Q4: Comment partager un modèle/Comment utiliser les modèles d'autres personnes ?
+Les fichiers pth stockés dans rvc_root/logs/nom_de_l'expérience ne sont pas destinés à être partagés ou inférés, mais à stocker les points de contrôle de l'expérience pour la reproductibilité et l'entraînement ultérieur. Le modèle à partager doit être le fichier pth de 60+MB dans le dossier des poids ;
+
+À l'avenir, les poids/nom_de_l'expérience.pth et les logs/nom_de_l'expérience/ajouté_xxx.index seront fusionnés en un seul fichier poids/nom_de_l'expérience.zip pour éliminer le besoin d'une entrée d'index manuelle ; partagez donc le fichier zip, et non le fichier pth, sauf si vous souhaitez continuer l'entraînement sur une machine différente ;
+
+Copier/partager les fichiers pth de plusieurs centaines de Mo du dossier des logs au dossier des poids pour une inférence forcée peut entraîner des erreurs telles que des f0, tgt_sr, ou d'autres clés manquantes. Vous devez utiliser l'onglet ckpt en bas pour sélectionner manuellement ou automatiquement (si l'information se trouve dans les logs/nom_de_l'expérience), si vous souhaitez inclure les informations sur la hauteur et les options de taux d'échantillonnage audio cible, puis extraire le modèle plus petit. Après extraction, il y aura un fichier pth de 60+ MB dans le dossier des poids, et vous pouvez actualiser les voix pour l'utiliser.
+
+## Q5: Erreur de connexion.
+Il se peut que vous ayez fermé la console (fenêtre de ligne de commande noire).
+
+## Q6: WebUI affiche 'Expecting value: line 1 column 1 (char 0)'.
+Veuillez désactiver le proxy système LAN/proxy global puis rafraîchir.
+
+## Q7: Comment s'entraîner et déduire sans le WebUI ?
+Script d'entraînement :
+Vous pouvez d'abord lancer l'entraînement dans WebUI, et les versions en ligne de commande de la préparation du jeu de données et de l'entraînement seront affichées dans la fenêtre de message.
+
+Script d'inférence :
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/myinfer.py
+
+Par exemple :
+
+runtime\python.exe myinfer.py 0 "E:\codes\py39\RVC-beta\todo-songs\1111.wav" "E:\codes\py39\logs\mi-test\added_IVF677_Flat_nprobe_7.index" récolte "test.wav" "weights/mi-test.pth" 0.6 cuda:0 True
+
+f0up_key=sys.argv[1]
+input_path=sys.argv[2]
+index_path=sys.argv[3]
+f0method=sys.argv[4]#récolte ou pm
+opt_path=sys.argv[5]
+model_path=sys.argv[6]
+index_rate=float(sys.argv[7])
+device=sys.argv[8]
+is_half=bool(sys.argv[9])
+
+### Explication des arguments :
+
+1. **Numéro de voix cible** : `0` (dans cet exemple)
+2. **Chemin du fichier audio d'entrée** : `"C:\ YOUR PATH FOR THE ROOT (RVC0813Nvidia)\INPUTS_VOCAL\vocal.wav"`
+3. **Chemin du fichier index** : `"C:\ YOUR PATH FOR THE ROOT (RVC0813Nvidia)\logs\Hagrid.index"`
+4. **Méthode pour l'extraction du pitch (F0)** : `harvest` (dans cet exemple)
+5. **Chemin de sortie pour le fichier audio traité** : `"C:\ YOUR PATH FOR THE ROOT (RVC0813Nvidia)\INPUTS_VOCAL\test.wav"`
+6. **Chemin du modèle** : `"C:\ YOUR PATH FOR THE ROOT (RVC0813Nvidia)\weights\HagridFR.pth"`
+7. **Taux d'index** : `0.6` (dans cet exemple)
+8. **Périphérique pour l'exécution (GPU/CPU)** : `cuda:0` pour une carte NVIDIA, par exemple.
+9. **Protection des droits d'auteur (True/False)**.
+
+
+
+## Q8: Erreur Cuda/Mémoire Cuda épuisée.
+Il y a une faible chance qu'il y ait un problème avec la configuration CUDA ou que le dispositif ne soit pas pris en charge ; plus probablement, il n'y a pas assez de mémoire (manque de mémoire).
+
+Pour l'entraînement, réduisez la taille du lot (si la réduction à 1 n'est toujours pas suffisante, vous devrez peut-être changer la carte graphique) ; pour l'inférence, ajustez les paramètres x_pad, x_query, x_center, et x_max dans le fichier config.py selon les besoins. Les cartes mémoire de 4 Go ou moins (par exemple 1060(3G) et diverses cartes de 2 Go) peuvent être abandonnées, tandis que les cartes mémoire de 4 Go ont encore une chance.
+
+## Q9: Combien de total_epoch sont optimaux ?
+Si la qualité audio du jeu d'entraînement est médiocre et que le niveau de bruit est élevé, 20-30 époques sont suffisantes. Le fixer trop haut n'améliorera pas la qualité audio de votre jeu d'entraînement de faible qualité.
+
+Si la qualité audio du jeu d'entraînement est élevée, le niveau de bruit est faible, et la durée est suffisante, vous pouvez l'augmenter. 200 est acceptable (puisque l'entraînement est rapide, et si vous êtes capable de préparer un jeu d'entraînement de haute qualité, votre GPU peut probablement gérer une durée d'entraînement plus longue sans problème).
+
+## Q10: Quelle durée de jeu d'entraînement est nécessaire ?
+Un jeu d'environ 10 min à 50 min est recommandé.
+
+Avec une garantie de haute qualité sonore et de faible bruit de fond, plus peut être ajouté si le timbre du jeu est uniforme.
+
+Pour un jeu d'entraînement de haut niveau (ton maigre + ton distinctif), 5 min à 10 min sont suffisantes.
+
+Il y a des personnes qui ont réussi à s'entraîner avec des données de 1 min à 2 min, mais le succès n'est pas reproductible par d'autres et n'est pas très informatif.
Cela nécessite que le jeu d'entraînement ait un timbre très distinctif (par exemple, un son de fille d'anime aérien à haute fréquence) et que la qualité de l'audio soit élevée ;
+Aucune tentative réussie n'a été faite jusqu'à présent avec des données de moins de 1 min. Cela n'est pas recommandé.
+
+## Q11: À quoi sert le taux d'index et comment l'ajuster ?
+Si la qualité tonale du modèle pré-entraîné et de la source d'inférence est supérieure à celle du jeu d'entraînement, ils peuvent améliorer la qualité tonale du résultat d'inférence, mais au prix d'un possible biais tonal vers le ton du modèle sous-jacent/source d'inférence plutôt que le ton du jeu d'entraînement, ce qui est généralement appelé "fuite de ton".
+
+Le taux d'index est utilisé pour réduire/résoudre le problème de la fuite de timbre. Si le taux d'index est fixé à 1, théoriquement il n'y a pas de fuite de timbre de la source d'inférence et la qualité du timbre est plus biaisée vers le jeu d'entraînement. Si le jeu d'entraînement a une qualité sonore inférieure à celle de la source d'inférence, alors un taux d'index plus élevé peut réduire la qualité sonore. Le réduire à 0 n'a pas l'effet d'utiliser le mélange de récupération pour protéger les tons du jeu d'entraînement.
+
+Si le jeu d'entraînement a une bonne qualité audio et une longue durée, augmentez le total_epoch, lorsque le modèle lui-même est moins susceptible de se référer à la source déduite et au modèle sous-jacent pré-entraîné, et qu'il y a peu de "fuite de ton", le taux d'index n'est pas important et vous pouvez même ne pas créer/partager le fichier index.
+
+## Q12: Comment choisir le gpu lors de l'inférence ?
+Dans le fichier config.py, sélectionnez le numéro de carte après "device cuda:".
+
+La correspondance entre le numéro de carte et la carte graphique peut être vue dans la section d'information de la carte graphique de l'onglet d'entraînement.
+
+## Q13: Comment utiliser le modèle sauvegardé au milieu de l'entraînement ?
+Sauvegardez via l'extraction de modèle en bas de l'onglet de traitement ckpt.
+
+## Q14: Erreur de fichier/erreur de mémoire (lors de l'entraînement) ?
+Il y a trop de processus et votre mémoire n'est pas suffisante. Vous pouvez le corriger en :
+
+1. Diminuer l'entrée dans le champ "Threads of CPU".
+
+2. Pré-découper le jeu d'entraînement en fichiers audio plus courts.
+
+## Q15: Comment poursuivre l'entraînement avec plus de données
+
+étape 1 : mettre toutes les données wav dans path2.
+
+étape 2 : exp_name2+path2 -> traiter le jeu de données et extraire la caractéristique.
+
+étape 3 : copier les derniers fichiers G et D de exp_name1 (votre expérience précédente) dans le dossier exp_name2.
+
+étape 4 : cliquez sur "entraîner le modèle", et il continuera l'entraînement depuis le début de votre époque de modèle exp précédente.
+
+## Q16: erreur à propos de llvmlite.dll
+
+OSError: Impossible de charger le fichier objet partagé : llvmlite.dll
+
+FileNotFoundError: Impossible de trouver le module lib\site-packages\llvmlite\binding\llvmlite.dll (ou l'une de ses dépendances). Essayez d'utiliser la syntaxe complète du constructeur.
+
+Le problème se produira sous Windows, installez https://aka.ms/vs/17/release/vc_redist.x64.exe et il sera corrigé.
+
+## Q17: RuntimeError: La taille étendue du tensor (17280) doit correspondre à la taille existante (0) à la dimension non-singleton 1. Tailles cibles : [1, 17280]. Tailles des tensors : [0]
+
+Supprimez les fichiers wav dont la taille est nettement inférieure à celle des autres, et cela ne se reproduira plus. Ensuite, cliquez sur "entraîner le modèle" et "entraîner l'index".
+
+## Q18: RuntimeError: La taille du tensor a (24) doit correspondre à la taille du tensor b (16) à la dimension non-singleton 2
+
+Ne changez pas le taux d'échantillonnage puis continuez l'entraînement. S'il est nécessaire de changer, le nom de l'expérience doit être modifié et le modèle sera formé à partir de zéro. Vous pouvez également copier les hauteurs et caractéristiques (dossiers 0/1/2/2b) extraites la dernière fois pour accélérer le processus d'entraînement.
+
diff --git a/docs/fr/training_tips_fr.md b/docs/fr/training_tips_fr.md
new file mode 100644
index 0000000..836b450
--- /dev/null
+++ b/docs/fr/training_tips_fr.md
@@ -0,0 +1,65 @@
+Instructions et conseils pour la formation RVC
+======================================
+Ces conseils expliquent comment se déroule la formation des données.
+
+# Flux de formation
+Je vais expliquer selon les étapes de l'onglet de formation de l'interface graphique.
+
+## étape 1
+Définissez ici le nom de l'expérience.
+
+Vous pouvez également définir ici si le modèle doit prendre en compte le pitch.
+Si le modèle ne considère pas le pitch, le modèle sera plus léger, mais pas adapté au chant.
+
+Les données de chaque expérience sont placées dans `/logs/nom-de-votre-experience/`.
+
+## étape 2a
+Charge et pré-traite l'audio.
+
+### charger l'audio
+Si vous spécifiez un dossier avec de l'audio, les fichiers audio de ce dossier seront lus automatiquement.
+Par exemple, si vous spécifiez `C:Users\hoge\voices`, `C:Users\hoge\voices\voice.mp3` sera chargé, mais `C:Users\hoge\voices\dir\voice.mp3` ne sera pas chargé.
+
+Comme ffmpeg est utilisé en interne pour lire l'audio, si l'extension est prise en charge par ffmpeg, elle sera lue automatiquement.
+Après la conversion en int16 avec ffmpeg, convertir en float32 et normaliser entre -1 et 1.
+
+### débruitage
+L'audio est lissé par filtfilt de scipy.
+
+### Séparation audio
+Tout d'abord, l'audio d'entrée est divisé en détectant des parties de silence qui durent plus d'une certaine période (max_sil_kept = 5 secondes ?). Après avoir séparé l'audio sur le silence, séparez l'audio toutes les 4 secondes avec un chevauchement de 0,3 seconde. Pour l'audio séparé en 4 secondes, après normalisation du volume, convertir le fichier wav en `/logs/nom-de-votre-experience/0_gt_wavs` puis le convertir à un taux d'échantillonnage de 16k dans `/logs/nom-de-votre-experience/1_16k_wavs` sous forme de fichier wav.
+
+## étape 2b
+### Extraire le pitch
+Extrait les informations de pitch des fichiers wav. Extraire les informations de pitch (=f0) en utilisant la méthode intégrée dans parselmouth ou pyworld et les sauvegarder dans `/logs/nom-de-votre-experience/2a_f0`. Convertissez ensuite logarithmiquement les informations de pitch en un entier entre 1 et 255 et sauvegardez-le dans `/logs/nom-de-votre-experience/2b-f0nsf`.
+
+### Extraire l'empreinte de caractéristique
+Convertissez le fichier wav en incorporation à l'avance en utilisant HuBERT. Lisez le fichier wav sauvegardé dans `/logs/nom-de-votre-experience/1_16k_wavs`, convertissez le fichier wav en caractéristiques de dimension 256 avec HuBERT, et sauvegardez au format npy dans `/logs/nom-de-votre-experience/3_feature256`.
+
+## étape 3
+former le modèle.
+### Glossaire pour les débutants
+Dans l'apprentissage profond, l'ensemble de données est divisé et l'apprentissage progresse petit à petit. Dans une mise à jour de modèle (étape), les données de batch_size sont récupérées et des prédictions et corrections d'erreur sont effectuées. Faire cela une fois pour un ensemble de données compte comme une époque.
+
+Par conséquent, le temps d'apprentissage est le temps d'apprentissage par étape x (le nombre de données dans l'ensemble de données / taille du lot) x le nombre d'époques. En général, plus la taille du lot est grande, plus l'apprentissage devient stable (temps d'apprentissage par étape ÷ taille du lot) devient plus petit, mais il utilise plus de mémoire GPU. La RAM GPU peut être vérifiée avec la commande nvidia-smi. L'apprentissage peut être effectué en peu de temps en augmentant la taille du lot autant que possible selon la machine de l'environnement d'exécution.
+
+### Spécifier le modèle pré-entraîné
+RVC commence à former le modèle à partir de poids pré-entraînés plutôt que de zéro, il peut donc être formé avec un petit ensemble de données.
+
+Par défaut :
+
+- Si vous considérez le pitch, il charge `rvc-location/pretrained/f0G40k.pth` et `rvc-location/pretrained/f0D40k.pth`.
+- Si vous ne considérez pas le pitch, il charge `rvc-location/pretrained/f0G40k.pth` et `rvc-location/pretrained/f0D40k.pth`.
+
+Lors de l'apprentissage, les paramètres du modèle sont sauvegardés dans `logs/nom-de-votre-experience/G_{}.pth` et `logs/nom-de-votre-experience/D_{}.pth` pour chaque save_every_epoch, mais en spécifiant ce chemin, vous pouvez démarrer l'apprentissage. Vous pouvez redémarrer ou commencer à former à partir de poids de modèle appris lors d'une expérience différente.
+
+### Index d'apprentissage
+RVC sauvegarde les valeurs de caractéristique HuBERT utilisées lors de la formation, et pendant l'inférence, recherche les valeurs de caractéristique qui sont similaires aux valeurs de caractéristique utilisées lors de l'apprentissage pour effectuer l'inférence. Afin d'effectuer cette recherche à haute vitesse, l'index est appris à l'avance.
+Pour l'apprentissage d'index, nous utilisons la bibliothèque de recherche de voisinage approximatif faiss. Lisez la valeur de caractéristique de `logs/nom-de-votre-experience/3_feature256` et utilisez-la pour apprendre l'index, et sauvegardez-la sous `logs/nom-de-votre-experience/add_XXX.index`.
+
+(À partir de la version de mise à jour 20230428, elle est lue à partir de l'index, et la sauvegarde / spécification n'est plus nécessaire.)
+
+### Description du bouton
+- Former le modèle : après avoir exécuté l'étape 2b, appuyez sur ce bouton pour former le modèle.
+- Former l'index de caractéristique : après avoir formé le modèle, effectuez un apprentissage d'index.
+- Formation en un clic : étape 2b, formation du modèle et formation de l'index de caractéristique tout d'un coup.```
diff --git a/docs/jp/Changelog_JA.md b/docs/jp/Changelog_JA.md
new file mode 100644
index 0000000..9ca36b6
--- /dev/null
+++ b/docs/jp/Changelog_JA.md
@@ -0,0 +1,123 @@
+### 2023 年 10 月 6 日更新
+
+リアルタイム声変換のためのインターフェース go-realtime_gui.bat/realtime_gui.py を作成しました(実際には既に存在していました)。今回のアップデートでは、リアルタイム声変換のパフォーマンスを重点的に最適化しました。0813 版との比較:
+
+- 1. インターフェース操作の最適化:パラメータのホット更新(パラメータ調整時に中断して再起動する必要がない)、レイジーロードモデル(既にロードされたモデルは再ロードする必要がない)、音量因子パラメータ追加(音量を入力オーディオに近づける)
+- 2. 内蔵ノイズリダクション効果と速度の最適化
+- 3. 推論速度の大幅な最適化
+
+入出力デバイスは同じタイプを選択する必要があります。例えば、両方とも MME タイプを選択します。
+
+1006 バージョンの全体的な更新は:
+
+- 1. rmvpe 音声ピッチ抽出アルゴリズムの効果をさらに向上、特に男性の低音部分で大きな改善
+- 2. 推論インターフェースレイアウトの最適化
+
+### 2023 年 8 月 13 日更新
+
+1-通常のバグ修正
+
+- 保存頻度と総ラウンド数の最小値を 1 に変更。総ラウンド数の最小値を 2 に変更
+- pretrain モデルなしでのトレーニングエラーを修正
+- 伴奏とボーカルの分離完了後の VRAM クリア
+- faiss 保存パスを絶対パスから相対パスに変更
+- パスに空白が含まれる場合のサポート(トレーニングセットのパス+実験名がサポートされ、エラーにならない)
+- filelist の強制的な utf8 エンコーディングをキャンセル
+- リアルタイム声変換中にインデックスを有効にすることによる CPU の大幅な使用問題を解決
+
+2-重要なアップデート
+
+- 現在最も強力なオープンソースの人間の声のピッチ抽出モデル RMVPE をトレーニングし、RVC のトレーニング、オフライン/リアルタイム推論に使用。pytorch/onnx/DirectML をサポート
+- pytorch-dml を通じて A カードと I カードのサポート
+ (1)リアルタイム声変換(2)推論(3)ボーカルと伴奏の分離(4)トレーニングはまだサポートされておらず、CPU でのトレーニングに切り替わります。onnx_dml を通じて rmvpe_gpu の推論をサポート
+
+### 2023 年 6 月 18 日更新
+
+- v2 に 32k と 48k の 2 つの新しい事前トレーニングモデルを追加
+- 非 f0 モデルの推論エラーを修正
+- 1 時間を超えるトレーニングセットのインデックス構築フェーズでは、自動的に kmeans で特徴を縮小し、インデックスのトレーニングを加速し、検索に追加
+- 人間の声をギターに変換するおもちゃのリポジトリを添付
+- データ処理で異常値スライスを除外
+- onnx エクスポートオプションタブ
+
+失敗した実験:
+
+- ~~特徴検索に時間次元を追加:ダメ、効果がない~~
+- ~~特徴検索に PCAR 次元削減オプションを追加:ダメ、大きなデータは kmeans でデータ量を減らし、小さいデータは次元削減の時間が節約するマッチングの時間よりも長い~~
+- ~~onnx 推論のサポート(推論のみの小さな圧縮パッケージ付き):ダメ、nsf の生成には pytorch が必要~~
+- ~~トレーニング中に音声、ジェンダー、eq、ノイズなどで入力をランダムに増強:ダメ、効果がない~~
+- ~~小型声码器の接続調査:ダメ、効果が悪化~~
+
+todolist:
+
+- ~~トレーニングセットの音声ピッチ認識に crepe をサポート:既に RMVPE に置き換えられているため不要~~
+- ~~多プロセス harvest 推論:既に RMVPE に置き換えられているため不要~~
+- ~~crepe の精度サポートと RVC-config の同期:既に RMVPE に置き換えられているため不要。これをサポートするには torchcrepe ライブラリも同期する必要があり、面倒~~
+- F0 エディタとの連携
+
+### 2023 年 5 月 28 日更新
+
+- v2 の jupyter notebook を追加、韓国語の changelog を追加、いくつかの環境依存関係を追加
+- 呼吸、清辅音、歯音の保護モードを追加
+- crepe-full 推論をサポート
+- UVR5 人間の声と伴奏の分離に 3 つの遅延除去モデルと MDX-Net の混响除去モデルを追加、HP3 人声抽出モデルを追加
+- インデックス名にバージョンと実験名を追加
+- 人間の声と伴奏の分離、推論のバッチエクスポートにオーディオエクスポートフォーマットオプションを追加
+- 32k モデルのトレーニングを廃止
+
+### 2023 年 5 月 13 日更新
+
+- ワンクリックパッケージ内の古いバージョンの runtime 内の lib.infer_pack と uvr5_pack の残骸をクリア
+- トレーニングセットの事前処理の擬似マルチプロセスバグを修正
+- harvest による音声ピッチ認識で無声音現象を弱めるために中間値フィルターを追加、中間値フィルターの半径を調整可能
+- 音声エクスポートにポストプロセスリサンプリングを追加
+- トレーニング時の n_cpu プロセス数を「F0 抽出のみ調整」から「データ事前処理と F0 抽出の調整」に変更
+- logs フォルダ下の index パスを自動検出し、ドロップダウンリスト機能を提供
+- タブページに「よくある質問」を追加(または github-rvc-wiki を参照)
+- 同じパスの入力音声推論に音声ピッチキャッシュを追加(用途:harvest 音声ピッチ抽出を使用すると、全体のパイプラインが長く繰り返される音声ピッチ抽出プロセスを経験し、キャッシュを使用しない場合、異なる音色、インデックス、音声ピッチ中間値フィルター半径パラメーターをテストするユーザーは、最初のテスト後の待機結果が非常に苦痛になります)
+
+### 2023 年 5 月 14 日更新
+
+- 音量エンベロープのアライメント入力ミックス(「入力が無音で出力がわずかなノイズ」の問題を緩和することができます。入力音声の背景ノイズが大きい場合は、オンにしないことをお勧めします。デフォルトではオフ(1 として扱われる))
+- 指定された頻度で抽出された小型モデルを保存する機能をサポート(異なるエポックでの推論効果を試したいが、すべての大きなチェックポイントを保存して手動で小型モデルを抽出するのが面倒な場合、この機能は非常に便利です)
+- システム全体のプロキシが開かれている場合にブラウザの接続エラーが発生する問題を環境変数の設定で解決
+- v2 事前訓練モデルをサポート(現在、テストのために 40k バージョンのみが公開されており、他の 2 つのサンプリングレートはまだ完全に訓練されていません)
+- 推論前に 1 を超える過大な音量を制限
+- データ事前処理パラメーターを微調整
+
+### 2023 年 4 月 9 日更新
+
+- トレーニングパラメーターを修正し、GPU の平均利用率を向上させる。A100 は最高 25%から約 90%に、V100 は 50%から約 90%に、2060S は 60%から約 85%に、P40 は 25%から約 95%に向上し、トレーニング速度が大幅に向上
+- パラメーターを修正:全体の batch_size を各カードの batch_size に変更
+- total_epoch を修正:最大制限 100 から 1000 に解除; デフォルト 10 からデフォルト 20 に引き上げ
+- ckpt 抽出時に音声ピッチの有無を誤って認識し、推論が異常になる問題を修正
+- 分散トレーニングで各ランクが ckpt を 1 回ずつ保存する問題を修正
+- 特徴抽出で nan 特徴をフィルタリング
+- 入力が無音で出力がランダムな子音またはノイズになる問題を修正(旧バージョンのモデルはトレーニングセットを作り直して再トレーニングする必要があります)
+
+### 2023 年 4 月 16 日更新
+
+- ローカルリアルタイム音声変換ミニ GUI を新設、go-realtime_gui.bat をダブルクリックで起動
+- トレーニングと推論で 50Hz 以下の周波数帯をフィルタリング
+- トレーニングと推論の音声ピッチ抽出 pyworld の最低音声ピッチをデフォルトの 80 から 50 に下げ、50-80hz の男性低音声が無声にならないように
+- WebUI がシステムの地域に基づいて言語を変更する機能をサポート(現在サポートされているのは en_US、ja_JP、zh_CN、zh_HK、zh_SG、zh_TW、サポートされていない場合はデフォルトで en_US になります)
+- 一部のグラフィックカードの認識を修正(例えば V100-16G の認識失敗、P4 の認識失敗)
+
+### 2023 年 4 月 28 日更新
+
+- faiss インデックス設定をアップグレードし、速度が速く、品質が高くなりました
+- total_npy 依存をキャンセルし、今後のモデル共有では total_npy の記入は不要
+- 16 シリーズの制限を解除。4G メモリ GPU に 4G の推論設定を提供
+- 一部のオーディオ形式で UVR5 の人声伴奏分離のバグを修正
+- リアルタイム音声変換ミニ gui に 40k 以外のモデルと妥協のない音声ピッチモデルのサポートを追加
+
+### 今後の計画:
+
+機能:
+
+- 複数人のトレーニングタブのサポート(最大 4 人)
+
+底層モデル:
+
+- 呼吸 wav をトレーニングセットに追加し、呼吸が音声変換の電子音の問題を修正
+- 歌声トレーニングセットを追加した底層モデルをトレーニングしており、将来的には公開する予定です
diff --git a/docs/jp/README.ja.md b/docs/jp/README.ja.md
new file mode 100644
index 0000000..715e503
--- /dev/null
+++ b/docs/jp/README.ja.md
@@ -0,0 +1,245 @@
+
+
+
Retrieval-based-Voice-Conversion-WebUI
+VITSに基づく使いやすい音声変換(voice changer)framework
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+[**更新日誌**](./Changelog_JA.md) | [**よくある質問**](./faq_ja.md) | [**AutoDL·5 円で AI 歌手をトレーニング**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/Autodl%E8%AE%AD%E7%BB%83RVC%C2%B7AI%E6%AD%8C%E6%89%8B%E6%95%99%E7%A8%8B) | [**対照実験記録**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/%E5%AF%B9%E7%85%A7%E5%AE%9E%E9%AA%8C%C2%B7%E5%AE%9E%E9%AA%8C%E8%AE%B0%E5%BD%95) | [**オンラインデモ**](https://modelscope.cn/studios/FlowerCry/RVCv2demo)
+
+[**English**](../en/README.en.md) | [**中文简体**](../../README.md) | [**日本語**](../jp/README.ja.md) | [**한국어**](../kr/README.ko.md) ([**韓國語**](../kr/README.ko.han.md)) | [**Français**](../fr/README.fr.md) | [**Türkçe**](../tr/README.tr.md) | [**Português**](../pt/README.pt.md)
+
+
+
+> デモ動画は[こちら](https://www.bilibili.com/video/BV1pm4y1z7Gm/)でご覧ください。
+
+> RVC によるリアルタイム音声変換: [w-okada/voice-changer](https://github.com/w-okada/voice-changer)
+
+> 著作権侵害を心配することなく使用できるように、基底モデルは約 50 時間の高品質なオープンソースデータセットで訓練されています。
+
+> RVCv3 の基底モデルルをご期待ください。より大きなパラメータ、より大きなデータ、より良い効果を提供し、基本的に同様の推論速度を維持しながら、トレーニングに必要なデータ量はより少なくなります。
+
+
+
+ | トレーニングと推論インターフェース |
+ リアルタイム音声変換インターフェース |
+
+
+  |
+  |
+
+
+ | go-webui.bat |
+ go-realtime_gui.bat |
+
+
+ | 実行したい操作を自由に選択できます。 |
+ 既に端から端までの170msの遅延を実現しました。ASIO入出力デバイスを使用すれば、端から端までの90msの遅延を達成できますが、ハードウェアドライバーのサポートに非常に依存しています。 |
+
+
+
+## はじめに
+
+本リポジトリには下記の特徴があります。
+
+- Top1 検索を用いることで、生の特徴量を訓練用データセット特徴量に変換し、トーンリーケージを削減します。
+- 比較的貧弱な GPU でも、高速かつ簡単に訓練できます。
+- 少量のデータセットからでも、比較的良い結果を得ることができます。(10 分以上のノイズの少ない音声を推奨します。)
+- モデルを融合することで、音声を混ぜることができます。(ckpt processing タブの、ckpt merge を使用します。)
+- 使いやすい WebUI。
+- UVR5 Model も含んでいるため、人の声と BGM を素早く分離できます。
+- 最先端の[人間の声のピッチ抽出アルゴリズム InterSpeech2023-RMVPE](#参照プロジェクト)を使用して無声音問題を解決します。効果は最高(著しく)で、crepe_full よりも速く、リソース使用が少ないです。
+- A カードと I カードの加速サポート
+
+私たちの[デモビデオ](https://www.bilibili.com/video/BV1pm4y1z7Gm/)をチェックしてください!
+
+## 環境構築
+
+下記のコマンドは、Python3.8 以上の環境で実行する必要があります:
+
+### Windows/Linux/MacOS などのプラットフォーム共通方法
+
+以下の方法のいずれかを選択してください。
+
+#### 1. pip を通じた依存関係のインストール
+
+1. Pytorch 及びその主要な依存関係のインストール、すでにインストールされている場合はスキップ。参照:https://pytorch.org/get-started/locally/
+
+```bash
+pip install torch torchvision torchaudio
+```
+
+2. win システム + Nvidia Ampere アーキテクチャ(RTX30xx)の場合、#21 の経験に基づいて pytorch に対応する cuda バージョンを指定
+
+```bash
+pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+```
+
+3. 自分のグラフィックカードに合わせた依存関係のインストール
+
+- N カード
+
+```bash
+pip install -r requirements.txt
+```
+
+- A カード/I カード
+
+```bash
+pip install -r requirements-dml.txt
+```
+
+- A カード ROCM(Linux)
+
+```bash
+pip install -r requirements-amd.txt
+```
+
+#### 2. poetry を通じた依存関係のインストール
+
+Poetry 依存関係管理ツールのインストール、すでにインストールされている場合はスキップ。参照:https://python-poetry.org/docs/#installation
+
+```bash
+curl -sSL https://install.python-poetry.org | python3 -
+```
+
+poetry を使って依存関係をインストール
+
+```bash
+poetry install
+```
+
+### MacOS
+
+`run.sh`を使って依存関係をインストールできます
+
+```bash
+sh ./run.sh
+```
+
+## その他の事前訓練されたモデルの準備
+
+RVC は推論とトレーニングのために他のいくつかの事前訓練されたモデルが必要です。
+
+これらのモデルは私たちの[Hugging Face space](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)でダウンロードできます。
+
+### 1. assets のダウンロード
+
+以下は、RVC に必要なすべての事前学習モデルとその他のファイルのリストです。`tools`フォルダーにこれらをダウンロードするスクリプトがあります。
+
+- ./assets/hubert_base
+
+- ./assets/pretrained
+
+- ./assets/uvr5_weights
+
+v2 バージョンのモデルを使用する場合、追加で以下をダウンロードする必要があります。
+
+- ./assets/pretrained_v2
+
+### 2. ffmpeg のインストール
+
+ffmpeg と ffprobe が既にインストールされている場合はスキップします。
+
+#### Ubuntu/Debian ユーザー
+
+```bash
+sudo apt install ffmpeg
+```
+
+#### MacOS ユーザー
+
+```bash
+brew install ffmpeg
+```
+
+#### Windows ユーザー
+
+ダウンロード後、ルートディレクトリに配置してください。
+
+- [ffmpeg.exe をダウンロード](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffmpeg.exe)
+
+- [ffprobe.exe をダウンロード](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffprobe.exe)
+
+### 3. RMVPE 人間の声のピッチ抽出アルゴリズムに必要なファイルのダウンロード
+
+最新の RMVPE 人間の声のピッチ抽出アルゴリズムを使用する場合、ピッチ抽出モデルのパラメータをダウンロードして RVC のルートディレクトリに配置する必要があります。
+
+- [rmvpe.pt をダウンロード](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.pt)
+
+#### dml 環境の RMVPE をダウンロード(オプション、A カード/I カードユーザー)
+
+- [rmvpe.onnx をダウンロード](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.onnx)
+
+### 4. AMD グラフィックカード Rocm(オプション、Linux のみ)
+
+Linux システムで AMD の Rocm 技術をベースに RVC を実行したい場合、[こちら](https://rocm.docs.amd.com/en/latest/deploy/linux/os-native/install.html)で必要なドライバーを先にインストールしてください。
+
+Arch Linux を使用している場合、pacman を使用して必要なドライバーをインストールできます。
+
+```
+pacman -S rocm-hip-sdk rocm-opencl-sdk
+```
+
+一部のモデルのグラフィックカード(例:RX6700XT)の場合、以下のような環境変数を追加で設定する必要があるかもしれません。
+
+```
+export ROCM_PATH=/opt/rocm
+export HSA_OVERRIDE_GFX_VERSION=10.3.0
+```
+
+同時に、現在のユーザーが`render`および`video`ユーザーグループに属していることを確認してください。
+
+```
+sudo usermod -aG render $USERNAME
+sudo usermod -aG video $USERNAME
+```
+
+## 使用開始
+
+### 直接起動
+
+以下のコマンドで WebUI を起動します
+'''bash
+python webui.py
+'''
+
+### 統合パッケージの使用
+
+`RVC-beta.7z`をダウンロードして解凍
+
+#### Windows ユーザー
+
+`go-webui.bat`をダブルクリック
+
+#### MacOS ユーザー
+
+'''bash
+sh ./run.sh
+'''
+
+## 参考プロジェクト
+
+- [ContentVec](https://github.com/auspicious3000/contentvec/)
+- [VITS](https://github.com/jaywalnut310/vits)
+- [HIFIGAN](https://github.com/jik876/hifi-gan)
+- [Gradio](https://github.com/gradio-app/gradio)
+- [FFmpeg](https://github.com/FFmpeg/FFmpeg)
+- [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
+- [audio-slicer](https://github.com/openvpi/audio-slicer)
+- [Vocal pitch extraction:RMVPE](https://github.com/Dream-High/RMVPE)
+ - 事前訓練されたモデルは[yxlllc](https://github.com/yxlllc/RMVPE)と[RVC-Boss](https://github.com/RVC-Boss)によって訓練され、テストされました。
+
+## すべての貢献者の努力に感謝します
+
+
+
+
diff --git a/docs/jp/faiss_tips_ja.md b/docs/jp/faiss_tips_ja.md
new file mode 100644
index 0000000..89cf5ba
--- /dev/null
+++ b/docs/jp/faiss_tips_ja.md
@@ -0,0 +1,101 @@
+faiss tuning TIPS
+==================
+# about faiss
+faissはfacebook researchの開発する、密なベクトルに対する近傍探索をまとめたライブラリで、多くの近似近傍探索の手法を効率的に実装しています。
+近似近傍探索はある程度精度を犠牲にしながら高速に類似するベクトルを探します。
+
+## faiss in RVC
+RVCではHuBERTで変換した特徴量のEmbeddingに対し、学習データから生成されたEmbeddingと類似するものを検索し、混ぜることでより元の音声に近い変換を実現しています。ただ、この検索は愚直に行うと時間がかかるため、近似近傍探索を用いることで高速な変換を実現しています。
+
+# 実装のoverview
+モデルが配置されている '/logs/your-experiment/3_feature256'には各音声データからHuBERTで抽出された特徴量が配置されています。
+ここからnpyファイルをファイル名でソートした順番で読み込み、ベクトルを連結してbig_npyを作成しfaissを学習させます。(このベクトルのshapeは[N, 256]です。)
+
+本Tipsではまずこれらのパラメータの意味を解説します。
+
+# 手法の解説
+## index factory
+index factoryは複数の近似近傍探索の手法を繋げるパイプラインをstringで表記するfaiss独自の記法です。
+これにより、index factoryの文字列を変更するだけで様々な近似近傍探索の手法を試せます。
+RVCでは以下のように使われています。
+
+```python
+index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)
+```
+index_factoryの引数のうち、1つ目はベクトルの次元数、2つ目はindex factoryの文字列で、3つ目には用いる距離を指定することができます。
+
+より詳細な記法については
+https://github.com/facebookresearch/faiss/wiki/The-index-factory
+
+## 距離指標
+embeddingの類似度として用いられる代表的な指標として以下の二つがあります。
+
+- ユークリッド距離(METRIC_L2)
+- 内積(METRIC_INNER_PRODUCT)
+
+ユークリッド距離では各次元において二乗の差をとり、全次元の差を足してから平方根をとります。これは日常的に用いる2次元、3次元での距離と同じです。
+内積はこのままでは類似度の指標として用いず、一般的にはL2ノルムで正規化してから内積をとるコサイン類似度を用います。
+
+どちらがよいかは場合によりますが、word2vec等で得られるembeddingやArcFace等で学習した類似画像検索のモデルではコサイン類似度が用いられることが多いです。ベクトルXに対してl2正規化をnumpyで行う場合は、0 divisionを避けるために十分に小さな値をepsとして以下のコードで可能です。
+
+```python
+X_normed = X / np.maximum(eps, np.linalg.norm(X, ord=2, axis=-1, keepdims=True))
+```
+
+また、index factoryには第3引数に渡す値を選ぶことで計算に用いる距離指標を変更できます。
+
+```python
+index = faiss.index_factory(dimention, text, faiss.METRIC_INNER_PRODUCT)
+```
+
+## IVF
+IVF(Inverted file indexes)は全文検索における転置インデックスと似たようなアルゴリズムです。
+学習時には検索対象に対してkmeansでクラスタリングを行い、クラスタ中心を用いてボロノイ分割を行います。各データ点には一つずつクラスタが割り当てられるので、クラスタからデータ点を逆引きする辞書を作成します。
+
+例えば以下のようにクラスタが割り当てられた場合
+|index|クラスタ|
+|-----|-------|
+|1|A|
+|2|B|
+|3|A|
+|4|C|
+|5|B|
+
+作成される転置インデックスは以下のようになります。
+
+|クラスタ|index|
+|-------|-----|
+|A|1, 3|
+|B|2, 5|
+|C|4|
+
+検索時にはまずクラスタからn_probe個のクラスタを検索し、次にそれぞれのクラスタに属するデータ点について距離を計算します。
+
+# 推奨されるパラメータ
+indexの選び方については公式にガイドラインがあるので、それに準じて説明します。
+https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index
+
+1M以下のデータセットにおいては4bit-PQが2023年4月時点ではfaissで利用できる最も効率的な手法です。
+これをIVFと組み合わせ、4bit-PQで候補を絞り、最後に正確な指標で距離を再計算するには以下のindex factoryを用いることで記載できます。
+
+```python
+index = faiss.index_factory(256, "IVF1024,PQ128x4fs,RFlat")
+```
+
+## IVFの推奨パラメータ
+IVFの数が多すぎる場合、たとえばデータ数の数だけIVFによる粗量子化を行うと、これは愚直な全探索と同じになり効率が悪いです。
+1M以下の場合ではIVFの値はデータ点の数Nに対して4*sqrt(N) ~ 16*sqrt(N)に推奨しています。
+
+n_probeはn_probeの数に比例して計算時間が増えるので、精度と相談して適切に選んでください。個人的にはRVCにおいてそこまで精度は必要ないと思うのでn_probe = 1で良いと思います。
+
+## FastScan
+FastScanは直積量子化で大まかに距離を近似するのを、レジスタ内で行うことにより高速に行うようにした手法です。
+直積量子化は学習時にd次元ごと(通常はd=2)に独立してクラスタリングを行い、クラスタ同士の距離を事前計算してlookup tableを作成します。予測時はlookup tableを見ることで各次元の距離をO(1)で計算できます。
+そのため、PQの次に指定する数字は通常ベクトルの半分の次元を指定します。
+
+FastScanに関するより詳細な説明は公式のドキュメントを参照してください。
+https://github.com/facebookresearch/faiss/wiki/Fast-accumulation-of-PQ-and-AQ-codes-(FastScan)
+
+## RFlat
+RFlatはFastScanで計算した大まかな距離を、index factoryの第三引数で指定した正確な距離で再計算する指示です。
+k個の近傍を取得する際は、k*k_factor個の点について再計算が行われます。
diff --git a/docs/jp/faq_ja.md b/docs/jp/faq_ja.md
new file mode 100644
index 0000000..85598ff
--- /dev/null
+++ b/docs/jp/faq_ja.md
@@ -0,0 +1,122 @@
+## Q1: ffmpeg error/utf8 error
+
+大体の場合、ffmpeg の問題ではなく、音声パスの問題です。
+ffmpeg は空白や()などの特殊文字を含むパスを読み込む際に ffmpeg error が発生する可能性があります。トレーニングセットの音声が中国語のパスを含む場合、filelist.txt に書き込む際に utf8 error が発生する可能性があります。
+
+## Q2: ワンクリックトレーニングが終わってもインデックスがない
+
+"Training is done. The program is closed."と表示された場合、モデルトレーニングは成功しています。その直後のエラーは誤りです。
+
+ワンクリックトレーニングが終了しても added で始まるインデックスファイルがない場合、トレーニングセットが大きすぎてインデックス追加のステップが停止している可能性があります。バッチ処理 add インデックスでメモリの要求が高すぎる問題を解決しました。一時的に「トレーニングインデックス」ボタンをもう一度クリックしてみてください。
+
+## Q3: トレーニングが終了してもトレーニングセットの音色が見えない
+
+音色をリフレッシュしてもう一度確認してください。それでも見えない場合は、トレーニングにエラーがなかったか、コンソールと WebUI のスクリーンショット、logs/実験名の下のログを開発者に送って確認してみてください。
+
+## Q4: モデルをどのように共有するか
+
+rvc_root/logs/実験名の下に保存されている pth は、推論に使用するために共有するためのものではなく、実験の状態を保存して再現およびトレーニングを続けるためのものです。共有するためのモデルは、weights フォルダの下にある 60MB 以上の pth ファイルです。
+ 今後、weights/exp_name.pth と logs/exp_name/added_xxx.index を組み合わせて weights/exp_name.zip にパッケージ化し、インデックスの記入ステップを省略します。その場合、zip ファイルを共有し、pth ファイルは共有しないでください。別のマシンでトレーニングを続ける場合を除きます。
+ logs フォルダの数百 MB の pth ファイルを weights フォルダにコピー/共有して推論に強制的に使用すると、f0、tgt_sr などのさまざまなキーが存在しないというエラーが発生する可能性があります。ckpt タブの一番下で、音高、目標オーディオサンプリングレートを手動または自動(ローカルの logs に関連情報が見つかる場合は自動的に)で選択してから、ckpt の小型モデルを抽出する必要があります(入力パスに G で始まるものを記入)。抽出が完了すると、weights フォルダに 60MB 以上の pth ファイルが表示され、音色をリフレッシュした後に使用できます。
+
+## Q5: Connection Error
+
+コンソール(黒いウィンドウ)を閉じた可能性があります。
+
+## Q6: WebUI が Expecting value: line 1 column 1 (char 0)と表示する
+
+システムのローカルネットワークプロキシ/グローバルプロキシを閉じてください。
+
+これはクライアントのプロキシだけでなく、サーバー側のプロキシも含まれます(例えば autodl で http_proxy と https_proxy を設定して学術的な加速を行っている場合、使用する際には unset でオフにする必要があります)。
+
+## Q7: WebUI を使わずにコマンドでトレーニングや推論を行うには
+
+トレーニングスクリプト:
+まず WebUI を実行し、メッセージウィンドウにデータセット処理とトレーニング用のコマンドラインが表示されます。
+
+推論スクリプト:
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/myinfer.py
+
+例:
+
+runtime\python.exe myinfer.py 0 "E:\codes\py39\RVC-beta\todo-songs\1111.wav" "E:\codes\py39\logs\mi-test\added_IVF677_Flat_nprobe_7.index" harvest "test.wav" "weights/mi-test.pth" 0.6 cuda:0 True
+
+f0up_key=sys.argv[1]
+input_path=sys.argv[2]
+index_path=sys.argv[3]
+f0method=sys.argv[4]#harvest or pm
+opt_path=sys.argv[5]
+model_path=sys.argv[6]
+index_rate=float(sys.argv[7])
+device=sys.argv[8]
+is_half=bool(sys.argv[9])
+
+## Q8: Cuda error/Cuda out of memory
+
+まれに cuda の設定問題やデバイスがサポートされていない可能性がありますが、大半はメモリ不足(out of memory)が原因です。
+
+トレーニングの場合は batch size を小さくします(1 にしても足りない場合はグラフィックカードを変更するしかありません)。推論の場合は、config.py の末尾にある x_pad、x_query、x_center、x_max を適宜小さくします。4GB 以下のメモリ(例えば 1060(3G)や各種 2GB のグラフィックカード)は諦めることをお勧めしますが、4GB のメモリのグラフィックカードはまだ救いがあります。
+
+## Q9: total_epoch はどのくらいに設定するのが良いですか
+
+トレーニングセットの音質が悪く、ノイズが多い場合は、20〜30 で十分です。高すぎると、ベースモデルの音質が低音質のトレーニングセットを高めることができません。
+トレーニングセットの音質が高く、ノイズが少なく、長い場合は、高く設定できます。200 は問題ありません(トレーニング速度が速いので、高音質のトレーニングセットを準備できる条件がある場合、グラフィックカードも条件が良いはずなので、少しトレーニング時間が長くなることを気にすることはありません)。
+
+## Q10: トレーニングセットはどれくらいの長さが必要ですか
+
+10 分から 50 分を推奨します。
+ 音質が良く、バックグラウンドノイズが低い場合、個人的な特徴のある音色であれば、多ければ多いほど良いです。
+ 高品質のトレーニングセット(精巧に準備された + 特徴的な音色)であれば、5 分から 10 分でも大丈夫です。リポジトリの作者もよくこの方法で遊びます。
+ 1 分から 2 分のデータでトレーニングに成功した人もいますが、その成功体験は他人には再現できないため、あまり参考になりません。トレーニングセットの音色が非常に特徴的である必要があります(例:高い周波数の透明な声や少女の声など)、そして音質が良い必要があります。
+ 1 分未満のデータでトレーニングを試みた(成功した)ケースはまだ見たことがありません。このような試みはお勧めしません。
+
+## Q11: index rate は何に使うもので、どのように調整するのか(啓蒙)
+
+もしベースモデルや推論ソースの音質がトレーニングセットよりも高い場合、推論結果の音質を向上させることができますが、音色がベースモデル/推論ソースの音色に近づくことがあります。これを「音色漏れ」と言います。
+ index rate は音色漏れの問題を減少させたり解決するために使用されます。1 に設定すると、理論的には推論ソースの音色漏れの問題は存在しませんが、音質はトレーニングセットに近づきます。トレーニングセットの音質が推論ソースよりも低い場合、index rate を高くすると音質が低下する可能性があります。0 に設定すると、検索ミックスを利用してトレーニングセットの音色を保護する効果はありません。
+ トレーニングセットが高品質で長い場合、total_epoch を高く設定することができ、この場合、モデル自体は推論ソースやベースモデルの音色をあまり参照しないため、「音色漏れ」の問題はほとんど発生しません。この時、index rate は重要ではなく、インデックスファイルを作成したり共有したりする必要もありません。
+
+## Q11: 推論時に GPU をどのように選択するか
+
+config.py ファイルの device cuda:の後にカード番号を選択します。
+カード番号とグラフィックカードのマッピング関係は、トレーニングタブのグラフィックカード情報欄で確認できます。
+
+## Q12: トレーニング中に保存された pth ファイルをどのように推論するか
+
+ckpt タブの一番下で小型モデルを抽出します。
+
+## Q13: トレーニングをどのように中断し、続行するか
+
+現在の段階では、WebUI コンソールを閉じて go-webui.bat をダブルクリックしてプログラムを再起動するしかありません。ウェブページのパラメータもリフレッシュして再度入力する必要があります。
+トレーニングを続けるには:同じウェブページのパラメータでトレーニングモデルをクリックすると、前回のチェックポイントからトレーニングを続けます。
+
+## Q14: トレーニング中にファイルページ/メモリエラーが発生した場合の対処法
+
+プロセスが多すぎてメモリがオーバーフローしました。以下の方法で解決できるかもしれません。
+
+1. 「音高抽出とデータ処理に使用する CPU プロセス数」を適宜下げます。
+2. トレーニングセットのオーディオを手動でカットして、あまり長くならないようにします。
+
+## Q15: 途中でデータを追加してトレーニングする方法
+
+1. 全データに新しい実験名を作成します。
+2. 前回の最新の G と D ファイル(あるいはどの中間 ckpt を基にトレーニングしたい場合は、その中間のものをコピーすることもできます)を新しい実験名にコピーします。
+3. 新しい実験名でワンクリックトレーニングを開始すると、前回の最新の進捗からトレーニングを続けます。
+
+## Q16: llvmlite.dll に関するエラー
+
+```bash
+OSError: Could not load shared object file: llvmlite.dll
+
+FileNotFoundError: Could not find module lib\site-packages\llvmlite\binding\llvmlite.dll (or one of its dependencies). Try using the full path with constructor syntax.
+```
+
+Windows プラットフォームではこのエラーが発生しますが、https://aka.ms/vs/17/release/vc_redist.x64.exeをインストールしてWebUIを再起動すれば解決します。
+
+## Q17: RuntimeError: テンソルの拡張サイズ(17280)は、非シングルトン次元 1 での既存サイズ(0)と一致する必要があります。 ターゲットサイズ:[1, 17280]。 テンソルサイズ:[0]
+
+wavs16k フォルダーの下で、他のファイルよりも明らかに小さいいくつかのオーディオファイルを見つけて削除し、トレーニングモデルをクリックすればエラーは発生しませんが、ワンクリックプロセスが中断されたため、モデルのトレーニングが完了したらインデックスのトレーニングをクリックする必要があります。
+
+## Q18: RuntimeError: テンソル a のサイズ(24)は、非シングルトン次元 2 でテンソル b(16)のサイズと一致する必要があります
+
+トレーニング中にサンプリングレートを変更してはいけません。変更する必要がある場合は、実験名を変更して最初からトレーニングする必要があります。もちろん、前回抽出した音高と特徴(0/1/2/2b フォルダ)をコピーしてトレーニングプロセスを加速することもできます。
diff --git a/docs/jp/training_tips_ja.md b/docs/jp/training_tips_ja.md
new file mode 100644
index 0000000..c5b06f2
--- /dev/null
+++ b/docs/jp/training_tips_ja.md
@@ -0,0 +1,64 @@
+RVCの訓練における説明、およびTIPS
+===============================
+本TIPSではどのようにデータの訓練が行われているかを説明します。
+
+# 訓練の流れ
+GUIの訓練タブのstepに沿って説明します。
+
+## step1
+実験名の設定を行います。
+
+また、モデルに音高ガイド(ピッチ)を考慮させるかもここで設定できます。考慮させない場合はモデルは軽量になりますが、歌唱には向かなくなります。
+
+各実験のデータは`/logs/実験名/`に配置されます。
+
+## step2a
+音声の読み込みと前処理を行います。
+
+### load audio
+音声のあるフォルダを指定すると、そのフォルダ内にある音声ファイルを自動で読み込みます。
+例えば`C:Users\hoge\voices`を指定した場合、`C:Users\hoge\voices\voice.mp3`は読み込まれますが、`C:Users\hoge\voices\dir\voice.mp3`は読み込まれません。
+
+音声の読み込みには内部でffmpegを利用しているので、ffmpegで対応している拡張子であれば自動的に読み込まれます。
+ffmpegでint16に変換した後、float32に変換し、-1 ~ 1の間に正規化されます。
+
+### denoising
+音声についてscipyのfiltfiltによる平滑化を行います。
+
+### 音声の分割
+入力した音声はまず、一定期間(max_sil_kept=5秒?)より長く無音が続く部分を検知して音声を分割します。無音で音声を分割した後は、0.3秒のoverlapを含む4秒ごとに音声を分割します。4秒以内に区切られた音声は、音量の正規化を行った後wavファイルを`/logs/実験名/0_gt_wavs`に、そこから16kのサンプリングレートに変換して`/logs/実験名/1_16k_wavs`にwavファイルで保存します。
+
+## step2b
+### ピッチの抽出
+wavファイルからピッチ(音の高低)の情報を抽出します。parselmouthやpyworldに内蔵されている手法でピッチ情報(=f0)を抽出し、`/logs/実験名/2a_f0`に保存します。その後、ピッチ情報を対数で変換して1~255の整数に変換し、`/logs/実験名/2b-f0nsf`に保存します。
+
+### feature_printの抽出
+HuBERTを用いてwavファイルを事前にembeddingに変換します。`/logs/実験名/1_16k_wavs`に保存したwavファイルを読み込み、HuBERTでwavファイルを256次元の特徴量に変換し、npy形式で`/logs/実験名/3_feature256`に保存します。
+
+## step3
+モデルのトレーニングを行います。
+### 初心者向け用語解説
+深層学習ではデータセットを分割し、少しずつ学習を進めていきます。一回のモデルの更新(step)では、batch_size個のデータを取り出し予測と誤差の修正を行います。これをデータセットに対して一通り行うと一epochと数えます。
+
+そのため、学習時間は 1step当たりの学習時間 x (データセット内のデータ数 ÷ バッチサイズ) x epoch数 かかります。一般にバッチサイズを大きくするほど学習は安定し、(1step当たりの学習時間÷バッチサイズ)は小さくなりますが、その分GPUのメモリを多く使用します。GPUのRAMはnvidia-smiコマンド等で確認できます。実行環境のマシンに合わせてバッチサイズをできるだけ大きくするとより短時間で学習が可能です。
+
+### pretrained modelの指定
+RVCではモデルの訓練を0からではなく、事前学習済みの重みから開始するため、少ないデータセットで学習を行えます。
+
+デフォルトでは
+
+- 音高ガイドを考慮する場合、`RVCのある場所/pretrained/f0G40k.pth`と`RVCのある場所/pretrained/f0D40k.pth`を読み込みます。
+- 音高ガイドを考慮しない場合、`RVCのある場所/pretrained/G40k.pth`と`RVCのある場所/pretrained/D40k.pth`を読み込みます。
+
+学習時はsave_every_epochごとにモデルのパラメータが`logs/実験名/G_{}.pth`と`logs/実験名/D_{}.pth`に保存されますが、このパスを指定することで学習を再開したり、もしくは違う実験で学習したモデルの重みから学習を開始できます。
+
+### indexの学習
+RVCでは学習時に使われたHuBERTの特徴量を保存し、推論時は学習時の特徴量から近い特徴量を探してきて推論を行います。この検索を高速に行うために事前にindexの学習を行います。
+indexの学習には近似近傍探索ライブラリのfaissを用います。`/logs/実験名/3_feature256`の特徴量を読み込み、それを用いて学習したindexを`/logs/実験名/add_XXX.index`として保存します。
+(20230428updateよりtotal_fea.npyはindexから読み込むので不要になりました。)
+
+### ボタンの説明
+- モデルのトレーニング: step2bまでを実行した後、このボタンを押すとモデルの学習を行います。
+- 特徴インデックスのトレーニング: モデルのトレーニング後、indexの学習を行います。
+- ワンクリックトレーニング: step2bまでとモデルのトレーニング、特徴インデックスのトレーニングを一括で行います。
+
diff --git a/docs/kr/Changelog_KO.md b/docs/kr/Changelog_KO.md
new file mode 100644
index 0000000..e56ef7a
--- /dev/null
+++ b/docs/kr/Changelog_KO.md
@@ -0,0 +1,124 @@
+### 2023년 10월 6일 업데이트
+
+실시간 음성 변환을 위한 인터페이스인 go-realtime_gui.bat/realtime_gui.py를 제작했습니다(사실 이는 이미 존재했었습니다). 이번 업데이트는 주로 실시간 음성 변환 성능을 최적화하는 데 중점을 두었습니다. 0813 버전과 비교하여:
+
+- 1. 인터페이스 조작 최적화: 매개변수 핫 업데이트(매개변수 조정 시 중단 후 재시작 필요 없음), 모델 지연 로딩(이미 로드된 모델은 재로드 필요 없음), 음량 인자 매개변수 추가(음량을 입력 오디오에 가깝게 조정)
+- 2. 내장된 노이즈 감소 효과 및 속도 최적화
+- 3. 추론 속도 크게 향상
+
+입력 및 출력 장치는 동일한 유형을 선택해야 합니다. 예를 들어, 모두 MME 유형을 선택해야 합니다.
+
+1006 버전의 전체 업데이트는 다음과 같습니다:
+
+- 1. rmvpe 음성 피치 추출 알고리즘의 효과를 계속해서 향상, 특히 남성 저음역에 대한 개선이 큼
+- 2. 추론 인터페이스 레이아웃 최적화
+
+### 2023년 08월 13일 업데이트
+
+1-정기적인 버그 수정
+
+- 최소 총 에포크 수를 1로 변경하고, 최소 총 에포크 수를 2로 변경합니다.
+- 사전 훈련(pre-train) 모델을 사용하지 않는 훈련 오류 수정
+- 반주 보컬 분리 후 그래픽 메모리 지우기
+- 페이즈 저장 경로 절대 경로를 상대 경로로 변경
+- 공백이 포함된 경로 지원(훈련 세트 경로와 실험 이름 모두 지원되며 더 이상 오류가 보고되지 않음)
+- 파일 목록에서 필수 utf8 인코딩 취소
+- 실시간 음성 변경 중 faiss 검색으로 인한 CPU 소모 문제 해결
+
+2-키 업데이트
+
+- 현재 가장 강력한 오픈 소스 보컬 피치 추출 모델 RMVPE를 훈련하고, 이를 RVC 훈련, 오프라인/실시간 추론에 사용하며, PyTorch/Onx/DirectML을 지원합니다.
+- 파이토치\_DML을 통한 AMD 및 인텔 그래픽 카드 지원
+ (1) 실시간 음성 변화 (2) 추론 (3) 보컬 반주 분리 (4) 현재 지원되지 않는 훈련은 CPU 훈련으로 전환, Onnx_Dml을 통한 gpu의 RMVPE 추론 지원
+
+### 2023년 6월 18일 업데이트
+
+- v2 버전에서 새로운 32k와 48k 사전 학습 모델을 추가.
+- non-f0 모델들의 추론 오류 수정.
+- 학습 세트가 1시간을 넘어가는 경우, 인덱스 생성 단계에서 minibatch-kmeans을 사용해, 학습속도 가속화.
+- [huggingface](https://huggingface.co/spaces/lj1995/vocal2guitar)에서 vocal2guitar 제공.
+- 데이터 처리 단계에서 이상 값 자동으로 제거.
+- ONNX로 내보내는(export) 옵션 탭 추가.
+
+업데이트에 적용되지 않았지만 시도한 것들 :
+
+- ~~시계열 차원을 추가하여 특징 검색을 진행했지만, 유의미한 효과는 없었습니다.~~
+- ~~PCA 차원 축소를 추가하여 특징 검색을 진행했지만, 유의미한 효과는 없었습니다.~~
+- ~~ONNX 추론을 지원하는 것에 실패했습니다. nsf 생성시, Pytorch가 필요하기 때문입니다.~~
+- ~~훈련 중에 입력에 대한 음고, 성별, 이퀄라이저, 노이즈 등 무작위로 강화하는 것에, 유의미한 효과는 없었습니다.~~
+
+추후 업데이트 목록:
+
+- ~~Vocos-RVC (소형 보코더) 통합 예정.~~
+- ~~학습 단계에 음고 인식을 위한 Crepe 지원 예정.~~
+- ~~Crepe의 정밀도를 REC-config와 동기화하여 지원 예정.~~
+- FO 에디터 지원 예정.
+
+### 2023년 5월 28일 업데이트
+
+- v2 jupyter notebook 추가, 한국어 업데이트 로그 추가, 의존성 모듈 일부 수정.
+- 무성음 및 숨소리 보호 모드 추가.
+- crepe-full pitch 감지 지원.
+- UVR5 보컬 분리: 디버브 및 디-에코 모델 지원.
+- index 이름에 experiment 이름과 버전 추가.
+- 배치 음성 변환 처리 및 UVR5 보컬 분리 시, 사용자가 수동으로 출력 오디오의 내보내기(export) 형식을 선택할 수 있도록 지원.
+- 32k 훈련 모델 지원 종료.
+
+### 2023년 5월 13일 업데이트
+
+- 원클릭 패키지의 이전 버전 런타임 내, 불필요한 코드(lib.infer_pack 및 uvr5_pack) 제거.
+- 훈련 세트 전처리의 유사 다중 처리 버그 수정.
+- Harvest 피치 인식 알고리즘에 대한 중위수 필터링 반경 조정 추가.
+- 오디오 내보낼 때, 후처리 리샘플링 지원.
+- 훈련에 대한 다중 처리 "n_cpu" 설정이 "f0 추출"에서 "데이터 전처리 및 f0 추출"로 변경.
+- logs 폴더 하의 인덱스 경로를 자동으로 감지 및 드롭다운 목록 기능 제공.
+- 탭 페이지에 "자주 묻는 질문과 답변" 추가. (github RVC wiki 참조 가능)
+- 동일한 입력 오디오 경로를 사용할 때 추론, Harvest 피치를 캐시.
+ (주의: Harvest 피치 추출을 사용하면 전체 파이프라인은 길고 반복적인 피치 추출 과정을 거치게됩니다. 캐싱을 하지 않는다면, 첫 inference 이후의 단계에서 timbre, 인덱스, 피치 중위수 필터링 반경 설정 등 대기시간이 엄청나게 길어집니다!)
+
+### 2023년 5월 14일 업데이트
+
+- 입력의 볼륨 캡슐을 사용하여 출력의 볼륨 캡슐을 혼합하거나 대체. (입력이 무음이거나 출력의 노이즈 문제를 최소화 할 수 있습니다. 입력 오디오의 배경 노이즈(소음)가 큰 경우 해당 기능을 사용하지 않는 것이 좋습니다. 기본적으로 비활성화 되어있는 옵션입니다. (1: 비활성화 상태))
+- 추출된 소형 모델을 지정된 빈도로 저장하는 기능을 지원. (다양한 에폭 하에서의 성능을 보려고 하지만 모든 대형 체크포인트를 저장하고 매번 ckpt 처리를 통해 소형 모델을 수동으로 추출하고 싶지 않은 경우 이 기능은 매우 유용합니다)
+- 환경 변수를 설정하여 서버의 전역 프록시로 인한 "연결 오류" 문제 해결.
+- 사전 훈련된 v2 모델 지원. (현재 40k 버전만 테스트를 위해 공개적으로 사용 가능하며, 다른 두 개의 샘플링 비율은 아직 완전히 훈련되지 않아 보류되었습니다.)
+- 추론 전, 1을 초과하는 과도한 볼륨 제한.
+- 데이터 전처리 매개변수 미세 조정.
+
+### 2023년 4월 9일 업데이트
+
+- GPU 이용률 향상을 위해 훈련 파라미터 수정: A100은 25%에서 약 90%로 증가, V100: 50%에서 약 90%로 증가, 2060S: 60%에서 약 85%로 증가, P40: 25%에서 약 95%로 증가.
+ 훈련 속도가 크게 향상.
+- 매개변수 기준 변경: total batch_size는 GPU당 batch_size를 의미.
+- total_epoch 변경: 최대 한도가 100에서 1000으로 증가. 기본값이 10에서 20으로 증가.
+- ckpt 추출이 피치를 잘못 인식하여 비정상적인 추론을 유발하는 문제 수정.
+- 분산 훈련 과정에서 각 랭크마다 ckpt를 저장하는 문제 수정.
+- 특성 추출 과정에 나노 특성 필터링 적용.
+- 무음 입력/출력이 랜덤하게 소음을 생성하는 문제 수정. (이전 모델은 새 데이터셋으로 다시 훈련해야 합니다)
+
+### 2023년 4월 16일 업데이트
+
+- 로컬 실시간 음성 변경 미니-GUI 추가, go-realtime_gui.bat를 더블 클릭하여 시작.
+- 훈련 및 추론 중 50Hz 이하의 주파수 대역에 대해 필터링 적용.
+- 훈련 및 추론의 pyworld 최소 피치 추출을 기본 80에서 50으로 낮춤. 이로 인해, 50-80Hz 사이의 남성 저음이 무음화되지 않습니다.
+- 시스템 지역에 따른 WebUI 언어 변경 지원. (현재 en_US, ja_JP, zh_CN, zh_HK, zh_SG, zh_TW를 지원하며, 지원되지 않는 경우 기본값은 en_US)
+- 일부 GPU의 인식 수정. (예: V100-16G 인식 실패, P4 인식 실패)
+
+### 2023년 4월 28일 업데이트
+
+- Faiss 인덱스 설정 업그레이드로 속도가 더 빨라지고 품질이 향상.
+- total_npy에 대한 의존성 제거. 추후의 모델 공유는 total_npy 입력을 필요로 하지 않습니다.
+- 16 시리즈 GPU에 대한 제한 해제, 4GB VRAM GPU에 대한 4GB 추론 설정 제공.
+- 일부 오디오 형식에 대한 UVR5 보컬 동반 분리에서의 버그 수정.
+- 실시간 음성 변경 미니-GUI는 이제 non-40k 및 non-lazy 피치 모델을 지원합니다.
+
+### 추후 계획
+
+Features:
+
+- 다중 사용자 훈련 탭 지원.(최대 4명)
+
+Base model:
+
+- 훈련 데이터셋에 숨소리 wav 파일을 추가하여, 보컬의 호흡이 노이즈로 변환되는 문제 수정.
+- 보컬 훈련 세트의 기본 모델을 추가하기 위한 작업을 진행중이며, 이는 향후에 발표될 예정.
diff --git a/docs/kr/README.ko.han.md b/docs/kr/README.ko.han.md
new file mode 100644
index 0000000..507dbe4
--- /dev/null
+++ b/docs/kr/README.ko.han.md
@@ -0,0 +1,104 @@
+
+
+
Retrieval-based-Voice-Conversion-WebUI
+VITS基盤의 簡單하고使用하기 쉬운音聲變換틀
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+
+
+------
+[**更新日誌**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/docs/Changelog_KO.md)
+
+[**English**](../en/README.en.md) | [**中文简体**](../../README.md) | [**日本語**](../jp/README.ja.md) | [**한국어**](../kr/README.ko.md) ([**韓國語**](../kr/README.ko.han.md)) | [**Français**](../fr/README.fr.md) | [**Türkçe**](../tr/README.tr.md) | [**Português**](../pt/README.pt.md)
+
+> [示範映像](https://www.bilibili.com/video/BV1pm4y1z7Gm/)을 確認해 보세요!
+
+> RVC를活用한實時間音聲變換: [w-okada/voice-changer](https://github.com/w-okada/voice-changer)
+
+> 基本모델은 50時間假量의 高品質 오픈 소스 VCTK 데이터셋을 使用하였으므로, 著作權上의 念慮가 없으니 安心하고 使用하시기 바랍니다.
+
+> 著作權問題가 없는 高品質의 노래를 以後에도 繼續해서 訓練할 豫定입니다.
+
+## 紹介
+本Repo는 다음과 같은 特徵을 가지고 있습니다:
++ top1檢索을利用하여 入力音色特徵을 訓練세트音色特徵으로 代替하여 音色의漏出을 防止;
++ 相對的으로 낮은性能의 GPU에서도 빠른訓練可能;
++ 적은量의 데이터로 訓練해도 좋은 結果를 얻을 수 있음 (最小10分以上의 低雜음音聲데이터를 使用하는 것을 勸獎);
++ 모델融合을通한 音色의 變調可能 (ckpt處理탭->ckpt混合選擇);
++ 使用하기 쉬운 WebUI (웹 使用者인터페이스);
++ UVR5 모델을 利用하여 목소리와 背景音樂의 빠른 分離;
+
+## 環境의準備
+poetry를通해 依存를設置하는 것을 勸獎합니다.
+
+다음命令은 Python 버전3.8以上의環境에서 實行되어야 합니다:
+```bash
+# PyTorch 關聯主要依存設置, 이미設置되어 있는 境遇 건너뛰기 可能
+# 參照: https://pytorch.org/get-started/locally/
+pip install torch torchvision torchaudio
+
+# Windows + Nvidia Ampere Architecture(RTX30xx)를 使用하고 있다面, #21 에서 명시된 것과 같이 PyTorch에 맞는 CUDA 버전을 指定해야 합니다.
+#pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+
+# Poetry 設置, 이미設置되어 있는 境遇 건너뛰기 可能
+# Reference: https://python-poetry.org/docs/#installation
+curl -sSL https://install.python-poetry.org | python3 -
+
+# 依存設置
+poetry install
+```
+pip를 活用하여依存를 設置하여도 無妨합니다.
+
+```bash
+pip install -r requirements.txt
+```
+
+## 其他預備모델準備
+RVC 모델은 推論과訓練을 依하여 다른 預備모델이 必要합니다.
+
+[Huggingface space](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)를 通해서 다운로드 할 수 있습니다.
+
+다음은 RVC에 必要한 預備모델 및 其他 파일 目錄입니다:
+```bash
+./assets/hubert_base
+
+./assets/pretrained
+
+./assets/uvr5_weights
+
+V2 버전 모델을 테스트하려면 추가 다운로드가 필요합니다.
+
+./assets/pretrained_v2
+
+# Windows를 使用하는境遇 이 사전도 必要할 수 있습니다. FFmpeg가 設置되어 있으면 건너뛰어도 됩니다.
+ffmpeg.exe
+```
+그後 以下의 命令을 使用하여 WebUI를 始作할 수 있습니다:
+```bash
+python webui.py
+```
+Windows를 使用하는境遇 `RVC-beta.7z`를 다운로드 및 壓縮解除하여 RVC를 直接使用하거나 `go-webui.bat`을 使用하여 WebUi를 直接할 수 있습니다.
+
+## 參考
++ [ContentVec](https://github.com/auspicious3000/contentvec/)
++ [VITS](https://github.com/jaywalnut310/vits)
++ [HIFIGAN](https://github.com/jik876/hifi-gan)
++ [Gradio](https://github.com/gradio-app/gradio)
++ [FFmpeg](https://github.com/FFmpeg/FFmpeg)
++ [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
++ [audio-slicer](https://github.com/openvpi/audio-slicer)
+## 모든寄與者분들의勞力에感謝드립니다
+
+
+
+
diff --git a/docs/kr/README.ko.md b/docs/kr/README.ko.md
new file mode 100644
index 0000000..b65f13d
--- /dev/null
+++ b/docs/kr/README.ko.md
@@ -0,0 +1,246 @@
+
+
+
Retrieval-based-Voice-Conversion-WebUI
+VITS 기반의 간단하고 사용하기 쉬운 음성 변환 프레임워크.
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+[**업데이트 로그**](./Changelog_KO.md) | [**자주 묻는 질문**](./faq_ko.md) | [**AutoDL·5원으로 AI 가수 훈련**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/Autodl%E8%AE%AD%E7%BB%83RVC%C2%B7AI%E6%AD%8C%E6%89%8B%E6%95%99%E7%A8%8B) | [**대조 실험 기록**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/%E5%AF%B9%E7%85%A7%E5%AE%9E%E9%AA%8C%C2%B7%E5%AE%9E%E9%AA%8C%E8%AE%B0%E5%BD%95) | [**온라인 데모**](https://modelscope.cn/studios/FlowerCry/RVCv2demo)
+
+[**English**](../en/README.en.md) | [**中文简体**](../../README.md) | [**日本語**](../jp/README.ja.md) | [**한국어**](../kr/README.ko.md) ([**韓國語**](../kr/README.ko.han.md)) | [**Français**](../fr/README.fr.md) | [**Türkçe**](../tr/README.tr.md) | [**Português**](../pt/README.pt.md)
+
+
+
+> [데모 영상](https://www.bilibili.com/video/BV1pm4y1z7Gm/)을 확인해 보세요!
+
+> RVC를 활용한 실시간 음성변환: [w-okada/voice-changer](https://github.com/w-okada/voice-changer)
+
+> 기본 모델은 50시간 가량의 고퀄리티 오픈 소스 VCTK 데이터셋을 사용하였으므로, 저작권상의 염려가 없으니 안심하고 사용하시기 바랍니다.
+
+> 더 큰 매개변수, 더 큰 데이터, 더 나은 효과, 기본적으로 동일한 추론 속도, 더 적은 양의 훈련 데이터가 필요한 RVCv3의 기본 모델을 기대해 주십시오.
+
+
+
+ | 훈련 및 추론 인터페이스 |
+ 실시간 음성 변환 인터페이스 |
+
+
+  |
+  |
+
+
+ | go-webui.bat |
+ go-realtime_gui.bat |
+
+
+ | 원하는 작업을 자유롭게 선택할 수 있습니다. |
+ 우리는 이미 끝에서 끝까지 170ms의 지연을 실현했습니다. ASIO 입력 및 출력 장치를 사용하면 끝에서 끝까지 90ms의 지연을 달성할 수 있지만, 이는 하드웨어 드라이버 지원에 매우 의존적입니다. |
+
+
+
+## 소개
+
+본 Repo는 다음과 같은 특징을 가지고 있습니다:
+
+- top1 검색을 이용하여 입력 음색 특징을 훈련 세트 음색 특징으로 대체하여 음색의 누출을 방지
+- 상대적으로 낮은 성능의 GPU에서도 빠른 훈련 가능
+- 적은 양의 데이터로 훈련해도 좋은 결과를 얻을 수 있음 (최소 10분 이상의 저잡음 음성 데이터를 사용하는 것을 권장)
+- 모델 융합을 통한 음색의 변조 가능 (ckpt 처리 탭->ckpt 병합 선택)
+- 사용하기 쉬운 WebUI (웹 인터페이스)
+- UVR5 모델을 이용하여 목소리와 배경음악의 빠른 분리;
+- 최첨단 [음성 피치 추출 알고리즘 InterSpeech2023-RMVPE](#参考项目)을 사용하여 무성음 문제를 해결합니다. 효과는 최고(압도적)이며 crepe_full보다 더 빠르고 리소스 사용이 적음
+- A카드와 I카드 가속을 지원
+
+해당 프로젝트의 [데모 비디오](https://www.bilibili.com/video/BV1pm4y1z7Gm/)를 확인해보세요!
+
+## 환경 설정
+
+다음 명령은 Python 버전이 3.8 이상인 환경에서 실행해야 합니다.
+
+### Windows/Linux/MacOS 등 플랫폼 공통 방법
+
+아래 방법 중 하나를 선택하세요.
+
+#### 1. pip를 통한 의존성 설치
+
+1. Pytorch 및 의존성 모듈 설치, 이미 설치되어 있으면 생략. 참조: https://pytorch.org/get-started/locally/
+
+```bash
+pip install torch torchvision torchaudio
+```
+
+2. win 시스템 + Nvidia Ampere 아키텍처(RTX30xx) 사용 시, #21의 사례에 따라 pytorch에 해당하는 cuda 버전을 지정
+
+```bash
+pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+```
+
+3. 자신의 그래픽 카드에 맞는 의존성 설치
+
+- N카드
+
+```bash
+pip install -r requirements.txt
+```
+
+- A카드/I카드
+
+```bash
+pip install -r requirements-dml.txt
+```
+
+- A카드ROCM(Linux)
+
+```bash
+pip install -r requirements-amd.txt
+```
+
+#### 2. poetry를 통한 의존성 설치
+
+Poetry 의존성 관리 도구 설치, 이미 설치된 경우 생략. 참조: https://python-poetry.org/docs/#installation
+
+```bash
+curl -sSL https://install.python-poetry.org | python3 -
+```
+
+poetry를 통한 의존성 설치
+
+```bash
+poetry install
+```
+
+### MacOS
+
+`run.sh`를 통해 의존성 설치 가능
+
+```bash
+sh ./run.sh
+```
+
+## 기타 사전 훈련된 모델 준비
+
+RVC는 추론과 훈련을 위해 다른 일부 사전 훈련된 모델이 필요합니다.
+
+이러한 모델은 저희의 [Hugging Face space](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)에서 다운로드할 수 있습니다.
+
+### 1. assets 다운로드
+
+다음은 RVC에 필요한 모든 사전 훈련된 모델과 기타 파일의 목록입니다. `tools` 폴더에서 이들을 다운로드하는 스크립트를 찾을 수 있습니다.
+
+- ./assets/hubert_base
+
+- ./assets/pretrained
+
+- ./assets/uvr5_weights
+
+v2 버전 모델을 사용하려면 추가로 다음을 다운로드해야 합니다.
+
+- ./assets/pretrained_v2
+
+### 2. ffmpeg 설치
+
+ffmpeg와 ffprobe가 이미 설치되어 있다면 건너뜁니다.
+
+#### Ubuntu/Debian 사용자
+
+```bash
+sudo apt install ffmpeg
+```
+
+#### MacOS 사용자
+
+```bash
+brew install ffmpeg
+```
+
+#### Windows 사용자
+
+다운로드 후 루트 디렉토리에 배치.
+
+- [ffmpeg.exe 다운로드](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffmpeg.exe)
+
+- [ffprobe.exe 다운로드](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffprobe.exe)
+
+### 3. RMVPE 인간 음성 피치 추출 알고리즘에 필요한 파일 다운로드
+
+최신 RMVPE 인간 음성 피치 추출 알고리즘을 사용하려면 음피치 추출 모델 매개변수를 다운로드하고 RVC 루트 디렉토리에 배치해야 합니다.
+
+- [rmvpe.pt 다운로드](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.pt)
+
+#### dml 환경의 RMVPE 다운로드(선택사항, A카드/I카드 사용자)
+
+- [rmvpe.onnx 다운로드](https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.onnx)
+
+### 4. AMD 그래픽 카드 Rocm(선택사항, Linux만 해당)
+
+Linux 시스템에서 AMD의 Rocm 기술을 기반으로 RVC를 실행하려면 [여기](https://rocm.docs.amd.com/en/latest/deploy/linux/os-native/install.html)에서 필요한 드라이버를 먼저 설치하세요.
+
+Arch Linux를 사용하는 경우 pacman을 사용하여 필요한 드라이버를 설치할 수 있습니다.
+
+```
+pacman -S rocm-hip-sdk rocm-opencl-sdk
+```
+
+일부 모델의 그래픽 카드(예: RX6700XT)의 경우, 다음과 같은 환경 변수를 추가로 설정해야 할 수 있습니다.
+
+```
+export ROCM_PATH=/opt/rocm
+export HSA_OVERRIDE_GFX_VERSION=10.3.0
+```
+
+동시에 현재 사용자가 `render` 및 `video` 사용자 그룹에 속해 있는지 확인하세요.
+
+```
+sudo usermod -aG render $USERNAME
+sudo usermod -aG video $USERNAME
+```
+
+## 시작하기
+
+### 직접 시작
+
+다음 명령어로 WebUI를 시작하세요
+
+```bash
+python webui.py
+```
+
+### 통합 패키지 사용
+
+`RVC-beta.7z`를 다운로드하고 압축 해제
+
+#### Windows 사용자
+
+`go-webui.bat` 더블 클릭
+
+#### MacOS 사용자
+
+```bash
+sh ./run.sh
+```
+
+## 참조 프로젝트
+
+- [ContentVec](https://github.com/auspicious3000/contentvec/)
+- [VITS](https://github.com/jaywalnut310/vits)
+- [HIFIGAN](https://github.com/jik876/hifi-gan)
+- [Gradio](https://github.com/gradio-app/gradio)
+- [FFmpeg](https://github.com/FFmpeg/FFmpeg)
+- [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
+- [audio-slicer](https://github.com/openvpi/audio-slicer)
+- [Vocal pitch extraction:RMVPE](https://github.com/Dream-High/RMVPE)
+ - 사전 훈련된 모델은 [yxlllc](https://github.com/yxlllc/RMVPE)와 [RVC-Boss](https://github.com/RVC-Boss)에 의해 훈련되고 테스트되었습니다.
+
+## 모든 기여자들의 노력에 감사드립니다
+
+
+
+
diff --git a/docs/kr/faiss_tips_ko.md b/docs/kr/faiss_tips_ko.md
new file mode 100644
index 0000000..9371d67
--- /dev/null
+++ b/docs/kr/faiss_tips_ko.md
@@ -0,0 +1,132 @@
+Facebook AI Similarity Search (Faiss) 팁
+==================
+# Faiss에 대하여
+Faiss 는 Facebook Research가 개발하는, 고밀도 벡터 이웃 검색 라이브러리입니다. 근사 근접 탐색법 (Approximate Neigbor Search)은 약간의 정확성을 희생하여 유사 벡터를 고속으로 찾습니다.
+
+## RVC에 있어서 Faiss
+RVC에서는 HuBERT로 변환한 feature의 embedding을 위해 훈련 데이터에서 생성된 embedding과 유사한 embadding을 검색하고 혼합하여 원래의 음성에 더욱 가까운 변환을 달성합니다. 그러나, 이 탐색법은 단순히 수행하면 시간이 다소 소모되므로, 근사 근접 탐색법을 통해 고속 변환을 가능케 하고 있습니다.
+
+# 구현 개요
+모델이 위치한 `/logs/your-experiment/3_feature256`에는 각 음성 데이터에서 HuBERT가 추출한 feature들이 있습니다. 여기에서 파일 이름별로 정렬된 npy 파일을 읽고, 벡터를 연결하여 big_npy ([N, 256] 모양의 벡터) 를 만듭니다. big_npy를 `/logs/your-experiment/total_fea.npy`로 저장한 후, Faiss로 학습시킵니다.
+
+2023/04/18 기준으로, Faiss의 Index Factory 기능을 이용해, L2 거리에 근거하는 IVF를 이용하고 있습니다. IVF의 분할수(n_ivf)는 N//39로, n_probe는 int(np.power(n_ivf, 0.3))가 사용되고 있습니다. (webui.py의 train_index 주위를 찾으십시오.)
+
+이 팁에서는 먼저 이러한 매개 변수의 의미를 설명하고, 개발자가 추후 더 나은 index를 작성할 수 있도록 하는 조언을 작성합니다.
+
+# 방법의 설명
+## Index factory
+index factory는 여러 근사 근접 탐색법을 문자열로 연결하는 pipeline을 문자열로 표기하는 Faiss만의 독자적인 기법입니다. 이를 통해 index factory의 문자열을 변경하는 것만으로 다양한 근사 근접 탐색을 시도해 볼 수 있습니다. RVC에서는 다음과 같이 사용됩니다:
+
+```python
+index = Faiss.index_factory(256, "IVF%s,Flat" % n_ivf)
+```
+`index_factory`의 인수들 중 첫 번째는 벡터의 차원 수이고, 두번째는 index factory 문자열이며, 세번째에는 사용할 거리를 지정할 수 있습니다.
+
+기법의 보다 자세한 설명은 https://github.com/facebookresearch/Faiss/wiki/The-index-factory 를 확인해 주십시오.
+
+## 거리에 대한 index
+embedding의 유사도로서 사용되는 대표적인 지표로서 이하의 2개가 있습니다.
+
+- 유클리드 거리 (METRIC_L2)
+- 내적(内積) (METRIC_INNER_PRODUCT)
+
+유클리드 거리에서는 각 차원에서 제곱의 차를 구하고, 각 차원에서 구한 차를 모두 더한 후 제곱근을 취합니다. 이것은 일상적으로 사용되는 2차원, 3차원에서의 거리의 연산법과 같습니다. 내적은 그 값을 그대로 유사도 지표로 사용하지 않고, L2 정규화를 한 이후 내적을 취하는 코사인 유사도를 사용합니다.
+
+어느 쪽이 더 좋은지는 경우에 따라 다르지만, word2vec에서 얻은 embedding 및 ArcFace를 활용한 이미지 검색 모델은 코사인 유사성이 이용되는 경우가 많습니다. numpy를 사용하여 벡터 X에 대해 L2 정규화를 하고자 하는 경우, 0 division을 피하기 위해 충분히 작은 값을 eps로 한 뒤 이하에 코드를 활용하면 됩니다.
+
+```python
+X_normed = X / np.maximum(eps, np.linalg.norm(X, ord=2, axis=-1, keepdims=True))
+```
+
+또한, `index factory`의 3번째 인수에 건네주는 값을 선택하는 것을 통해 계산에 사용하는 거리 index를 변경할 수 있습니다.
+
+```python
+index = Faiss.index_factory(dimention, text, Faiss.METRIC_INNER_PRODUCT)
+```
+
+## IVF
+IVF (Inverted file indexes)는 역색인 탐색법과 유사한 알고리즘입니다. 학습시에는 검색 대상에 대해 k-평균 군집법을 실시하고 클러스터 중심을 이용해 보로노이 분할을 실시합니다. 각 데이터 포인트에는 클러스터가 할당되므로, 클러스터에서 데이터 포인트를 조회하는 dictionary를 만듭니다.
+
+예를 들어, 클러스터가 다음과 같이 할당된 경우
+|index|Cluster|
+|-----|-------|
+|1|A|
+|2|B|
+|3|A|
+|4|C|
+|5|B|
+
+IVF 이후의 결과는 다음과 같습니다:
+
+|cluster|index|
+|-------|-----|
+|A|1, 3|
+|B|2, 5|
+|C|4|
+
+탐색 시, 우선 클러스터에서 `n_probe`개의 클러스터를 탐색한 다음, 각 클러스터에 속한 데이터 포인트의 거리를 계산합니다.
+
+# 권장 매개변수
+index의 선택 방법에 대해서는 공식적으로 가이드 라인이 있으므로, 거기에 준해 설명합니다.
+https://github.com/facebookresearch/Faiss/wiki/Guidelines-to-choose-an-index
+
+1M 이하의 데이터 세트에 있어서는 4bit-PQ가 2023년 4월 시점에서는 Faiss로 이용할 수 있는 가장 효율적인 수법입니다. 이것을 IVF와 조합해, 4bit-PQ로 후보를 추려내고, 마지막으로 이하의 index factory를 이용하여 정확한 지표로 거리를 재계산하면 됩니다.
+
+```python
+index = Faiss.index_factory(256, "IVF1024,PQ128x4fs,RFlat")
+```
+
+## IVF 권장 매개변수
+IVF의 수가 너무 많으면, 가령 데이터 수의 수만큼 IVF로 양자화(Quantization)를 수행하면, 이것은 완전탐색과 같아져 효율이 나빠지게 됩니다. 1M 이하의 경우 IVF 값은 데이터 포인트 수 N에 대해 4sqrt(N) ~ 16sqrt(N)를 사용하는 것을 권장합니다.
+
+n_probe는 n_probe의 수에 비례하여 계산 시간이 늘어나므로 정확도와 시간을 적절히 균형을 맞추어 주십시오. 개인적으로 RVC에 있어서 그렇게까지 정확도는 필요 없다고 생각하기 때문에 n_probe = 1이면 된다고 생각합니다.
+
+## FastScan
+FastScan은 직적 양자화를 레지스터에서 수행함으로써 거리의 고속 근사를 가능하게 하는 방법입니다.직적 양자화는 학습시에 d차원마다(보통 d=2)에 독립적으로 클러스터링을 실시해, 클러스터끼리의 거리를 사전 계산해 lookup table를 작성합니다. 예측시는 lookup table을 보면 각 차원의 거리를 O(1)로 계산할 수 있습니다. 따라서 PQ 다음에 지정하는 숫자는 일반적으로 벡터의 절반 차원을 지정합니다.
+
+FastScan에 대한 자세한 설명은 공식 문서를 참조하십시오.
+https://github.com/facebookresearch/Faiss/wiki/Fast-accumulation-of-PQ-and-AQ-codes-(FastScan)
+
+## RFlat
+RFlat은 FastScan이 계산한 대략적인 거리를 index factory의 3번째 인수로 지정한 정확한 거리로 다시 계산하라는 인스트럭션입니다. k개의 근접 변수를 가져올 때 k*k_factor개의 점에 대해 재계산이 이루어집니다.
+
+# Embedding 테크닉
+## Alpha 쿼리 확장
+퀴리 확장이란 탐색에서 사용되는 기술로, 예를 들어 전문 탐색 시, 입력된 검색문에 단어를 몇 개를 추가함으로써 검색 정확도를 올리는 방법입니다. 백터 탐색을 위해서도 몇가지 방법이 제안되었는데, 그 중 α-쿼리 확장은 추가 학습이 필요 없는 매우 효과적인 방법으로 알려져 있습니다. [Attention-Based Query Expansion Learning](https://arxiv.org/abs/2007.08019)와 [2nd place solution of kaggle shopee competition](https://www.kaggle.com/code/lyakaap/2nd-place-solution/notebook) 논문에서 소개된 바 있습니다..
+
+α-쿼리 확장은 한 벡터에 인접한 벡터를 유사도의 α곱한 가중치로 더해주면 됩니다. 코드로 예시를 들어 보겠습니다. big_npy를 α query expansion로 대체합니다.
+
+```python
+alpha = 3.
+index = Faiss.index_factory(256, "IVF512,PQ128x4fs,RFlat")
+original_norm = np.maximum(np.linalg.norm(big_npy, ord=2, axis=1, keepdims=True), 1e-9)
+big_npy /= original_norm
+index.train(big_npy)
+index.add(big_npy)
+dist, neighbor = index.search(big_npy, num_expand)
+
+expand_arrays = []
+ixs = np.arange(big_npy.shape[0])
+for i in range(-(-big_npy.shape[0]//batch_size)):
+ ix = ixs[i*batch_size:(i+1)*batch_size]
+ weight = np.power(np.einsum("nd,nmd->nm", big_npy[ix], big_npy[neighbor[ix]]), alpha)
+ expand_arrays.append(np.sum(big_npy[neighbor[ix]] * np.expand_dims(weight, axis=2),axis=1))
+big_npy = np.concatenate(expand_arrays, axis=0)
+
+# index version 정규화
+big_npy = big_npy / np.maximum(np.linalg.norm(big_npy, ord=2, axis=1, keepdims=True), 1e-9)
+```
+
+위 테크닉은 탐색을 수행하는 쿼리에도, 탐색 대상 DB에도 적응 가능한 테크닉입니다.
+
+## MiniBatch KMeans에 의한 embedding 압축
+
+total_fea.npy가 너무 클 경우 K-means를 이용하여 벡터를 작게 만드는 것이 가능합니다. 이하 코드로 embedding의 압축이 가능합니다. n_clusters에 압축하고자 하는 크기를 지정하고 batch_size에 256 * CPU의 코어 수를 지정함으로써 CPU 병렬화의 혜택을 충분히 얻을 수 있습니다.
+
+```python
+import multiprocessing
+from sklearn.cluster import MiniBatchKMeans
+kmeans = MiniBatchKMeans(n_clusters=10000, batch_size=256 * multiprocessing.cpu_count(), init="random")
+kmeans.fit(big_npy)
+sample_npy = kmeans.cluster_centers_
+```
diff --git a/docs/kr/faq_ko.md b/docs/kr/faq_ko.md
new file mode 100644
index 0000000..cf9ce7d
--- /dev/null
+++ b/docs/kr/faq_ko.md
@@ -0,0 +1,130 @@
+## Q1:ffmpeg 오류/utf8 오류
+
+대부분의 경우 ffmpeg 문제가 아니라 오디오 경로 문제입니다.
+ffmpeg가 공백, () 등의 특수 문자가 포함된 경로를 읽을 때 ffmpeg 오류가 발생할 수 있습니다. 트레이닝 세트 오디오가 중문 경로일 때 filelist.txt에 쓸 때 utf8 오류가 발생할 수 있습니다.
+
+## Q2:일괄 트레이닝이 끝나고 인덱스가 없음
+
+"Training is done. The program is closed."라고 표시되면 모델 트레이닝이 성공한 것이며, 이어지는 오류는 가짜입니다.
+
+일괄 트레이닝이 끝나고 'added'로 시작하는 인덱스 파일이 없으면 트레이닝 세트가 너무 커서 인덱스 추가 단계에서 멈췄을 수 있습니다. 메모리에 대한 인덱스 추가 요구 사항이 너무 큰 문제를 배치 처리 add 인덱스로 해결했습니다. 임시로 "트레이닝 인덱스" 버튼을 다시 클릭해 보세요.
+
+## Q3:트레이닝이 끝나고 트레이닝 세트의 음색을 추론에서 보지 못함
+
+'음색 새로고침'을 클릭해 보세요. 여전히 없다면 트레이닝에 오류가 있는지, 콘솔 및 webui의 스크린샷, logs/실험명 아래의 로그를 개발자에게 보내 확인해 보세요.
+
+## Q4:모델 공유 방법
+
+rvc_root/logs/실험명 아래에 저장된 pth는 추론에 사용하기 위한 것이 아니라 실험 상태를 저장하고 복원하며, 트레이닝을 계속하기 위한 것입니다. 공유에 사용되는 모델은 weights 폴더 아래 60MB 이상인 pth 파일입니다.
+
+향후에는 weights/exp_name.pth와 logs/exp_name/added_xxx.index를 결합하여 weights/exp_name.zip으로 만들어 index 입력 단계를 생략할 예정입니다. 그러면 zip 파일을 공유하고 pth 파일은 공유하지 마세요. 단지 다른 기계에서 트레이닝을 계속하려는 경우에만 공유하세요.
+
+logs 폴더 아래 수백 MB의 pth 파일을 weights 폴더에 복사/공유하여 강제로 추론에 사용하면 f0, tgt_sr 등의 키가 없다는 오류가 발생할 수 있습니다. ckpt 탭 아래에서 수동 또는 자동(로컬 logs에서 관련 정보를 찾을 수 있는 경우 자동)으로 음성, 대상 오디오 샘플링률 옵션을 선택한 후 ckpt 소형 모델을 추출해야 합니다(입력 경로에 G로 시작하는 경로를 입력). 추출 후 weights 폴더에 60MB 이상의 pth 파일이 생성되며, 음색 새로고침 후 사용할 수 있습니다.
+
+## Q5:연결 오류
+
+아마도 컨트롤 콘솔(검은 창)을 닫았을 것입니다.
+
+## Q6:WebUI에서 "Expecting value: line 1 column 1 (char 0)" 오류가 발생함
+
+시스템 로컬 네트워크 프록시/글로벌 프록시를 닫으세요.
+
+이는 클라이언트의 프록시뿐만 아니라 서버 측의 프록시도 포함합니다(예: autodl로 http_proxy 및 https_proxy를 설정한 경우 사용 시 unset으로 끄세요).
+
+## Q7:WebUI 없이 명령으로 트레이닝 및 추론하는 방법
+
+트레이닝 스크립트:
+먼저 WebUI를 실행하여 데이터 세트 처리 및 트레이닝에 사용되는 명령줄을 메시지 창에서 확인할 수 있습니다.
+
+추론 스크립트:
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/myinfer.py
+
+예제:
+
+runtime\python.exe myinfer.py 0 "E:\codes\py39\RVC-beta\todo-songs\1111.wav" "E:\codes\py39\logs\mi-test\added_IVF677_Flat_nprobe_7.index" harvest "test.wav" "weights/mi-test.pth" 0.6 cuda:0 True
+
+f0up_key=sys.argv[1]
+input_path=sys.argv[2]
+index_path=sys.argv[3]
+f0method=sys.argv[4]#harvest 또는 pm
+opt_path=sys.argv[5]
+model_path=sys.argv[6]
+index_rate=float(sys.argv[7])
+device=sys.argv[8]
+is_half=bool(sys.argv[9])
+
+## Q8:Cuda 오류/Cuda 메모리 부족
+
+아마도 cuda 설정 문제이거나 장치가 지원되지 않을 수 있습니다. 대부분의 경우 메모리가 부족합니다(out of memory).
+
+트레이닝의 경우 batch size를 줄이세요(1로 줄여도 부족하다면 다른 그래픽 카드로 트레이닝을 해야 합니다). 추론의 경우 config.py 파일 끝에 있는 x_pad, x_query, x_center, x_max를 적절히 줄이세요. 4GB 미만의 메모리(예: 1060(3GB) 및 여러 2GB 그래픽 카드)를 가진 경우는 포기하세요. 4GB 메모리 그래픽 카드는 아직 구할 수 있습니다.
+
+## Q9:total_epoch를 몇으로 설정하는 것이 좋을까요
+
+트레이닝 세트의 오디오 품질이 낮고 배경 소음이 많으면 20~30이면 충분합니다. 너무 높게 설정하면 바닥 모델의 오디오 품질이 낮은 트레이닝 세트를 높일 수 없습니다.
+트레이닝 세트의 오디오 품질이 높고 배경 소음이 적고 길이가 길 경우 높게 설정할 수 있습니다. 200도 괜찮습니다(트레이닝 속도가 빠르므로, 고품질 트레이닝 세트를 준비할 수 있는 조건이 있다면, 그래픽 카드도 좋을 것이므로, 조금 더 긴 트레이닝 시간에 대해 걱정하지 않을 것입니다).
+
+## Q10: 트레이닝 세트는 얼마나 길어야 하나요
+
+10분에서 50분을 추천합니다.
+
+음질이 좋고 백그라운드 노이즈가 낮은 상태에서, 개인적인 특색 있는 음색이라면 더 많으면 더 좋습니다.
+
+고품질의 트레이닝 세트(정교하게 준비된 + 특색 있는 음색)라면, 5분에서 10분도 괜찮습니다. 저장소의 저자도 종종 이렇게 합니다.
+
+1분에서 2분의 데이터로 트레이닝에 성공한 사람도 있지만, 그러한 성공 사례는 다른 사람이 재현하기 어려우며 참고 가치가 크지 않습니다. 이는 트레이닝 세트의 음색이 매우 뚜렷해야 하며(예: 높은 주파수의 명확한 목소리나 소녀음) 음질이 좋아야 합니다.
+
+1분 미만의 데이터로 트레이닝을 시도(성공)한 사례는 아직 보지 못했습니다. 이런 시도는 권장하지 않습니다.
+
+## Q11: index rate는 무엇이며, 어떻게 조정하나요? (과학적 설명)
+
+만약 베이스 모델과 추론 소스의 음질이 트레이닝 세트보다 높다면, 그들은 추론 결과의 음질을 높일 수 있지만, 음색이 베이스 모델/추론 소스의 음색으로 기울어질 수 있습니다. 이 현상을 "음색 유출"이라고 합니다.
+
+index rate는 음색 유출 문제를 줄이거나 해결하는 데 사용됩니다. 1로 조정하면 이론적으로 추론 소스의 음색 유출 문제가 없지만, 음질은 트레이닝 세트에 더 가깝게 됩니다. 만약 트레이닝 세트의 음질이 추론 소스보다 낮다면, index rate를 높이면 음질이 낮아질 수 있습니다. 0으로 조정하면 검색 혼합을 이용하여 트레이닝 세트의 음색을 보호하는 효과가 없습니다.
+
+트레이닝 세트가 고품질이고 길이가 길 경우, total_epoch를 높일 수 있으며, 이 경우 모델 자체가 추론 소스와 베이스 모델의 음색을 거의 참조하지 않아 "음색 유출" 문제가 거의 발생하지 않습니다. 이때 index rate는 중요하지 않으며, 심지어 index 색인 파일을 생성하거나 공유하지 않아도 됩니다.
+
+## Q11: 추론시 GPU를 어떻게 선택하나요?
+
+config.py 파일에서 device cuda: 다음에 카드 번호를 선택합니다.
+카드 번호와 그래픽 카드의 매핑 관계는 트레이닝 탭의 그래픽 카드 정보란에서 볼 수 있습니다.
+
+## Q12: 트레이닝 중간에 저장된 pth를 어떻게 추론하나요?
+
+ckpt 탭 하단에서 소형 모델을 추출합니다.
+
+## Q13: 트레이닝을 어떻게 중단하고 계속할 수 있나요?
+
+현재 단계에서는 WebUI 콘솔을 닫고 go-webui.bat을 더블 클릭하여 프로그램을 다시 시작해야 합니다. 웹 페이지 매개변수도 새로 고쳐서 다시 입력해야 합니다.
+트레이닝을 계속하려면: 같은 웹 페이지 매개변수로 트레이닝 모델을 클릭하면 이전 체크포인트에서 트레이닝을 계속합니다.
+
+## Q14: 트레이닝 중 파일 페이지/메모리 오류가 발생하면 어떻게 해야 하나요?
+
+프로세스가 너무 많이 열려 메모리가 폭발했습니다. 다음과 같은 방법으로 해결할 수 있습니다.
+
+1. "음높이 추출 및 데이터 처리에 사용되는 CPU 프로세스 수"를 적당히 낮춥니다.
+2. 트레이닝 세트 오디오를 수동으로 잘라 너무 길지 않게 합니다.
+
+## Q15: 트레이닝 도중 데이터를 어떻게 추가하나요?
+
+1. 모든 데이터에 새로운 실험 이름을 만듭니다.
+2. 이전에 가장 최신의 G와 D 파일(또는 어떤 중간 ckpt를 기반으로 트레이닝하고 싶다면 중간 것을 복사할 수도 있음)을 새 실험 이름으로 복사합니다.
+3. 새 실험 이름으로 원클릭 트레이닝을 시작하면 이전의 최신 진행 상황에서 계속 트레이닝합니다.
+
+## Q16: llvmlite.dll에 관한 오류
+
+```bash
+OSError: Could not load shared object file: llvmlite.dll
+
+FileNotFoundError: Could not find module lib\site-packages\llvmlite\binding\llvmlite.dll (or one of its dependencies). Try using the full path with constructor syntax.
+```
+
+Windows 플랫폼에서 이 오류가 발생하면 https://aka.ms/vs/17/release/vc_redist.x64.exe를 설치하고 WebUI를 다시 시작하면 해결됩니다.
+
+## Q17: RuntimeError: 텐서의 확장된 크기(17280)는 비 단일 항목 차원 1에서 기존 크기(0)와 일치해야 합니다. 대상 크기: [1, 17280]. 텐서 크기: [0]
+
+wavs16k 폴더 아래에서 다른 파일들보다 크기가 현저히 작은 일부 오디오 파일을 찾아 삭제하고, 트레이닝 모델을 클릭하면 오류가 발생하지 않습니다. 하지만 원클릭 프로세스가 중단되었기 때문에 모델 트레이닝이 완료된 후에는 인덱스 트레이닝을 클릭해야 합니다.
+
+## Q18: RuntimeError: 텐서 a의 크기(24)가 비 단일 항목 차원 2에서 텐서 b(16)의 크기와 일치해야 합니다.
+
+트레이닝 도중에 샘플링 레이트를 변경해서는 안 됩니다. 변경해야 한다면 실험 이름을 변경하고 처음부터 트레이닝해야 합니다. 물론, 이전에 추출한 음높이와 특징(0/1/2/2b 폴더)을 복사하여 트레이닝 프로세스를 가속화할 수도 있습니다.
diff --git a/docs/kr/training_tips_ko.md b/docs/kr/training_tips_ko.md
new file mode 100644
index 0000000..8b3b624
--- /dev/null
+++ b/docs/kr/training_tips_ko.md
@@ -0,0 +1,53 @@
+RVC 훈련에 대한 설명과 팁들
+======================================
+본 팁에서는 어떻게 데이터 훈련이 이루어지고 있는지 설명합니다.
+
+# 훈련의 흐름
+GUI의 훈련 탭의 단계를 따라 설명합니다.
+
+## step1
+실험 이름을 지정합니다. 또한, 모델이 피치(소리의 높낮이)를 고려해야 하는지 여부를 여기에서 설정할 수도 있습니다..
+각 실험을 위한 데이터는 `/logs/experiment name/`에 배치됩니다..
+
+## step2a
+음성 파일을 불러오고 전처리합니다.
+
+### 음성 파일 불러오기
+음성 파일이 있는 폴더를 지정하면 해당 폴더에 있는 음성 파일이 자동으로 가져와집니다.
+예를 들어 `C:Users\hoge\voices`를 지정하면 `C:Users\hoge\voices\voice.mp3`가 읽히지만 `C:Users\hoge\voices\dir\voice.mp3`는 읽히지 않습니다.
+
+음성 로드에는 내부적으로 ffmpeg를 이용하고 있으므로, ffmpeg로 대응하고 있는 확장자라면 자동적으로 읽힙니다.
+ffmpeg에서 int16으로 변환한 후 float32로 변환하고 -1과 1 사이에 정규화됩니다.
+
+### 잡음 제거
+음성 파일에 대해 scipy의 filtfilt를 이용하여 잡음을 처리합니다.
+
+### 음성 분할
+입력한 음성 파일은 먼저 일정 기간(max_sil_kept=5초?)보다 길게 무음이 지속되는 부분을 감지하여 음성을 분할합니다.무음으로 음성을 분할한 후에는 0.3초의 overlap을 포함하여 4초마다 음성을 분할합니다.4초 이내에 구분된 음성은 음량의 정규화를 실시한 후 wav 파일을 `/logs/실험명/0_gt_wavs`로, 거기에서 16k의 샘플링 레이트로 변환해 `/logs/실험명/1_16k_wavs`에 wav 파일로 저장합니다.
+
+## step2b
+### 피치 추출
+wav 파일에서 피치(소리의 높낮이) 정보를 추출합니다. parselmouth나 pyworld에 내장되어 있는 메서드으로 피치 정보(=f0)를 추출해, `/logs/실험명/2a_f0`에 저장합니다. 그 후 피치 정보를 로그로 변환하여 1~255 정수로 변환하고 `/logs/실험명/2b-f0nsf`에 저장합니다.
+
+### feature_print 추출
+HuBERT를 이용하여 wav 파일을 미리 embedding으로 변환합니다. `/logs/실험명/1_16k_wavs`에 저장한 wav 파일을 읽고 HuBERT에서 wav 파일을 256차원 feature들로 변환한 후 npy 형식으로 `/logs/실험명/3_feature256`에 저장합니다.
+
+## step3
+모델의 훈련을 진행합니다.
+
+### 초보자용 용어 해설
+심층학습(딥러닝)에서는 데이터셋을 분할하여 조금씩 학습을 진행합니다.한 번의 모델 업데이트(step) 단계 당 batch_size개의 데이터를 탐색하여 예측과 오차를 수정합니다. 데이터셋 전부에 대해 이 작업을 한 번 수행하는 이를 하나의 epoch라고 계산합니다.
+
+따라서 학습 시간은 단계당 학습 시간 x (데이터셋 내 데이터의 수 / batch size) x epoch 수가 소요됩니다. 일반적으로 batch size가 클수록 학습이 안정적이게 됩니다. (step당 학습 시간 ÷ batch size)는 작아지지만 GPU 메모리를 더 많이 사용합니다. GPU RAM은 nvidia-smi 명령어를 통해 확인할 수 있습니다. 실행 환경에 따라 배치 크기를 최대한 늘리면 짧은 시간 내에 학습이 가능합니다.
+
+### 사전 학습된 모델 지정
+RVC는 적은 데이터셋으로도 훈련이 가능하도록 사전 훈련된 가중치에서 모델 훈련을 시작합니다. 기본적으로 `rvc-location/pretrained/f0G40k.pth` 및 `rvc-location/pretrained/f0D40k.pth`를 불러옵니다. 학습을 할 시에, 모델 파라미터는 각 save_every_epoch별로 `logs/experiment name/G_{}.pth` 와 `logs/experiment name/D_{}.pth`로 저장이 되는데, 이 경로를 지정함으로써 학습을 재개하거나, 다른 실험에서 학습한 모델의 가중치에서 학습을 시작할 수 있습니다.
+
+### index의 학습
+RVC에서는 학습시에 사용된 HuBERT의 feature값을 저장하고, 추론 시에는 학습 시 사용한 feature값과 유사한 feature 값을 탐색해 추론을 진행합니다. 이 탐색을 고속으로 수행하기 위해 사전에 index을 학습하게 됩니다.
+Index 학습에는 근사 근접 탐색법 라이브러리인 Faiss를 사용하게 됩니다. `/logs/실험명/3_feature256`의 feature값을 불러와, 이를 모두 결합시킨 feature값을 `/logs/실험명/total_fea.npy`로서 저장, 그것을 사용해 학습한 index를`/logs/실험명/add_XXX.index`로 저장합니다.
+
+### 버튼 설명
+- モデルのトレーニング (모델 학습): step2b까지 실행한 후, 이 버튼을 눌러 모델을 학습합니다.
+- 特徴インデックスのトレーニング (특징 지수 훈련): 모델의 훈련 후, index를 학습합니다.
+- ワンクリックトレーニング (원클릭 트레이닝): step2b까지의 모델 훈련, feature index 훈련을 일괄로 실시합니다.
\ No newline at end of file
diff --git a/docs/pt/Changelog_pt.md b/docs/pt/Changelog_pt.md
new file mode 100644
index 0000000..c7e8b5b
--- /dev/null
+++ b/docs/pt/Changelog_pt.md
@@ -0,0 +1,105 @@
+### 2023-10-06
+- Criamos uma GUI para alteração de voz em tempo real: go-realtime_gui.bat/realtime_gui.py (observe que você deve escolher o mesmo tipo de dispositivo de entrada e saída, por exemplo, MME e MME).
+- Treinamos um modelo RMVPE de extração de pitch melhor.
+- Otimizar o layout da GUI de inferência.
+
+### 2023-08-13
+1-Correção de bug regular
+- Alterar o número total mínimo de épocas para 1 e alterar o número total mínimo de epoch para 2
+- Correção de erros de treinamento por não usar modelos de pré-treinamento
+- Após a separação dos vocais de acompanhamento, limpe a memória dos gráficos
+- Alterar o caminho absoluto do faiss save para o caminho relativo
+- Suporte a caminhos com espaços (tanto o caminho do conjunto de treinamento quanto o nome do experimento são suportados, e os erros não serão mais relatados)
+- A lista de arquivos cancela a codificação utf8 obrigatória
+- Resolver o problema de consumo de CPU causado pela busca do faiss durante alterações de voz em tempo real
+
+Atualizações do 2-Key
+- Treine o modelo de extração de pitch vocal de código aberto mais forte do momento, o RMVPE, e use-o para treinamento de RVC, inferência off-line/em tempo real, com suporte a PyTorch/Onnx/DirectML
+- Suporte para placas gráficas AMD e Intel por meio do Pytorch_DML
+
+(1) Mudança de voz em tempo real (2) Inferência (3) Separação do acompanhamento vocal (4) Não há suporte para treinamento no momento, mudaremos para treinamento de CPU; há suporte para inferência RMVPE de gpu por Onnx_Dml
+
+
+### 2023-06-18
+- Novos modelos v2 pré-treinados: 32k e 48k
+- Correção de erros de inferência de modelo não-f0
+- Para conjuntos de treinamento que excedam 1 hora, faça minibatch-kmeans automáticos para reduzir a forma dos recursos, de modo que o treinamento, a adição e a pesquisa do Index sejam muito mais rápidos.
+- Fornecer um espaço de brinquedo vocal2guitar huggingface
+- Exclusão automática de áudios de conjunto de treinamento de atalhos discrepantes
+- Guia de exportação Onnx
+
+Experimentos com falha:
+- ~~Recuperação de recurso: adicionar recuperação de recurso temporal: não eficaz~~
+- ~~Recuperação de recursos: adicionar redução de dimensionalidade PCAR: a busca é ainda mais lenta~~
+- ~~Aumento de dados aleatórios durante o treinamento: não é eficaz~~
+
+Lista de tarefas:
+- ~~Vocos-RVC (vocoder minúsculo): não é eficaz~~
+- ~~Suporte de crepe para treinamento: substituído pelo RMVPE~~
+- ~~Inferência de crepe de meia precisão:substituída pelo RMVPE. E difícil de conseguir.~~
+- Suporte ao editor de F0
+
+### 2023-05-28
+- Adicionar notebook jupyter v2, changelog em coreano, corrigir alguns requisitos de ambiente
+- Adicionar consoante sem voz e modo de proteção de respiração
+- Suporte à detecção de pitch crepe-full
+- Separação vocal UVR5: suporte a modelos dereverb e modelos de-echo
+- Adicionar nome e versão do experimento no nome do Index
+- Suporte aos usuários para selecionar manualmente o formato de exportação dos áudios de saída durante o processamento de conversão de voz em lote e a separação vocal UVR5
+- Não há mais suporte para o treinamento do modelo v1 32k
+
+### 2023-05-13
+- Limpar os códigos redundantes na versão antiga do tempo de execução no pacote de um clique: lib.infer_pack e uvr5_pack
+- Correção do bug de pseudo multiprocessamento no pré-processamento do conjunto de treinamento
+- Adição do ajuste do raio de filtragem mediana para o algoritmo de reconhecimento de inclinação da extração
+- Suporte à reamostragem de pós-processamento para exportação de áudio
+- A configuração "n_cpu" de multiprocessamento para treinamento foi alterada de "extração de f0" para "pré-processamento de dados e extração de f0"
+- Detectar automaticamente os caminhos de Index na pasta de registros e fornecer uma função de lista suspensa
+- Adicionar "Perguntas e respostas frequentes" na página da guia (você também pode consultar o wiki do RVC no github)
+- Durante a inferência, o pitch da colheita é armazenado em cache quando se usa o mesmo caminho de áudio de entrada (finalidade: usando a extração do pitch da colheita, todo o pipeline passará por um processo longo e repetitivo de extração do pitch. Se o armazenamento em cache não for usado, os usuários que experimentarem diferentes configurações de raio de filtragem de timbre, Index e mediana de pitch terão um processo de espera muito doloroso após a primeira inferência)
+
+### 2023-05-14
+- Use o envelope de volume da entrada para misturar ou substituir o envelope de volume da saída (pode aliviar o problema de "muting de entrada e ruído de pequena amplitude de saída"). Se o ruído de fundo do áudio de entrada for alto, não é recomendável ativá-lo, e ele não é ativado por padrão (1 pode ser considerado como não ativado)
+- Suporte ao salvamento de modelos pequenos extraídos em uma frequência especificada (se você quiser ver o desempenho em épocas diferentes, mas não quiser salvar todos os pontos de verificação grandes e extrair manualmente modelos pequenos pelo processamento ckpt todas as vezes, esse recurso será muito prático)
+- Resolver o problema de "erros de conexão" causados pelo proxy global do servidor, definindo variáveis de ambiente
+- Oferece suporte a modelos v2 pré-treinados (atualmente, apenas as versões 40k estão disponíveis publicamente para teste e as outras duas taxas de amostragem ainda não foram totalmente treinadas)
+- Limita o volume excessivo que excede 1 antes da inferência
+- Ajustou ligeiramente as configurações do pré-processamento do conjunto de treinamento
+
+
+#######################
+
+Histórico de registros de alterações:
+
+### 2023-04-09
+- Parâmetros de treinamento corrigidos para melhorar a taxa de utilização da GPU: A100 aumentou de 25% para cerca de 90%, V100: 50% para cerca de 90%, 2060S: 60% para cerca de 85%, P40: 25% para cerca de 95%; melhorou significativamente a velocidade de treinamento
+- Parâmetro alterado: total batch_size agora é por GPU batch_size
+- Total_epoch alterado: limite máximo aumentado de 100 para 1000; padrão aumentado de 10 para 20
+- Corrigido o problema da extração de ckpt que reconhecia o pitch incorretamente, causando inferência anormal
+- Corrigido o problema do treinamento distribuído que salvava o ckpt para cada classificação
+- Aplicada a filtragem de recursos nan para extração de recursos
+- Corrigido o problema com a entrada/saída silenciosa que produzia consoantes aleatórias ou ruído (os modelos antigos precisavam ser treinados novamente com um novo conjunto de dados)
+
+### Atualização 2023-04-16
+- Adicionada uma mini-GUI de alteração de voz local em tempo real, iniciada com um clique duplo em go-realtime_gui.bat
+- Filtragem aplicada para bandas de frequência abaixo de 50 Hz durante o treinamento e a inferência
+- Diminuição da extração mínima de tom do pyworld do padrão 80 para 50 para treinamento e inferência, permitindo que vozes masculinas de tom baixo entre 50-80 Hz não sejam silenciadas
+- A WebUI suporta a alteração de idiomas de acordo com a localidade do sistema (atualmente suporta en_US, ja_JP, zh_CN, zh_HK, zh_SG, zh_TW; o padrão é en_US se não for suportado)
+- Correção do reconhecimento de algumas GPUs (por exemplo, falha no reconhecimento da V100-16G, falha no reconhecimento da P4)
+
+### Atualização de 2023-04-28
+- Atualizadas as configurações do Index faiss para maior velocidade e qualidade
+- Removida a dependência do total_npy; o futuro compartilhamento de modelos não exigirá a entrada do total_npy
+- Restrições desbloqueadas para as GPUs da série 16, fornecendo configurações de inferência de 4 GB para GPUs com VRAM de 4 GB
+- Corrigido o erro na separação do acompanhamento vocal do UVR5 para determinados formatos de áudio
+- A mini-GUI de alteração de voz em tempo real agora suporta modelos de pitch não 40k e que não são lentos
+
+### Planos futuros:
+Recursos:
+- Opção de adição: extrair modelos pequenos para cada epoch salvo
+- Adicionar opção: exportar mp3 adicional para o caminho especificado durante a inferência
+- Suporte à guia de treinamento para várias pessoas (até 4 pessoas)
+
+Modelo básico:
+- Coletar arquivos wav de respiração para adicionar ao conjunto de dados de treinamento para corrigir o problema de sons de respiração distorcidos
+- No momento, estamos treinando um modelo básico com um conjunto de dados de canto estendido, que será lançado no futuro
diff --git a/docs/pt/README.pt.md b/docs/pt/README.pt.md
new file mode 100644
index 0000000..42d8fd0
--- /dev/null
+++ b/docs/pt/README.pt.md
@@ -0,0 +1,181 @@
+
+
+
Retrieval-based-Voice-Conversion-WebUI
+Uma estrutura de conversão de voz fácil de usar baseada em VITS.
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+
+
+------
+[**Changelog**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/docs/Changelog_EN.md) | [**FAQ (Frequently Asked Questions)**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/FAQ-(Frequently-Asked-Questions))
+
+[**English**](../en/README.en.md) | [**中文简体**](../../README.md) | [**日本語**](../jp/README.ja.md) | [**한국어**](../kr/README.ko.md) ([**韓國語**](../kr/README.ko.han.md)) | [**Türkçe**](../tr/README.tr.md) | [**Português**](../pt/README.pt.md)
+
+
+Confira nosso [Vídeo de demonstração](https://www.bilibili.com/video/BV1pm4y1z7Gm/) aqui!
+
+Treinamento/Inferência WebUI:go-webui.bat
+
+
+GUI de conversão de voz em tempo real:go-realtime_gui.bat
+
+
+
+> O dataset para o modelo de pré-treinamento usa quase 50 horas de conjunto de dados de código aberto VCTK de alta qualidade.
+
+> Dataset de músicas licenciadas de alta qualidade serão adicionados ao conjunto de treinamento, um após o outro, para seu uso, sem se preocupar com violação de direitos autorais.
+
+> Aguarde o modelo básico pré-treinado do RVCv3, que possui parâmetros maiores, mais dados de treinamento, melhores resultados, velocidade de inferência inalterada e requer menos dados de treinamento para treinamento.
+
+## Resumo
+Este repositório possui os seguintes recursos:
++ Reduza o vazamento de tom substituindo o recurso de origem pelo recurso de conjunto de treinamento usando a recuperação top1;
++ Treinamento fácil e rápido, mesmo em placas gráficas relativamente ruins;
++ Treinar com uma pequena quantidade de dados também obtém resultados relativamente bons (>=10min de áudio com baixo ruído recomendado);
++ Suporta fusão de modelos para alterar timbres (usando guia de processamento ckpt-> mesclagem ckpt);
++ Interface Webui fácil de usar;
++ Use o modelo UVR5 para separar rapidamente vocais e instrumentos.
++ Use o mais poderoso algoritmo de extração de voz de alta frequência [InterSpeech2023-RMVPE](#Credits) para evitar o problema de som mudo. Fornece os melhores resultados (significativamente) e é mais rápido, com consumo de recursos ainda menor que o Crepe_full.
++ Suporta aceleração de placas gráficas AMD/Intel.
+
+## Preparando o ambiente
+Os comandos a seguir precisam ser executados no ambiente Python versão 3.8 ou superior.
+
+(Windows/Linux)
+Primeiro instale as dependências principais através do pip:
+```bash
+# Instale as dependências principais relacionadas ao PyTorch, pule se instaladas
+# Referência: https://pytorch.org/get-started/locally/
+pip install torch torchvision torchaudio
+
+#Para arquitetura Windows + Nvidia Ampere (RTX30xx), você precisa especificar a versão cuda correspondente ao pytorch de acordo com a experiência de https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/issues/ 21
+#pip instalar tocha torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+
+#Para placas Linux + AMD, você precisa usar as seguintes versões do pytorch:
+#pip instalar tocha torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2
+```
+
+Então pode usar poesia para instalar as outras dependências:
+```bash
+# Instale a ferramenta de gerenciamento de dependências Poetry, pule se instalada
+# Referência: https://python-poetry.org/docs/#installation
+curl -sSL https://install.python-poetry.org | python3 -
+
+#Instale as dependências do projeto
+poetry install
+```
+
+Você também pode usar pip para instalá-los:
+```bash
+
+for Nvidia graphics cards
+ pip install -r requirements.txt
+
+for AMD/Intel graphics cards on Windows (DirectML):
+ pip install -r requirements-dml.txt
+
+for AMD graphics cards on Linux (ROCm):
+ pip install -r requirements-amd.txt
+```
+
+------
+Usuários de Mac podem instalar dependências via `run.sh`:
+```bash
+sh ./run.sh
+```
+
+## Preparação de outros Pré-modelos
+RVC requer outros pré-modelos para inferir e treinar.
+
+Baixe-os em nosso [Huggingface space](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/).
+
+Aqui está uma lista de pré-modelos e outros arquivos que o RVC precisa:
+```bash
+./assets/hubert_base
+
+./assets/pretrained
+
+./assets/uvr5_weights
+
+Downloads adicionais são necessários se você quiser testar a versão v2 do modelo.
+
+./assets/pretrained_v2
+
+Se você deseja testar o modelo da versão v2 (o modelo da versão v2 alterou a entrada do recurso dimensional 256 do Hubert + final_proj de 9 camadas para o recurso dimensional 768 do Hubert de 12 camadas e adicionou 3 discriminadores de período), você precisará baixar recursos adicionais
+
+./assets/pretrained_v2
+
+#Se você estiver usando Windows, também pode precisar desses dois arquivos, pule se FFmpeg e FFprobe estiverem instalados
+ffmpeg.exe
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffmpeg.exe
+
+ffprobe.exe
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffprobe.exe
+
+Se quiser usar o algoritmo de extração de tom vocal SOTA RMVPE mais recente, você precisa baixar os pesos RMVPE e colocá-los no diretório raiz RVC
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.pt
+
+ Para usuários de placas gráficas AMD/Intel, você precisa baixar:
+
+ https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.onnx
+
+```
+
+Em seguida, use este comando para iniciar o Webui:
+```bash
+python webui.py
+```
+
+Se estiver usando Windows ou macOS, você pode baixar e extrair `RVC-beta.7z` para usar RVC diretamente usando `go-webui.bat` no Windows ou `sh ./run.sh` no macOS para iniciar o Webui.
+
+## Suporte ROCm para placas gráficas AMD (somente Linux)
+Para usar o ROCm no Linux, instale todos os drivers necessários conforme descrito [aqui](https://rocm.docs.amd.com/en/latest/deploy/linux/os-native/install.html).
+
+No Arch use pacman para instalar o driver:
+````
+pacman -S rocm-hip-sdk rocm-opencl-sdk
+````
+
+Talvez você também precise definir estas variáveis de ambiente (por exemplo, em um RX6700XT):
+````
+export ROCM_PATH=/opt/rocm
+export HSA_OVERRIDE_GFX_VERSION=10.3.0
+````
+Verifique também se seu usuário faz parte do grupo `render` e `video`:
+````
+sudo usermod -aG render $USERNAME
+sudo usermod -aG video $USERNAME
+````
+Depois disso, você pode executar o WebUI:
+```bash
+python webui.py
+```
+
+## Credits
++ [ContentVec](https://github.com/auspicious3000/contentvec/)
++ [VITS](https://github.com/jaywalnut310/vits)
++ [HIFIGAN](https://github.com/jik876/hifi-gan)
++ [Gradio](https://github.com/gradio-app/gradio)
++ [FFmpeg](https://github.com/FFmpeg/FFmpeg)
++ [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
++ [audio-slicer](https://github.com/openvpi/audio-slicer)
++ [Vocal pitch extraction:RMVPE](https://github.com/Dream-High/RMVPE)
+ + The pretrained model is trained and tested by [yxlllc](https://github.com/yxlllc/RMVPE) and [RVC-Boss](https://github.com/RVC-Boss).
+
+## Thanks to all contributors for their efforts
+
+
+
diff --git a/docs/pt/faiss_tips_pt.md b/docs/pt/faiss_tips_pt.md
new file mode 100644
index 0000000..8649979
--- /dev/null
+++ b/docs/pt/faiss_tips_pt.md
@@ -0,0 +1,102 @@
+pONTAS de afinação FAISS
+==================
+# sobre faiss
+faiss é uma biblioteca de pesquisas de vetores densos na área, desenvolvida pela pesquisa do facebook, que implementa com eficiência muitos métodos de pesquisa de área aproximada.
+A Pesquisa Aproximada de área encontra vetores semelhantes rapidamente, sacrificando alguma precisão.
+
+## faiss em RVC
+No RVC, para a incorporação de recursos convertidos pelo HuBERT, buscamos incorporações semelhantes à incorporação gerada a partir dos dados de treinamento e as misturamos para obter uma conversão mais próxima do discurso original. No entanto, como essa pesquisa leva tempo se realizada de forma ingênua, a conversão de alta velocidade é realizada usando a pesquisa aproximada de área.
+
+# visão geral da implementação
+Em '/logs/nome-do-seu-modelo/3_feature256', onde o modelo está localizado, os recursos extraídos pelo HuBERT de cada dado de voz estão localizados.
+A partir daqui, lemos os arquivos npy ordenados por nome de arquivo e concatenamos os vetores para criar big_npy. (Este vetor tem a forma [N, 256].)
+Depois de salvar big_npy as /logs/nome-do-seu-modelo/total_fea.npy, treine-o com faiss.
+
+Neste artigo, explicarei o significado desses parâmetros.
+
+# Explicação do método
+## Fábrica de Index
+Uma fábrica de Index é uma notação faiss exclusiva que expressa um pipeline que conecta vários métodos de pesquisa de área aproximados como uma string.
+Isso permite que você experimente vários métodos aproximados de pesquisa de área simplesmente alterando a cadeia de caracteres de fábrica do Index.
+No RVC é usado assim:
+
+```python
+index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)
+```
+Entre os argumentos de index_factory, o primeiro é o número de dimensões do vetor, o segundo é a string de fábrica do Index e o terceiro é a distância a ser usada.
+
+Para uma notação mais detalhada
+https://github.com/facebookresearch/faiss/wiki/The-index-factory
+
+## Construção de Index
+Existem dois Indexs típicos usados como similaridade de incorporação da seguinte forma.
+
+- Distância euclidiana (MÉTRICA_L2)
+- Produto interno (METRIC_INNER_PRODUCT)
+
+A distância euclidiana toma a diferença quadrática em cada dimensão, soma as diferenças em todas as dimensões e, em seguida, toma a raiz quadrada. Isso é o mesmo que a distância em 2D e 3D que usamos diariamente.
+O produto interno não é usado como um Index de similaridade como é, e a similaridade de cosseno que leva o produto interno depois de ser normalizado pela norma L2 é geralmente usada.
+
+O que é melhor depende do caso, mas a similaridade de cosseno é frequentemente usada na incorporação obtida pelo word2vec e modelos de recuperação de imagem semelhantes aprendidos pelo ArcFace. Se você quiser fazer a normalização l2 no vetor X com numpy, você pode fazê-lo com o seguinte código com eps pequeno o suficiente para evitar a divisão 0.
+
+```python
+X_normed = X / np.maximum(eps, np.linalg.norm(X, ord=2, axis=-1, keepdims=True))
+```
+
+Além disso, para a Construção de Index, você pode alterar o Index de distância usado para cálculo escolhendo o valor a ser passado como o terceiro argumento.
+
+```python
+index = faiss.index_factory(dimention, text, faiss.METRIC_INNER_PRODUCT)
+```
+
+## FI
+IVF (Inverted file indexes) é um algoritmo semelhante ao Index invertido na pesquisa de texto completo.
+Durante o aprendizado, o destino da pesquisa é agrupado com kmeans e o particionamento Voronoi é realizado usando o centro de cluster. A cada ponto de dados é atribuído um cluster, por isso criamos um dicionário que procura os pontos de dados dos clusters.
+
+Por exemplo, se os clusters forem atribuídos da seguinte forma
+|index|Cluster|
+|-----|-------|
+|1|A|
+|2|B|
+|3|A|
+|4|C|
+|5|B|
+
+O Index invertido resultante se parece com isso:
+
+| cluster | Index |
+|-------|-----|
+| A | 1, 3 |
+| B | 2 5 |
+| C | 4 |
+
+Ao pesquisar, primeiro pesquisamos n_probe clusters dos clusters e, em seguida, calculamos as distâncias para os pontos de dados pertencentes a cada cluster.
+
+# Parâmetro de recomendação
+Existem diretrizes oficiais sobre como escolher um Index, então vou explicar de
+acordo. https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index
+
+Para conjuntos de dados abaixo de 1M, o 4bit-PQ é o método mais eficiente disponível no faiss em abril de 2023.
+Combinando isso com a fertilização in vitro, estreitando os candidatos com 4bit-PQ e, finalmente, recalcular a distância com um Index preciso pode ser descrito usando a seguinte fábrica de Indexs.
+
+```python
+index = faiss.index_factory(256, "IVF1024,PQ128x4fs,RFlat")
+```
+
+## Parâmetros recomendados para FIV
+Considere o caso de muitas FIVs. Por exemplo, se a quantização grosseira por FIV for realizada para o número de dados, isso é o mesmo que uma pesquisa exaustiva ingênua e é ineficiente.
+Para 1M ou menos, os valores de FIV são recomendados entre 4*sqrt(N) ~ 16*sqrt(N) para N número de pontos de dados.
+
+Como o tempo de cálculo aumenta proporcionalmente ao número de n_sondas, consulte a precisão e escolha adequadamente. Pessoalmente, não acho que o RVC precise de tanta precisão, então n_probe = 1 está bem.
+
+## FastScan
+O FastScan é um método que permite a aproximação de alta velocidade de distâncias por quantização de produto cartesiano, realizando-as em registros.
+A quantização cartesiana do produto executa o agrupamento independentemente para cada dimensão d (geralmente d = 2) durante o aprendizado, calcula a distância entre os agrupamentos com antecedência e cria uma tabela de pesquisa. No momento da previsão, a distância de cada dimensão pode ser calculada em O(1) olhando para a tabela de pesquisa.
+Portanto, o número que você especifica após PQ geralmente especifica metade da dimensão do vetor.
+
+Para uma descrição mais detalhada do FastScan, consulte a documentação oficial.
+https://github.com/facebookresearch/faiss/wiki/Fast-accumulation-of-PQ-and-AQ-codes-(FastScan)
+
+## RFlat
+RFlat é uma instrução para recalcular a distância aproximada calculada pelo FastScan com a distância exata especificada pelo terceiro argumento da Construção de Index.
+Ao obter áreas k, os pontos k*k_factor são recalculados.
diff --git a/docs/pt/faq_pt.md b/docs/pt/faq_pt.md
new file mode 100644
index 0000000..0b52470
--- /dev/null
+++ b/docs/pt/faq_pt.md
@@ -0,0 +1,224 @@
+# FAQ AI HUB BRASIL
+## O que é epoch, quantos utilizar, quanto de dataset utilizar e qual à configuração interessante?
+Epochs basicamente quantas vezes o seu dataset foi treinado.
+
+Recomendado ler Q8 e Q9 no final dessa página pra entender mais sobre dataset e epochs
+
+__**Não é uma regra, mas opinião:**__
+
+### **Mangio-Crepe Hop Length**
+- 64 pra cantores e dubladores
+- 128(padrão) para os demais (editado)
+
+### **Epochs e dataset**
+600epoch para cantores - --dataset entre 10 e 50 min desnecessario mais que 50 minutos--
+300epoch para os demais - --dataset entre 10 e 50 min desnecessario mais que 50 minutos--
+
+### **Tom**
+magio-crepe se for audios extraído de alguma musica
+harvest se for de estúdio
+
+## O que é index?
+Basicamente o que define o sotaque. Quanto maior o numero, mas próximo o sotaque fica do original. Porém, quando o modelo é bem, não é necessário um index.
+
+## O que significa cada sigla (pm, harvest, crepe, magio-crepe, RMVPE)?
+
+- pm = extração mais rápida, mas discurso de qualidade inferior;
+- harvest = graves melhores, mas extremamente lentos;
+- dio = conversão rápida mas pitch ruim;
+- crepe = melhor qualidade, mas intensivo em GPU;
+- crepe-tiny = mesma coisa que o crepe, só que com a qualidade um pouco inferior;
+- **mangio-crepe = melhor qualidade, mais otimizado; (MELHOR OPÇÃO)**
+- mangio-crepe-tiny = mesma coisa que o mangio-crepe, só que com a qualidade um pouco inferior;
+- RMVPE: um modelo robusto para estimativa de afinação vocal em música polifônica;
+
+## Pra rodar localmente, quais os requisitos minimos?
+Já tivemos relatos de pessoas com GTX 1050 rodando inferencia, se for treinar numa 1050 vai demorar muito mesmo e inferior a isso, normalmente da tela azul
+
+O mais importante é placa de vídeo, vram na verdade
+Se você tiver 4GB ou mais, você tem uma chance.
+
+**NOS DOIS CASOS NÃO É RECOMENDADO UTILIZAR O PC ENQUANTO ESTÁ UTILIZNDO, CHANCE DE TELA AZUL É ALTA**
+### Inference
+Não é algo oficial para requisitos minimos
+- Placa de vídeo: nvidia de 4gb
+- Memoria ram: 8gb
+- CPU: ?
+- Armanezamento: 20gb (sem modelos)
+
+### Treinamento de voz
+Não é algo oficial para requisitos minimos
+- Placa de vídeo: nvidia de 6gb
+- Memoria ram: 16gb
+- CPU: ?
+- Armanezamento: 20gb (sem modelos)
+
+## Limite de GPU no Google Colab excedido, apenas CPU o que fazer?
+Recomendamos esperar outro dia pra liberar mais 15gb ou 12 horas pra você. Ou você pode contribuir com o Google pagando algum dos planos, ai aumenta seu limite.
+Utilizar apenas CPU no Google Colab demora DEMAIS.
+
+
+## Google Colab desconectando com muita frequencia, o que fazer?
+Neste caso realmente não tem muito o que fazer. Apenas aguardar o proprietário do código corrigir ou a gente do AI HUB Brasil achar alguma solução. Isso acontece por diversos motivos, um incluindo a Google barrando o treinamento de voz.
+
+## O que é Batch Size/Tamanho de lote e qual numero utilizar?
+Batch Size/Tamanho do lote é basicamente quantos epoch faz ao mesmo tempo. Se por 20, ele fazer 20 epoch ao mesmo tempo e isso faz pesar mais na máquina e etc.
+
+No Google Colab você pode utilizar até 20 de boa.
+Se rodando localmente, depende da sua placa de vídeo, começa por baixo (6) e vai testando.
+
+## Sobre backup na hora do treinamento
+Backup vai de cada um. Eu quando uso a ``easierGUI`` utilizo a cada 100 epoch (meu caso isolado).
+No colab, se instavel, coloque a cada 10 epoch
+Recomendo utilizarem entre 25 e 50 pra garantir.
+
+Lembrando que cada arquivo geral é por volta de 50mb, então tenha muito cuidado quanto você coloca. Pois assim pode acabar lotando seu Google Drive ou seu PC.
+
+Depois de finalizado, da pra apagar os epoch de backup.
+
+## Como continuar da onde parou pra fazer mais epochs?
+Primeira coisa que gostaria de lembrar, não necessariamente quanto mais epochs melhor. Se fizer epochs demais vai dar **overtraining** o que pode ser ruim.
+
+### GUI NORMAL
+- Inicie normalmente a GUI novamente.
+- Na aba de treino utilize o MESMO nome que estava treinando, assim vai continuar o treino onde parou o ultimo backup.
+- Ignore as opções ``Processar o Conjunto de dados`` e ``Extrair Tom``
+- Antes de clicar pra treinar, arrume os epoch, bakcup e afins.
+ - Obviamente tem que ser um numero maior do qu estava em epoch.
+ - Backup você pode aumentar ou diminuir
+- Agora você vai ver a opção ``Carregue o caminho G do modelo base pré-treinado:`` e ``Carregue o caminho D do modelo base pré-treinado:``
+ -Aqui você vai por o caminho dos modelos que estão em ``./logs/minha-voz``
+ - Vai ficar algo parecido com isso ``e:/RVC/logs/minha-voz/G_0000.pth`` e ``e:/RVC/logs/minha-voz/D_0000.pth``
+-Coloque pra treinar
+
+**Lembrando que a pasta logs tem que ter todos os arquivos e não somente o arquivo ``G`` e ``D``**
+
+### EasierGUI
+- Inicie normalmente a easierGUI novamente.
+- Na aba de treino utilize o MESMO nome que estava treinando, assim vai continuar o treino onde parou o ultimo backup.
+- Selecione 'Treinar modelo', pode pular os 2 primeiros passos já que vamos continuar o treino.
+
+
+# FAQ Original traduzido
+## Q1: erro ffmpeg/erro utf8.
+Provavelmente não é um problema do FFmpeg, mas sim um problema de caminho de áudio;
+
+O FFmpeg pode encontrar um erro ao ler caminhos contendo caracteres especiais como spaces e (), o que pode causar um erro FFmpeg; e quando o áudio do conjunto de treinamento contém caminhos chineses, gravá-lo em filelist.txt pode causar um erro utf8.
+
+## Q2:Não é possível encontrar o arquivo de Index após "Treinamento com um clique".
+Se exibir "O treinamento está concluído. O programa é fechado ", então o modelo foi treinado com sucesso e os erros subsequentes são falsos;
+
+A falta de um arquivo de index 'adicionado' após o treinamento com um clique pode ser devido ao conjunto de treinamento ser muito grande, fazendo com que a adição do index fique presa; isso foi resolvido usando o processamento em lote para adicionar o index, o que resolve o problema de sobrecarga de memória ao adicionar o index. Como solução temporária, tente clicar no botão "Treinar Index" novamente.
+
+## Q3:Não é possível encontrar o modelo em “Modelo de voz” após o treinamento
+Clique em "Atualizar lista de voz" ou "Atualizar na EasyGUI e verifique novamente; se ainda não estiver visível, verifique se há erros durante o treinamento e envie capturas de tela do console, da interface do usuário da Web e dos ``logs/experiment_name/*.log`` para os desenvolvedores para análise posterior.
+
+## Q4:Como compartilhar um modelo/Como usar os modelos dos outros?
+Os arquivos ``.pth`` armazenados em ``*/logs/minha-voz`` não são destinados para compartilhamento ou inference, mas para armazenar os checkpoits do experimento para reprodutibilidade e treinamento adicional. O modelo a ser compartilhado deve ser o arquivo ``.pth`` de 60+MB na pasta **weights**;
+
+No futuro, ``weights/minha-voz.pth`` e ``logs/minha-voz/added_xxx.index`` serão mesclados em um único arquivo de ``weights/minha-voz.zip`` para eliminar a necessidade de entrada manual de index; portanto, compartilhe o arquivo zip, não somente o arquivo .pth, a menos que você queira continuar treinando em uma máquina diferente;
+
+Copiar/compartilhar os vários arquivos .pth de centenas de MB da pasta de logs para a pasta de weights para inference forçada pode resultar em erros como falta de f0, tgt_sr ou outras chaves. Você precisa usar a guia ckpt na parte inferior para manualmente ou automaticamente (se as informações forem encontradas nos ``logs/minha-voz``), selecione se deseja incluir informações de tom e opções de taxa de amostragem de áudio de destino e, em seguida, extrair o modelo menor. Após a extração, haverá um arquivo pth de 60+ MB na pasta de weights, e você pode atualizar as vozes para usá-lo.
+
+## Q5 Erro de conexão:
+Para sermos otimistas, aperte F5/recarregue a página, pode ter sido apenas um bug da GUI
+
+Se não...
+Você pode ter fechado o console (janela de linha de comando preta).
+Ou o Google Colab, no caso do Colab, as vezes pode simplesmente fechar
+
+## Q6: Pop-up WebUI 'Valor esperado: linha 1 coluna 1 (caractere 0)'.
+Desative o proxy LAN do sistema/proxy global e atualize.
+
+## Q7:Como treinar e inferir sem a WebUI?
+Script de treinamento:
+
Você pode executar o treinamento em WebUI primeiro, e as versões de linha de comando do pré-processamento e treinamento do conjunto de dados serão exibidas na janela de mensagens.
+
+Script de inference:
+
https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/myinfer.py
+
+
+por exemplo
+
+``runtime\python.exe myinfer.py 0 "E:\audios\1111.wav" "E:\RVC\logs\minha-voz\added_IVF677_Flat_nprobe_7.index" harvest "test.wav" "weights/mi-test.pth" 0.6 cuda:0 True``
+
+
+f0up_key=sys.argv[1]
+input_path=sys.argv[2]
+index_path=sys.argv[3]
+f0method=sys.argv[4]#harvest or pm
+opt_path=sys.argv[5]
+model_path=sys.argv[6]
+index_rate=float(sys.argv[7])
+device=sys.argv[8]
+is_half=bool(sys.argv[9])
+
+## Q8: Erro Cuda/Cuda sem memória.
+Há uma pequena chance de que haja um problema com a configuração do CUDA ou o dispositivo não seja suportado; mais provavelmente, não há memória suficiente (falta de memória).
+
+Para treinamento, reduza o (batch size) tamanho do lote (se reduzir para 1 ainda não for suficiente, talvez seja necessário alterar a placa gráfica); para inference, ajuste as configurações x_pad, x_query, x_center e x_max no arquivo config.py conforme necessário. Cartões de memória 4G ou inferiores (por exemplo, 1060(3G) e várias placas 2G) podem ser abandonados, enquanto os placas de vídeo com memória 4G ainda têm uma chance.
+
+## Q9:Quantos total_epoch são ótimos?
+Se a qualidade de áudio do conjunto de dados de treinamento for ruim e o nível de ruído for alto, **20-30 epochs** são suficientes. Defini-lo muito alto não melhorará a qualidade de áudio do seu conjunto de treinamento de baixa qualidade.
+
+Se a qualidade de áudio do conjunto de treinamento for alta, o nível de ruído for baixo e houver duração suficiente, você poderá aumentá-lo. **200 é aceitável** (uma vez que o treinamento é rápido e, se você puder preparar um conjunto de treinamento de alta qualidade, sua GPU provavelmente poderá lidar com uma duração de treinamento mais longa sem problemas).
+
+## Q10:Quanto tempo de treinamento é necessário?
+
+**Recomenda-se um conjunto de dados de cerca de 10 min a 50 min.**
+
+Com garantia de alta qualidade de som e baixo ruído de fundo, mais pode ser adicionado se o timbre do conjunto de dados for uniforme.
+
+Para um conjunto de treinamento de alto nível (limpo + distintivo), 5min a 10min é bom.
+
+Há algumas pessoas que treinaram com sucesso com dados de 1 a 2 minutos, mas o sucesso não é reproduzível por outros e não é muito informativo.
Isso requer que o conjunto de treinamento tenha um timbre muito distinto (por exemplo, um som de menina de anime arejado de alta frequência) e a qualidade do áudio seja alta;
+Dados com menos de 1 minuto, já obtivemo sucesso. Mas não é recomendado.
+
+
+## Q11:Qual é a taxa do index e como ajustá-la?
+Se a qualidade do tom do modelo pré-treinado e da fonte de inference for maior do que a do conjunto de treinamento, eles podem trazer a qualidade do tom do resultado do inference, mas ao custo de um possível viés de tom em direção ao tom do modelo subjacente/fonte de inference, em vez do tom do conjunto de treinamento, que é geralmente referido como "vazamento de tom".
+
+A taxa de index é usada para reduzir/resolver o problema de vazamento de timbre. Se a taxa do index for definida como 1, teoricamente não há vazamento de timbre da fonte de inference e a qualidade do timbre é mais tendenciosa em relação ao conjunto de treinamento. Se o conjunto de treinamento tiver uma qualidade de som mais baixa do que a fonte de inference, uma taxa de index mais alta poderá reduzir a qualidade do som. Reduzi-lo a 0 não tem o efeito de usar a mistura de recuperação para proteger os tons definidos de treinamento.
+
+Se o conjunto de treinamento tiver boa qualidade de áudio e longa duração, aumente o total_epoch, quando o modelo em si é menos propenso a se referir à fonte inferida e ao modelo subjacente pré-treinado, e há pouco "vazamento de tom", o index_rate não é importante e você pode até não criar/compartilhar o arquivo de index.
+
+## Q12:Como escolher o GPU ao inferir?
+No arquivo ``config.py``, selecione o número da placa em "device cuda:".
+
+O mapeamento entre o número da placa e a placa gráfica pode ser visto na seção de informações da placa gráfica da guia de treinamento.
+
+## Q13:Como usar o modelo salvo no meio do treinamento?
+Salvar via extração de modelo na parte inferior da guia de processamento do ckpt.
+
+## Q14: Erro de arquivo/memória (durante o treinamento)?
+Muitos processos e sua memória não é suficiente. Você pode corrigi-lo por:
+
+1. Diminuir a entrada no campo "Threads da CPU".
+2. Diminuir o tamanho do conjunto de dados.
+
+## Q15: Como continuar treinando usando mais dados
+
+passo 1: coloque todos os dados wav no path2.
+
+etapa 2: exp_name2 + path2 -> processar conjunto de dados e extrair recurso.
+
+passo 3: copie o arquivo G e D mais recente de exp_name1 (seu experimento anterior) para a pasta exp_name2.
+
+passo 4: clique em "treinar o modelo" e ele continuará treinando desde o início da época anterior do modelo exp.
+
+## Q16: erro sobre llvmlite.dll
+
+OSError: Não foi possível carregar o arquivo de objeto compartilhado: llvmlite.dll
+
+FileNotFoundError: Não foi possível encontrar o módulo lib\site-packages\llvmlite\binding\llvmlite.dll (ou uma de suas dependências). Tente usar o caminho completo com sintaxe de construtor.
+
+O problema acontecerá no Windows, instale https://aka.ms/vs/17/release/vc_redist.x64.exe e será corrigido.
+
+## Q17: RuntimeError: O tamanho expandido do tensor (17280) deve corresponder ao tamanho existente (0) na dimensão 1 não singleton. Tamanhos de destino: [1, 17280]. Tamanhos de tensor: [0]
+
+Exclua os arquivos wav cujo tamanho seja significativamente menor que outros e isso não acontecerá novamente. Em seguida, clique em "treinar o modelo" e "treinar o índice".
+
+## Q18: RuntimeError: O tamanho do tensor a (24) deve corresponder ao tamanho do tensor b (16) na dimensão não singleton 2
+
+Não altere a taxa de amostragem e continue o treinamento. Caso seja necessário alterar, o nome do exp deverá ser alterado e o modelo será treinado do zero. Você também pode copiar o pitch e os recursos (pastas 0/1/2/2b) extraídos da última vez para acelerar o processo de treinamento.
+
diff --git a/docs/pt/training_tips_pt.md b/docs/pt/training_tips_pt.md
new file mode 100644
index 0000000..985ab4f
--- /dev/null
+++ b/docs/pt/training_tips_pt.md
@@ -0,0 +1,65 @@
+Instruções e dicas para treinamento RVC
+======================================
+Estas DICAS explicam como o treinamento de dados é feito.
+
+# Fluxo de treinamento
+Explicarei ao longo das etapas na guia de treinamento da GUI.
+
+## Passo 1
+Defina o nome do experimento aqui.
+
+Você também pode definir aqui se o modelo deve levar em consideração o pitch.
+Se o modelo não considerar o tom, o modelo será mais leve, mas não será adequado para cantar.
+
+Os dados de cada experimento são colocados em `/logs/nome-do-seu-modelo/`.
+
+## Passo 2a
+Carrega e pré-processa áudio.
+
+### Carregar áudio
+Se você especificar uma pasta com áudio, os arquivos de áudio dessa pasta serão lidos automaticamente.
+Por exemplo, se você especificar `C:Users\hoge\voices`, `C:Users\hoge\voices\voice.mp3` será carregado, mas `C:Users\hoge\voices\dir\voice.mp3` será Não carregado.
+
+Como o ffmpeg é usado internamente para leitura de áudio, se a extensão for suportada pelo ffmpeg, ela será lida automaticamente.
+Após converter para int16 com ffmpeg, converta para float32 e normalize entre -1 e 1.
+
+### Eliminar ruído
+O áudio é suavizado pelo filtfilt do scipy.
+
+### Divisão de áudio
+Primeiro, o áudio de entrada é dividido pela detecção de partes de silêncio que duram mais que um determinado período (max_sil_kept=5 segundos?). Após dividir o áudio no silêncio, divida o áudio a cada 4 segundos com uma sobreposição de 0,3 segundos. Para áudio separado em 4 segundos, após normalizar o volume, converta o arquivo wav para `/logs/nome-do-seu-modelo/0_gt_wavs` e, em seguida, converta-o para taxa de amostragem de 16k para `/logs/nome-do-seu-modelo/1_16k_wavs ` como um arquivo wav.
+
+## Passo 2b
+### Extrair pitch
+Extraia informações de pitch de arquivos wav. Extraia as informações de pitch (=f0) usando o método incorporado em Parselmouth ou pyworld e salve-as em `/logs/nome-do-seu-modelo/2a_f0`. Em seguida, converta logaritmicamente as informações de pitch para um número inteiro entre 1 e 255 e salve-as em `/logs/nome-do-seu-modelo/2b-f0nsf`.
+
+### Extrair feature_print
+Converta o arquivo wav para incorporação antecipadamente usando HuBERT. Leia o arquivo wav salvo em `/logs/nome-do-seu-modelo/1_16k_wavs`, converta o arquivo wav em recursos de 256 dimensões com HuBERT e salve no formato npy em `/logs/nome-do-seu-modelo/3_feature256`.
+
+## Passo 3
+treinar o modelo.
+### Glossário para iniciantes
+No aprendizado profundo, o conjunto de dados é dividido e o aprendizado avança aos poucos. Em uma atualização do modelo (etapa), os dados batch_size são recuperados e previsões e correções de erros são realizadas. Fazer isso uma vez para um conjunto de dados conta como um epoch.
+
+Portanto, o tempo de aprendizagem é o tempo de aprendizagem por etapa x (o número de dados no conjunto de dados/tamanho do lote) x o número de epoch. Em geral, quanto maior o tamanho do lote, mais estável se torna o aprendizado (tempo de aprendizado por etapa ÷ tamanho do lote) fica menor, mas usa mais memória GPU. A RAM da GPU pode ser verificada com o comando nvidia-smi. O aprendizado pode ser feito em pouco tempo aumentando o tamanho do lote tanto quanto possível de acordo com a máquina do ambiente de execução.
+
+### Especifique o modelo pré-treinado
+O RVC começa a treinar o modelo a partir de pesos pré-treinados em vez de 0, para que possa ser treinado com um pequeno conjunto de dados.
+
+Por padrão
+
+- Se você considerar o pitch, ele carrega `rvc-location/pretrained/f0G40k.pth` e `rvc-location/pretrained/f0D40k.pth`.
+- Se você não considerar o pitch, ele carrega `rvc-location/pretrained/f0G40k.pth` e `rvc-location/pretrained/f0D40k.pth`.
+
+Ao aprender, os parâmetros do modelo são salvos em `logs/nome-do-seu-modelo/G_{}.pth` e `logs/nome-do-seu-modelo/D_{}.pth` para cada save_every_epoch, mas especificando nesse caminho, você pode começar a aprender. Você pode reiniciar ou iniciar o treinamento a partir de weights de modelo aprendidos em um experimento diferente.
+
+### Index de aprendizado
+O RVC salva os valores de recursos do HuBERT usados durante o treinamento e, durante a inferência, procura valores de recursos que sejam semelhantes aos valores de recursos usados durante o aprendizado para realizar a inferência. Para realizar esta busca em alta velocidade, o index é aprendido previamente.
+Para aprendizagem de index, usamos a biblioteca de pesquisa de associação de áreas aproximadas faiss. Leia o valor do recurso `logs/nome-do-seu-modelo/3_feature256` e use-o para aprender o index, e salve-o como `logs/nome-do-seu-modelo/add_XXX.index`.
+
+(A partir da versão 20230428update, ele é lido do index e não é mais necessário salvar/especificar.)
+
+### Descrição do botão
+- Treinar modelo: Após executar o passo 2b, pressione este botão para treinar o modelo.
+- Treinar índice de recursos: após treinar o modelo, execute o aprendizado do index.
+- Treinamento com um clique: etapa 2b, treinamento de modelo e treinamento de index de recursos, tudo de uma vez.
\ No newline at end of file
diff --git a/docs/tr/Changelog_TR.md b/docs/tr/Changelog_TR.md
new file mode 100644
index 0000000..6139839
--- /dev/null
+++ b/docs/tr/Changelog_TR.md
@@ -0,0 +1,97 @@
+
+### 2023-08-13
+1- Düzenli hata düzeltmeleri
+- Minimum toplam epoch sayısını 1 olarak değiştirin ve minimum toplam epoch sayısını 2 olarak değiştirin
+- Ön eğitim modellerini kullanmama nedeniyle oluşan eğitim hatalarını düzeltin
+- Eşlik eden vokallerin ayrılmasından sonra grafik belleğini temizleyin
+- Faiss kaydetme yolu mutlak yoldan göreli yola değiştirilmiştir
+- Boşluk içeren yolu destekleyin (hem eğitim kümesi yolu hem de deney adı desteklenir ve artık hata rapor edilmez)
+- Filelist, zorunlu utf8 kodlamasını iptal eder
+- Gerçek zamanlı ses değişikliği sırasında faiss aramasından kaynaklanan CPU tüketim sorununu çözün
+
+2- Temel güncellemeler
+- Geçerli en güçlü açık kaynak vokal ton çıkarma modeli RMVPE'yi eğitin ve RVC eğitimi, çevrimdışı/gerçek zamanlı çıkarım için kullanın, PyTorch/Onnx/DirectML destekler
+- Pytorch_DML aracılığıyla AMD ve Intel grafik kartları için destek ekleyin
+
+(1) Gerçek zamanlı ses değişimi (2) Çıkarım (3) Vokal eşlik ayrımı (4) Şu anda desteklenmeyen eğitim, CPU eğitimine geçiş yapacaktır; Onnx_Dml ile gpu için RMVPE çıkarımını destekler
+
+
+### 2023-06-18
+- Yeni ön eğitilmiş v2 modeller: 32k ve 48k
+- F0 modeli çıkarım hatalarını düzeltme
+- Eğitim kümesi 1 saati aşarsa, özelliği şekil açısından küçültmek için otomatik minibatch-kmeans yapın, böylece indeks eğitimi, eklemesi ve araması çok daha hızlı olur.
+- Bir oyunca vokal2guitar huggingface alanı sağlama
+- Aykırı kısa kesim eğitim kümesi seslerini otomatik olarak silme
+- Onnx dışa aktarma sekmesi
+
+Başarısız deneyler:
+- ~~Özellik çıkarımı: zamansal özellik çıkarımı ekleme: etkili değil~~
+- ~~Özellik çıkarımı: PCAR boyut azaltma ekleme: arama daha yavaş~~
+- ~~Eğitim sırasında rastgele veri artırma: etkili değil~~
+
+Yapılacaklar listesi:
+- ~~Vocos-RVC (küçük vokoder): etkili değil~~
+- ~~Eğitim için Crepe desteği: RMVPE ile değiştirildi~~
+- ~~Yarı hassas Crepe çıkarımı: RMVPE ile değiştirildi. Ve zor gerçekleştirilebilir.~~
+- F0 düzenleyici desteği
+
+### 2023-05-28
+- v2 jupyter notebook, korece değişiklik günlüğü, bazı çevre gereksinimlerini düzeltme
+- Sesli olmayan ünsüz ve nefes koruma modu ekleme
+- Crepe-full ton algılama desteği ekleme
+- UVR5 vokal ayrımı: yankı kaldırma modelleri ve yankı kaldırma modelleri destekleme
+- İndeks adında deney adı ve sürüm ekleme
+- Toplu ses dönüşüm işleme ve UVR5 vokal ayrımı sırasında çıkış seslerinin ihracat formatını kullanıcıların manuel olarak seçmelerine olanak tanıma
+- v1 32k model eğitimi artık desteklenmiyor
+
+### 2023-05-13
+- Tek tıklamayla paketin eski sürümündeki çalışma zamanındaki gereksiz kodları temizleme: lib.infer_pack ve uvr5_pack
+- Eğitim seti ön işleme içindeki sahte çoklu işlem hatasını düzeltme
+- Harvest ton tanıma algoritması için ortanca filtre yarıçap ayarı ekleme
+- Çıkış sesi için örnek alma örneği için yeniden örnekleme desteği ekleme
+- Eğitim için "n_cpu" çoklu işlem ayarı, "f0 çıkarma" yerine "veri ön işleme ve f0 çıkarma" için değiştirildi
+- Günlükler klasörü altındaki indeks yollarını otomatik olarak tespit etme ve bir açılır liste işlevi sağlama
+- Sekme sayfasına "Sıkça Sorulan Sorular ve Cevaplar"ı ekleme (ayrıca github RVC wiki'ye de bakabilirsiniz)
+- Çıkarım sırasında aynı giriş sesi yolunu kullanırken harvest tonunu önbelleğe alma (amaç: harvest ton çıkarımı kullanırken, tüm işlem hattı uzun ve tekrarlayan bir ton çıkarım işlemi geçirecektir. Önbellekleme kullanılmazsa, farklı timbre, indeks ve ton ortanca filtreleme yarıçapı ayarlarıyla deney yapan kullanıcılar, ilk çıkarım sonrası çok acı verici bir bekleme süreci yaşayacaktır)
+
+### 2023-05-14
+- Girişin hacim zarfını çıktının hacim zarfıyla karıştırmak veya değiştirmek için girişin hacim zarfını kullanma (problemi "giriş sessizleştirme ve çıktı küçük
+
+ amplitüdlü gürültü" sorununu hafifletebilir. Giriş sesi arka plan gürültüsü yüksekse, açık olması önerilmez ve varsayılan olarak açık değildir (1 varsayılan olarak kapalı olarak kabul edilir)
+- Belirli bir frekansta filtreleme uygulama eğitim ve çıkarım için 50Hz'nin altındaki frekans bantları için
+- Pyworld'un varsayılan 80'den 50'ye minimum ton çıkarma sınırlamasını eğitim ve çıkarım için düşürme, 50-80Hz arasındaki erkek alçak seslerin sessizleştirilmemesine izin verme
+- WebUI, sistem yereli diline göre dil değiştirme (şu anda en_US, ja_JP, zh_CN, zh_HK, zh_SG, zh_TW'yi destekliyor; desteklenmeyen durumda varsayılan olarak en_US'ye geçer)
+- Belirli bir giriş sesi yolunu kullanırken harvest tonunu önbelleğe alma (amaç: harvest ton çıkarma kullanırken, tüm işlem hattı uzun ve tekrarlayan bir ton çıkarma süreci geçirecektir. Önbellekleme kullanılmazsa, farklı timbre, indeks ve ton ortanca filtreleme yarıçapı ayarlarıyla deney yapan kullanıcılar, ilk çıkarım sonrası çok acı verici bir bekleme süreci yaşayacaktır)
+
+### 2023-04-09 Güncellemesi
+- GPU kullanım oranını artırmak için eğitim parametrelerini düzeltme: A100, %25'ten yaklaşık %90'a, V100: %50'den yaklaşık %90'a, 2060S: %60'tan yaklaşık %85'e, P40: %25'ten yaklaşık %95'e; eğitim hızını önemli ölçüde artırma
+- Parametre değişti: toplam_batch_size artık GPU başına batch_size
+- Toplam_epoch değişti: maksimum sınırı 1000'e yükseltildi; varsayılan 10'dan 20'ye yükseltildi
+- ckpt çıkarımı ile çalma tanıma hatasını düzeltme, anormal çıkarım oluşturan
+- Dağıtılmış eğitimde her sıra için ckpt kaydetme sorununu düzeltme
+- Özellik çıkarımı için NaN özellik filtrelemesi uygulama
+- Sessiz giriş/çıkışın rastgele ünsüzler veya gürültü üretme sorununu düzeltme (eski modeller yeni bir veri kümesiyle tekrar eğitilmelidir)
+
+### 2023-04-16 Güncellemesi
+- Yerel gerçek zamanlı ses değiştirme mini-GUI'si ekleme, çift tıklayarak go-realtime_gui.bat ile başlayın
+- Eğitim ve çıkarım sırasında 50Hz'nin altındaki frekans bantlarını filtreleme uygulama
+- Pyworld'deki varsayılan 80'den 50'ye minimum ton çıkarma sınırlamasını eğitim ve çıkarım için düşürme, 50-80Hz arasındaki erkek alçak seslerin sessizleştirilmemesine izin verme
+- WebUI, sistem yereli diline göre dil değiştirme (şu anda en_US, ja_JP, zh_CN, zh_HK, zh_SG, zh_TW'yi destekliyor; desteklenmeyen durumda varsayılan olarak en_US'ye geçer)
+- Bazı GPU'ların tanınmasını düzeltme (örneğin, V100-16G tanınmama sorunu, P4 tanınmama sorunu)
+
+### 2023-04-28 Güncellemesi
+- Daha hızlı hız ve daha yüksek kalite için faiss indeks ayarlarını yükseltme
+- Toplam_npy bağımlılığını kaldırma; gelecekteki model paylaşımları için total_npy girdisi gerekmeyecek
+- 16-serisi GPU'lar için kısıtlamaları açma, 4GB VRAM GPU'lar için 4GB çıkarım ayarları sağlama
+- Belirli ses biçimlerine yönelik UVR5 vokal eşlik ayrımındaki hata düzeltme
+- Gerçek zamanlı ses değiştirme mini-GUI şimdi 40k dışı ve tembel ton modellerini destekler
+
+### Gelecekteki Planlar:
+Özellikler:
+- Her epoch kaydetmek için küçük modeller çıkar seçeneğini ekleme
+- Çıkarım sırasında çıkış seslerini belirtilen yolda ekstra mp3 olarak kaydetme seçeneğini ekleme
+- Birden fazla kişinin eğitim sekmesini destekleme (en fazla 4 kişiye kadar)
+
+Temel model:
+- Bozuk nefes seslerinin sorununu düzeltmek için nefes alma wav dosyalarını eğitim veri kümesine eklemek
+- Şu anda genişletilmiş bir şarkı veri kümesiyle temel model eğitimi yapıyoruz ve gelecekte yayınlanacak
diff --git a/docs/tr/README.tr.md b/docs/tr/README.tr.md
new file mode 100644
index 0000000..b8829ef
--- /dev/null
+++ b/docs/tr/README.tr.md
@@ -0,0 +1,148 @@
+
+
+
+
Çekme Temelli Ses Dönüşümü Web Arayüzü
+VITS'e dayalı kullanımı kolay bir Ses Dönüşümü çerçevesi.
+
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI)
+
+

+
+[](https://colab.research.google.com/github/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/Retrieval_based_Voice_Conversion_WebUI.ipynb)
+[](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/LICENSE)
+[](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/)
+
+[](https://discord.gg/HcsmBBGyVk)
+
+
+
+------
+[**Değişiklik Geçmişi**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/blob/main/docs/Changelog_TR.md) | [**SSS (Sıkça Sorulan Sorular)**](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/wiki/SSS-(Sıkça-Sorulan-Sorular))
+
+[**İngilizce**](../en/README.en.md) | [**中文简体**](../../README.md) | [**日本語**](../jp/README.ja.md) | [**한국어**](../kr/README.ko.md) ([**韓國語**](../kr/README.ko.han.md)) | [**Français**](../fr/README.fr.md) | [**Türkçe**](../tr/README.tr.md) | [**Português**](../pt/README.pt.md)
+
+Burada [Demo Video'muzu](https://www.bilibili.com/video/BV1pm4y1z7Gm/) izleyebilirsiniz!
+
+RVC Kullanarak Gerçek Zamanlı Ses Dönüşüm Yazılımı: [w-okada/voice-changer](https://github.com/w-okada/voice-changer)
+
+> Ön eğitim modeli için veri kümesi neredeyse 50 saatlik yüksek kaliteli VCTK açık kaynak veri kümesini kullanır.
+
+> Yüksek kaliteli lisanslı şarkı veri setleri telif hakkı ihlali olmadan kullanımınız için eklenecektir.
+
+> Lütfen daha büyük parametrelere, daha fazla eğitim verisine sahip RVCv3'ün ön eğitimli temel modeline göz atın; daha iyi sonuçlar, değişmeyen çıkarsama hızı ve daha az eğitim verisi gerektirir.
+
+## Özet
+Bu depo aşağıdaki özelliklere sahiptir:
++ Ton sızıntısını en aza indirmek için kaynak özelliğini en iyi çıkarımı kullanarak eğitim kümesi özelliği ile değiştirme;
++ Kolay ve hızlı eğitim, hatta nispeten zayıf grafik kartlarında bile;
++ Az miktarda veriyle bile nispeten iyi sonuçlar alın (>=10 dakika düşük gürültülü konuşma önerilir);
++ Timbraları değiştirmek için model birleştirmeyi destekleme (ckpt işleme sekmesi-> ckpt birleştir);
++ Kullanımı kolay Web arayüzü;
++ UVR5 modelini kullanarak hızla vokalleri ve enstrümanları ayırma.
++ En güçlü Yüksek tiz Ses Çıkarma Algoritması [InterSpeech2023-RMVPE](#Krediler) sessiz ses sorununu önlemek için kullanılır. En iyi sonuçları (önemli ölçüde) sağlar ve Crepe_full'den daha hızlı çalışır, hatta daha düşük kaynak tüketimi sağlar.
++ AMD/Intel grafik kartları hızlandırması desteklenir.
+
+## Ortamın Hazırlanması
+Aşağıdaki komutlar, Python sürümü 3.8 veya daha yüksek olan bir ortamda çalıştırılmalıdır.
+
+(Windows/Linux)
+İlk olarak ana bağımlılıkları pip aracılığıyla kurun:
+```bash
+# PyTorch ile ilgili temel bağımlılıkları kurun, zaten kuruluysa atlayın
+# Referans: https://pytorch.org/get-started/locally/
+pip install torch torchvision torchaudio
+
+# Windows + Nvidia Ampere Mimarisi(RTX30xx) için, https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI/issues/21 deneyime göre pytorch'a karşılık gelen cuda sürümünü belirtmeniz gerekebilir
+#pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
+```
+
+Sonra poetry kullanarak diğer bağımlılıkları kurabilirsiniz:
+```bash
+# Poetry bağımlılık yönetim aracını kurun, zaten kuruluysa atlayın
+# Referans: https://python-poetry.org/docs/#installation
+curl -sSL https://install.python-poetry.org | python3 -
+
+# Projeyi bağımlılıkları kurun
+poetry install
+```
+
+Ayrıca bunları pip kullanarak da kurabilirsiniz:
+```bash
+
+Nvidia grafik kartları için
+ pip install -r requirements.txt
+
+AMD/Intel grafik kartları için:
+ pip install -r requirements-dml.txt
+
+```
+
+------
+Mac kullanıcıları `run.sh` aracılığıyla bağımlılıkları kurabilir:
+```bash
+sh ./run.sh
+```
+
+## Diğer Ön Modellerin Hazırlanması
+RVC'nin çıkarım ve eğitim yapması için diğer ön modellere ihtiyacı vardır.
+
+Bu ön modelleri [Huggingface alanımızdan](https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main/) indirmeniz gerekecektir.
+
+İşte RVC'nin ihtiyaç duyduğu diğer ön modellerin ve dosyaların bir listesi:
+```bash
+./assets/hubert_base
+
+./assets/pretrained
+
+./assets/uvr5_weights
+
+V2 sürümü modelini test etmek isterseniz, ek özellikler indirmeniz gerekecektir.
+
+./assets/pretrained_v2
+
+V2 sürüm modelini test etmek isterseniz (v2 sürüm modeli, 9 katmanlı Hubert+final_proj'ün 256 boyutlu özelliğini 12 katmanlı Hubert'ün 768 boyutlu özelliğiyle değiştirmiştir ve 3 periyot ayırıcı eklemiştir), ek özellikleri indirmeniz gerekecektir.
+
+./assets/pretrained_v2
+
+Eğer Windows kullanıyorsanız, FFmpeg ve FFprobe kurulu değilse bu iki dosyayı da indirmeniz gerekebilir.
+ffmpeg.exe
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffmpeg.exe
+
+ffprobe.exe
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/ffprobe.exe
+
+En son SOTA RMVPE vokal ton çıkarma algoritmasını kullanmak istiyorsanız, RMVPE ağırlıklarını indirip RVC kök dizinine koymalısınız.
+
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.pt
+
+ AMD/Intel grafik kartları kullanıcıları için indirmeniz gereken:
+
+ https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/rmvpe.onnx
+
+```
+
+Daha sonra bu komutu kullanarak Webui'yi başlatabilirsiniz:
+```bash
+python webui.py
+```
+Windows veya macOS kullanıyorsanız, `RVC-beta.7z` dosyasını indirip çıkararak `go-webui.bat`i kullanarak veya macOS'ta `sh ./run.sh` kullanarak doğrudan RVC'yi kullanabilirsiniz.
+
+## Krediler
++ [ContentVec](https://github.com/auspicious3000/contentvec/)
++ [VITS](https://github.com/jaywalnut310/vits)
++ [HIFIGAN](https://github.com/jik876/hifi-gan)
++ [Gradio](https://github.com/gradio-app/gradio)
++ [FFmpeg](https://github.com/FFmpeg/FFmpeg)
++ [Ultimate Vocal Remover](https://github.com/Anjok07/ultimatevocalremovergui)
++ [audio-slicer](https://github.com/openvpi/audio-slicer)
++ [Vokal ton çıkarma:RMVPE](https://github.com/Dream-High/RMVPE)
+ + Ön eğitimli model [yxlllc](https://github.com/yxlllc/RMVPE) ve [RVC-Boss](https://github.com/RVC-Boss) tarafından eğitilip test edilmiştir.
+
+## Katkıda Bulunan Herkese Teşekkürler
+
+
+
+```
diff --git a/docs/tr/faiss_tips_tr.md b/docs/tr/faiss_tips_tr.md
new file mode 100644
index 0000000..160f36c
--- /dev/null
+++ b/docs/tr/faiss_tips_tr.md
@@ -0,0 +1,104 @@
+
+# faiss Ayar İpuçları
+==================
+
+# faiss Hakkında
+faiss, yoğun vektörler için komşuluk aramalarının bir kütüphanesidir ve birçok yaklaşık komşuluk arama yöntemini verimli bir şekilde uygular. Facebook araştırma tarafından geliştirilen faiss, benzer vektörleri hızlı bir şekilde bulurken bazı doğruluğu feda eder.
+
+## RVC'de faiss Kullanımı
+RVC'de, HuBERT tarafından dönüştürülen özelliklerin gömülmesi için eğitim verisinden oluşturulan gömme ile benzer gömlemeleri ararız ve bunları karıştırarak orijinal konuşmaya daha yakın bir dönüşüm elde ederiz. Ancak bu arama basitçe yapıldığında zaman alır, bu nedenle yaklaşık komşuluk araması kullanarak yüksek hızlı dönüşüm sağlanır.
+
+# Uygulama Genel Bakış
+Modelin bulunduğu '/logs/your-experiment/3_feature256' dizininde, her ses verisinden HuBERT tarafından çıkarılan özellikler bulunur.
+Buradan, dosya adına göre sıralanmış npy dosyalarını okuyarak vektörleri birleştirip büyük_npy'yi oluştururuz. (Bu vektörün şekli [N, 256] şeklindedir.)
+Büyük_npy'yi /logs/your-experiment/total_fea.npy olarak kaydettikten sonra, onu faiss ile eğitiriz.
+
+Bu makalede, bu parametrelerin anlamını açıklayacağım.
+
+# Yöntemin Açıklaması
+## İndeks Fabrikası
+Bir indeks fabrikası, birden fazla yaklaşık komşuluk arama yöntemini bir dizi olarak bağlayan benzersiz bir faiss gösterimidir. Bu, indeks fabrikası dizesini değiştirerek basitçe çeşitli yaklaşık komşuluk arama yöntemlerini denemenizi sağlar.
+RVC'de bunu şu şekilde kullanırız:
+
+```python
+index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)
+```
+index_factory'nin argümanları arasında ilk vektör boyutu, ikinci indeks fabrikası dizesi ve üçüncü kullanılacak mesafe yer alır.
+
+Daha ayrıntılı gösterim için
+https://github.com/facebookresearch/faiss/wiki/The-index-factory
+
+## Mesafe İçin İndeks
+Aşağıdaki gibi gömme benzerliği olarak kullanılan iki tipik indeks bulunur.
+
+- Öklidyen mesafe (METRIC_L2)
+- iç çarpım (METRIC_INNER_PRODUCT)
+
+Öklidyen mesafe, her boyutta karesel farkı alır, tüm boyutlardaki farkları toplar ve ardından karekök alır. Bu, günlük hayatta kullandığımız 2D ve 3D'deki mesafeye benzer.
+İç çarpım, çoğunlukla L2 norm ile normalize edildikten sonra iç çarpımı alan ve genellikle kosinüs benzerliği olarak kullanılan bir benzerlik göstergesi olarak kullanılır.
+
+Hangisinin daha iyi olduğu duruma bağlıdır, ancak kosinüs benzerliği genellikle word2vec tarafından elde edilen gömme ve ArcFace tarafından öğrenilen benzer görüntü alım modellerinde kullanılır. Vektör X'i numpy ile l2 normalize yapmak isterseniz, 0 bölme hatasından kaçınmak için yeterince küçük bir eps ile şu kodu kullanabilirsiniz:
+
+```python
+X_normed = X / np.maximum(eps, np.linalg.norm(X, ord=2, axis=-1, keepdims=True))
+```
+
+Ayrıca, indeks fabrikası için üçüncü argüman olarak geçirilecek değeri seçerek hesaplamada kullanılan mesafe indeksini değiştirebilirsiniz.
+
+```python
+index = faiss.index_factory(dimention, text, faiss.METRIC_INNER_PRODUCT)
+```
+
+## IVF
+IVF (Ters dosya indeksleri), tam metin aramasındaki ters indeksle benzer bir algoritmadır.
+Öğrenme sırasında, arama hedefi kmeans ile kümelendirilir ve küme merkezi kullanılarak Voronoi bölütleme gerçekleştirilir. Her veri noktasına bir küme atanır, bu nedenle veri noktalarını kümeden arayan bir sözlük oluştururuz.
+
+Örneğin, kümelere aşağıdaki gibi atanmışsa
+|index|Cluster|
+|-----|-------|
+|1|A|
+|2|B|
+|3|A|
+|4|C|
+|5|B|
+
+Elde edilen ters indeks şu şekildedir:
+
+|cluster|index|
+|-------|-----|
+|A|1, 3|
+|B|2, 5|
+|C|4|
+
+Arama yaparken, önce kümeden n_probe küme ararız ve ardından her küme için ait veri noktalarının mesafelerini hesaplarız.
+
+# Tavsiye Edilen Parametreler
+Resmi olarak nasıl bir indeks seçileceği konusunda rehberler bulunmaktadır, bu nedenle buna uygun olarak açıklayacağım.
+https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index
+
+1M'den düşük veri kümeleri için, N sayısı için 4bit-PQ, Nisan 2023 itibariyle faiss'de mevcut en verimli yöntemdir.
+Bunu IVF ile birleştirerek adayları 4bit-PQ ile daraltmak ve nihayet doğru bir indeksle mesafeyi yeniden hesaplamak, aşağıdaki indeks fabrikas
+
+ını kullanarak açıklanabilir.
+
+```python
+index = faiss.index_factory(256, "IVF1024,PQ128x4fs,RFlat")
+```
+
+## IVF İçin Tavsiye Edilen Parametreler
+Çok sayıda IVF durumunu düşünün. Örneğin, veri sayısı için IVF tarafından kabaca nicelleme yapılırsa, bu basit bir tükenmez arama ile aynıdır ve verimsizdir.
+1M veya daha az için IVF değerleri, N veri noktaları için 4*sqrt(N) ~ 16*sqrt(N) arasında tavsiye edilir.
+
+Hesaplama süresi n_probes sayısına orantılı olarak arttığından, doğrulukla danışmanlık yapın ve uygun şekilde seçin. Kişisel olarak, RVC'nin bu kadar doğruluk gerektirmediğini düşünüyorum, bu nedenle n_probe = 1 uygundur.
+
+## FastScan
+FastScan, bunları kaydedicilerde gerçekleştirerek onları Kartez ürünü nicelleme ile hızlı yaklaşık mesafe sağlayan bir yöntemdir.
+Kartez ürünü nicelleme öğrenme sırasında her d boyut için (genellikle d = 2) kümeleme yapar, küme merkezlerini önceden hesaplar ve küme merkezleri arasındaki mesafeyi hesaplar ve bir arama tablosu oluşturur. Tahmin yaparken, her boyutun mesafesi arama tablosuna bakarak O(1) hesaplanabilir.
+PQ sonrası belirttiğiniz sayı genellikle vektörün yarısı olan boyutu belirtir.
+
+FastScan hakkında daha ayrıntılı açıklama için lütfen resmi belgelere başvurun.
+https://github.com/facebookresearch/faiss/wiki/Fast-accumulation-of-PQ-and-AQ-codes-(FastScan)
+
+## RFlat
+RFlat, FastScan ile hesaplanan kesirli mesafeyi indeks fabrikasının üçüncü argümanı tarafından belirtilen doğru mesafe ile yeniden hesaplamak için bir talimattır.
+k komşuları alırken, k*k_factor nokta yeniden hesaplanır.
diff --git a/docs/tr/faq_tr.md b/docs/tr/faq_tr.md
new file mode 100644
index 0000000..a856680
--- /dev/null
+++ b/docs/tr/faq_tr.md
@@ -0,0 +1,103 @@
+## Q1: FFmpeg Hatası/UTF8 Hatası
+Büyük olasılıkla bu bir FFmpeg sorunu değil, daha çok ses dosyası yolunda bir sorun;
+
+FFmpeg, boşluklar ve () gibi özel karakterler içeren yolları okurken bir hata ile karşılaşabilir; ve eğitim setinin ses dosyaları Çin karakterleri içeriyorsa, bunlar filelist.txt'ye yazıldığında utf8 hatasına neden olabilir.
+
+## Q2: "Tek Tıklamayla Eğitim" Sonrası İndeks Dosyası Bulunamıyor
+Eğer "Eğitim tamamlandı. Program kapatıldı." mesajını görüyorsa, model başarıyla eğitilmiş demektir ve sonraki hatalar sahte;
+
+"Added" dizini oluşturulduğu halde "Tek Tıklamayla Eğitim" sonrası indeks dosyası bulunamıyorsa, bu genellikle eğitim setinin çok büyük olmasından kaynaklanabilir ve indeksin eklenmesi sıkışabilir. Bu sorun indeks eklerken bellek yükünü azaltmak için toplu işlem yaparak çözülmüştür. Geçici bir çözüm olarak, "Eğitim İndeksini Eğit" düğmesine tekrar tıklamayı deneyin.
+
+## Q3: Eğitim Sonrası "Tonlama İnceleniyor" Bölümünde Model Bulunamıyor
+"Lanetleme İstemi Listesini Yenile" düğmesine tıklayarak tekrar kontrol edin; hala görünmüyorsa, eğitim sırasında herhangi bir hata olup olmadığını kontrol edin ve geliştiricilere daha fazla analiz için konsol, web arayüzü ve logs/experiment_name/*.log ekran görüntülerini gönderin.
+
+## Q4: Bir Model Nasıl Paylaşılır/Başkalarının Modelleri Nasıl Kullanılır?
+rvc_root/logs/experiment_name dizininde saklanan pth dosyaları paylaşım veya çıkarım için değildir, bunlar deney checkpoint'larıdır ve çoğaltılabilirlik ve daha fazla eğitim için saklanır. Paylaşılacak olan model, weights klasöründeki 60+MB'lık pth dosyası olmalıdır;
+
+Gelecekte, weights/exp_name.pth ve logs/exp_name/added_xxx.index birleştirilerek tek bir weights/exp_name.zip dosyasına dönüştürülecek ve manuel indeks girişi gereksinimini ortadan kaldıracaktır; bu nedenle pth dosyasını değil, farklı bir makinede eğitime devam etmek istemezseniz zip dosyasını paylaşın;
+
+Çıkarılmış modelleri zorlama çıkarım için logs klasöründen weights klasörüne birkaç yüz MB'lık pth dosyalarını kopyalamak/paylaşmak, eksik f0, tgt_sr veya diğer anahtarlar gibi hatalara neden olabilir. Smaller modeli manuel veya otomatik olarak çıkarmak için alttaki ckpt sekmesini kullanmanız gerekmektedir (eğer bilgi logs/exp_name içinde bulunuyorsa), pitch bilgisini ve hedef ses örnekleme oranı seçeneklerini seçmeli ve ardından daha küçük modele çıkarmalısınız. Çıkardıktan sonra weights klasöründe 60+ MB'lık bir pth dosyası olacaktır ve sesleri yeniden güncelleyebilirsiniz.
+
+## Q5: Bağlantı Hatası
+Büyük ihtimalle konsolu (siyah komut satırı penceresi) kapatmış olabilirsiniz.
+
+## Q6: Web Arayüzünde 'Beklenen Değer: Satır 1 Sütun 1 (Karakter 0)' Hatası
+Lütfen sistem LAN proxy/global proxy'sini devre dışı bırakın ve ardından sayfayı yenileyin.
+
+## Q7: WebUI Olmadan Nasıl Eğitim Yapılır ve Tahmin Yapılır?
+Eğitim komut dosyası:
+Önce WebUI'de eğitimi çalıştırabilirsiniz, ardından veri seti önişleme ve eğitiminin komut satırı sürümleri mesaj penceresinde görüntülenecektir.
+
+Tahmin komut dosyası:
+https://huggingface.co/lj1995/VoiceConversionWebUI/blob/main/myinfer.py
+
+
+örn:
+
+runtime\python.exe myinfer.py 0 "E:\codes\py39\RVC-beta\todo-songs\1111.wav" "E:\codes\py39\logs\mi-test\added_IVF677_Flat_nprobe_7.index" harvest "test.wav" "weights/mi-test.pth" 0.6 cuda:0 True
+
+
+f0up_key=sys.argv[1]
+input_path=sys.argv[2]
+index_path=sys.argv[3]
+f0method=sys.argv[4]#harvest or pm
+opt_path=sys.argv[5]
+model_path=sys.argv[6]
+index_rate=float(sys.argv[7])
+device=sys.argv[8]
+is_half=bool(sys.argv[9])
+
+## Q8: Cuda Hatası/Cuda Bellek Yetersizliği
+Küçük bir ihtimalle CUDA konfigürasyonunda bir problem olabilir veya cihaz desteklenmiyor olabilir; daha muhtemel olarak yetersiz bellek olabilir (bellek yetersizliği).
+
+Eğitim için toplu işlem boyutunu azaltın (1'e indirgemek yeterli değilse, grafik kartını değiştirmeniz gerekebilir); çıkarım için ise config.py dosyasındaki x_pad, x_query, x_center ve x_max ayarlarını ihtiyaca göre düzenleyin. 4GB veya daha düşük bellekli kartlar (örneğin 1060(3G) ve çeşit
+
+li 2GB kartlar) terk edilebilir, 4GB bellekli kartlar hala bir şansı vardır.
+
+## Q9: Optimal Olarak Kaç total_epoch Gerekli?
+Eğitim veri setinin ses kalitesi düşük ve gürültü seviyesi yüksekse, 20-30 dönem yeterlidir. Fazla yüksek bir değer belirlemek, düşük kaliteli eğitim setinizin ses kalitesini artırmaz.
+
+Eğitim setinin ses kalitesi yüksek, gürültü seviyesi düşük ve yeterli süre varsa, bu değeri artırabilirsiniz. 200 kabul edilebilir bir değerdir (çünkü eğitim hızlıdır ve yüksek kaliteli bir eğitim seti hazırlayabiliyorsanız, GPU'nuz muhtemelen uzun bir eğitim süresini sorunsuz bir şekilde yönetebilir).
+
+## Q10: Kaç Dakika Eğitim Verisi Süresi Gerekli?
+
+10 ila 50 dakika arası bir veri seti önerilir.
+
+Garantili yüksek ses kalitesi ve düşük arka plan gürültüsü varsa, veri setinin tonlaması homojen ise daha fazlası eklenebilir.
+
+Yüksek seviyede bir eğitim seti (zarif ve belirgin tonlama), 5 ila 10 dakika arası uygundur.
+
+1 ila 2 dakika veri ile başarılı bir şekilde eğitim yapan bazı insanlar olsa da, başarı diğerleri tarafından tekrarlanabilir değil ve çok bilgilendirici değil. Bu, eğitim setinin çok belirgin bir tonlamaya sahip olmasını (örneğin yüksek frekansta havadar bir anime kız sesi gibi) ve ses kalitesinin yüksek olmasını gerektirir; 1 dakikadan daha kısa süreli veri denenmemiştir ve önerilmez.
+
+
+## Q11: İndeks Oranı Nedir ve Nasıl Ayarlanır?
+Eğer önceden eğitilmiş model ve tahmin kaynağının ton kalitesi, eğitim setinden daha yüksekse, tahmin sonucunun ton kalitesini yükseltebilirler, ancak altta yatan modelin/tahmin kaynağının tonu yerine eğitim setinin tonuna yönelik olası bir ton önyargısıyla sonuçlanır, bu genellikle "ton sızıntısı" olarak adlandırılır.
+
+İndeks oranı, ton sızıntı sorununu azaltmak/çözmek için kullanılır. İndeks oranı 1 olarak ayarlandığında, teorik olarak tahmin kaynağından ton sızıntısı olmaz ve ton kalitesi daha çok eğitim setine yönelik olur. Eğer eğitim seti, tahmin kaynağından daha düşük ses kalitesine sahipse, daha yüksek bir indeks oranı ses kalitesini azaltabilir. Oranı 0'a düşürmek, eğitim seti tonlarını korumak için getirme karıştırmasını kullanmanın etkisine sahip değildir.
+
+Eğer eğitim seti iyi ses kalitesine ve uzun süreye sahipse, total_epoch'u artırın. Model, tahmin kaynağına ve önceden eğitilmiş alt modeline daha az başvurduğunda ve "ton sızıntısı" daha az olduğunda, indeks oranı önemli değil ve hatta indeks dosyası oluşturmak/paylaşmak gerekli değildir.
+
+## Q12: Tahmin Yaparken Hangi GPU'yu Seçmeli?
+config.py dosyasında "device cuda:" ardından kart numarasını seçin.
+
+Kart numarası ile grafik kartı arasındaki eşleme, eğitim sekmesinin grafik kartı bilgileri bölümünde görülebilir.
+
+## Q13: Eğitimin Ortasında Kaydedilen Model Nasıl Kullanılır?
+Kaydetme işlemini ckpt işleme sekmesinin altında yer alan model çıkarımı ile yapabilirsiniz.
+
+## Q14: Dosya/Bellek Hatası (Eğitim Sırasında)?
+Çok fazla işlem ve yetersiz bellek olabilir. Bu sorunu düzeltebilirsiniz:
+
+1. "CPU İş Parçacıkları" alanındaki girişi azaltarak.
+
+2. Eğitim verisini daha kısa ses dosyalarına önceden keserek.
+
+## Q15: Daha Fazla Veri Kullanarak Eğitime Nasıl Devam Edilir?
+
+Adım 1: Tüm wav verilerini path2 dizinine yerleştirin.
+
+Adım 2: exp_name2+path2 -> veri setini önişleme ve özellik çıkarma.
+
+Adım 3: exp_name1 (önceki deneyinizin) en son G ve D dosyalarını exp_name2 klasörüne kopyalayın.
+
+Adım 4: "modeli eğit" düğmesine tıklayın ve önceki deneyinizin model döneminden başlayarak eğitime devam edecektir.
diff --git a/docs/tr/training_tips_tr.md b/docs/tr/training_tips_tr.md
new file mode 100644
index 0000000..144cd77
--- /dev/null
+++ b/docs/tr/training_tips_tr.md
@@ -0,0 +1,67 @@
+## RVC Eğitimi için Talimatlar ve İpuçları
+======================================
+Bu TALİMAT, veri eğitiminin nasıl yapıldığını açıklamaktadır.
+
+# Eğitim Akışı
+Eğitim sekmesindeki adımları takip ederek açıklayacağım.
+
+## Adım 1
+Deney adını burada belirleyin.
+
+Ayrıca burada modelin pitch'i dikkate alıp almayacağını da belirleyebilirsiniz.
+Eğer model pitch'i dikkate almazsa, model daha hafif olacak, ancak şarkı söyleme için uygun olmayacaktır.
+
+Her deney için veriler `/logs/your-experiment-name/` dizinine yerleştirilir.
+
+## Adım 2a
+Ses yüklenir ve ön işleme yapılır.
+
+### Ses Yükleme
+Ses içeren bir klasör belirtirseniz, bu klasördeki ses dosyaları otomatik olarak okunur.
+Örneğin, `C:Users\hoge\voices` belirtirseniz, `C:Users\hoge\voices\voice.mp3` yüklenecek, ancak `C:Users\hoge\voices\dir\voice.mp3` yüklenmeyecektir.
+
+Ses okumak için dahili olarak ffmpeg kullanıldığından, uzantı ffmpeg tarafından destekleniyorsa otomatik olarak okunacaktır.
+ffmpeg ile int16'ya dönüştürüldükten sonra float32'ye dönüştürülüp -1 ile 1 arasında normalize edilir.
+
+### Gürültü Temizleme
+Ses scipy'nin filtfilt işlevi ile yumuşatılır.
+
+### Ses Ayırma
+İlk olarak, giriş sesi belirli bir süreden (max_sil_kept=5 saniye?) daha uzun süren sessiz kısımları tespit ederek böler. Sessizlik üzerinde ses bölündükten sonra sesi 4 saniyede bir 0.3 saniyelik bir örtüşme ile böler. 4 saniye içinde ayrılan sesler için ses normalleştirildikten sonra wav dosyası olarak `/logs/your-experiment-name/0_gt_wavs`'a, ardından 16 kHz örnekleme hızına dönüştürülerek `/logs/your-experiment-name/1_16k_wavs` olarak kaydedilir.
+
+## Adım 2b
+### Pitch Çıkarımı
+Wav dosyalarından pitch bilgisi çıkarılır. ParSelMouth veya PyWorld'e dahili olarak yerleştirilmiş yöntemi kullanarak pitch bilgisi (=f0) çıkarılır ve `/logs/your-experiment-name/2a_f0` dizinine kaydedilir. Ardından pitch bilgisi logaritmik olarak 1 ile 255 arasında bir tamsayıya dönüştürülüp `/logs/your-experiment-name/2b-f0nsf` dizinine kaydedilir.
+
+### Özellik Çıkarımı
+HuBERT'i kullanarak önceden gömme olarak wav dosyasını çıkarır. `/logs/your-experiment-name/1_16k_wavs`'a kaydedilen wav dosyasını okuyarak, wav dosyasını 256 boyutlu HuBERT özelliklerine dönüştürür ve npy formatında `/logs/your-experiment-name/3_feature256` dizinine kaydeder.
+
+## Adım 3
+Modeli eğit.
+### Başlangıç Seviyesi Sözlüğü
+Derin öğrenmede, veri kümesi bölmeye ve öğrenmeye adım adım devam eder. Bir model güncellemesinde (adım), batch_size veri alınır ve tahminler ve hata düzeltmeleri yapılır. Bunun bir defa bir veri kümesi için yapılması bir dönem olarak sayılır.
+
+Bu nedenle, öğrenme zamanı adım başına öğrenme zamanı x (veri kümesindeki veri sayısı / batch boyutu) x dönem sayısıdır. Genel olarak, batch boyutu ne kadar büyükse, öğrenme daha istikrarlı hale gelir (adım başına öğrenme süresi ÷ batch boyutu) küçülür, ancak daha fazla GPU belleği kullanır. GPU RAM'ı nvidia-smi komutu ile kontrol edilebilir. Çalışma ortamının makinesine göre batch boyutunu mümkün olduğunca artırarak öğrenme süresini kısa sürede yapabilirsiniz.
+
+### Önceden Eğitilmiş Modeli Belirtme
+RVC, modeli 0'dan değil önceden eğitilmiş ağırlıklardan başlatarak eğitir, bu nedenle küçük bir veri kümesi ile eğitilebilir.
+
+Varsayılan olarak
+
+- Eğer pitch'i dikkate alıyorsanız, `rvc-location/pretrained/f0G40k.pth` ve `rvc-location/pretrained/f0D40k.pth` yüklenir.
+- Eğer pitch'i dikkate almıyorsanız, yine `rvc-location/pretrained/f0G40k.pth` ve `rvc-location/pretrained/f0D40k.pth` yüklenir.
+
+Öğrenirken model parametreleri her save_every_epoch için `logs/your-experiment-name/G_{}.pth` ve `logs/your-experiment-name/D_{}.pth` olarak kaydedilir, ancak bu yolu belirterek öğrenmeye başlayabilirsiniz. Farklı bir deneyde öğrenilen model ağırlıklarından öğrenmeye yeniden başlayabilir veya eğitimi başlatabilirsiniz.
+
+### Öğrenme İndeksi
+RVC, eğitim sırasında kullanılan HuBERT özellik değerlerini kaydeder ve çıkarım sırasında, öğrenme sırasında kullanılan özellik değerlerine benzer özellik değerlerini arayarak çıkarım yapar. Bu aramayı yüksek hızda gerçekleştirebilmek için indeks öğrenilir.
+İndeks öğrenimi için yaklaş
+
+ık komşuluk arama kütüphanesi faiss kullanılır. `/logs/your-experiment-name/3_feature256`'daki özellik değerini okur ve indeksi öğrenmek için kullanır, `logs/your-experiment-name/add_XXX.index` olarak kaydedilir.
+
+(20230428 güncelleme sürümünden itibaren indeks okunur ve kaydetmek/belirtmek artık gerekli değildir.)
+
+### Düğme Açıklaması
+- Modeli Eğit: Adım 2b'yi çalıştırdıktan sonra, modeli eğitmek için bu düğmeye basın.
+- Özellik İndeksini Eğit: Modeli eğittikten sonra, indeks öğrenme işlemi yapın.
+- Tek Tıklamayla Eğitim: Adım 2b, model eğitimi ve özellik indeks eğitimini bir arada yapar.
diff --git a/go-realtime_gui.bat b/go-realtime_gui.bat
new file mode 100644
index 0000000..b4cdcdb
--- /dev/null
+++ b/go-realtime_gui.bat
@@ -0,0 +1,8 @@
+@echo off
+chcp 65001 >nul
+set "SCRIPT_DIR=%~dp0"
+set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
+cd /d "%SCRIPT_DIR%"
+set "PATH=%SCRIPT_DIR%\runtime;%PATH%"
+runtime\python.exe -I realtime_gui.py
+pause
diff --git a/go-webui.bat b/go-webui.bat
new file mode 100644
index 0000000..decc099
--- /dev/null
+++ b/go-webui.bat
@@ -0,0 +1,8 @@
+@echo off
+chcp 65001 >nul
+set "SCRIPT_DIR=%~dp0"
+set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
+cd /d "%SCRIPT_DIR%"
+set "PATH=%SCRIPT_DIR%\runtime;%PATH%"
+runtime\python.exe -I webui.py --pycmd runtime\python.exe --port 7897
+pause
diff --git a/i18n/i18n.py b/i18n/i18n.py
new file mode 100644
index 0000000..9afa437
--- /dev/null
+++ b/i18n/i18n.py
@@ -0,0 +1,26 @@
+import json
+import locale
+import os
+from tools.file_io import read_text
+
+
+def load_language_list(language):
+ return json.loads(read_text(f"./i18n/locale/{language}.json"))
+
+
+class I18nAuto:
+ def __init__(self, language=None):
+ if language in ["Auto", None]:
+ language = locale.getdefaultlocale()[
+ 0
+ ] # getlocale can't identify the system's language ((None, None))
+ if not os.path.exists(f"./i18n/locale/{language}.json"):
+ language = "en_US"
+ self.language = language
+ self.language_map = load_language_list(language)
+
+ def __call__(self, key):
+ return self.language_map.get(key, key)
+
+ def __repr__(self):
+ return "Use Language: " + self.language
diff --git a/i18n/locale/en_US.json b/i18n/locale/en_US.json
new file mode 100644
index 0000000..ac2a73c
--- /dev/null
+++ b/i18n/locale/en_US.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "Vocal extraction aggressiveness",
+ "%s → 成功": "%s → Success",
+ "%s运行中,请先停止该任务": "%s is running; stop it before starting another task",
+ "%s进程已终止": "The %s process has been terminated",
+ "====> 轮次:{} {}": "====> Epoch: {} {}",
+ "A模型权重": "Weight (w) for Model A:",
+ "A模型路径": "Path to Model A:",
+ "B模型路径": "Path to Model B:",
+ "CUDA可用:%s": "CUDA available: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "F0 and HuBERT feature extraction",
+ "F0提取": "F0 extraction",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0 curve file (optional). One pitch per line. Replaces the default F0 and pitch modulation:",
+ "HuBERT特征": "HuBERT features",
+ "Index Rate": "Index Rate",
+ "RVC模型路径": "RVC Model Path:",
+ "SOLA偏移:%d": "SOLA offset: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0 extraction] Completed | Success: %s | Skipped: %s | Failed: %s",
+ "[F0提取] 待处理:%s": "[F0 extraction] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0 extraction] No pending audio; all files were skipped",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0 extraction] Progress: %s/%s | Success: %s | Skipped: %s | %s",
+ "[F0提取][失败] %s": "[F0 extraction][Failed] %s",
+ "[F0提取][失败] %s\n%s": "[F0 extraction][Failed] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT features] Completed | Success: %s | Skipped: %s | Failed: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT features] No pending audio; skipped: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT features] Loading model: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT features] Device: %s | Pending: %s | Skipped: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT features] Progress: %s/%s | Success: %s | Failed: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT features][Failed] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT features][Failed] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT features][Failed] Model not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[Data slicing] Worker completed | Success: %s | Failed: %s",
+ "[数据切分] 完成": "[Data slicing] Completed",
+ "[数据切分] 开始": "[Data slicing] Started",
+ "[数据切分] 待处理:%s | 进程数:%s": "[Data slicing] Pending: %s | Processes: %s",
+ "[数据切分] 进度:%s/%s | %s": "[Data slicing] Progress: %s/%s | %s",
+ "[数据切分][失败] %s": "[Data slicing][Failed] %s",
+ "[数据切分][失败] %s\n%s": "[Data slicing][Failed] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[Data slicing][Skipped] Invalid or abnormal audio segment: %s_%s | Peak: %s",
+ "[索引训练] 写入进度:%s/%s": "[Index training] Write progress: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[Index training] Linked index to external directory: %s",
+ "[索引训练] 成功构建索引:%s": "[Index training] Index built successfully: %s",
+ "[索引训练] 正在写入特征向量": "[Index training] Adding feature vectors",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[Index training] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[Index training] Training index",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[Index training] Feature shape: %s | IVF count: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[Index training][Failed] Could not link index to external directory: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[Index training][Failed] Clustering failed; continuing with the original features\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[Index training][Failed] Extract features first",
+ "ckpt处理": "ckpt Processing",
+ "index文件路径不可包含中文": "The index file path cannot contain Chinese characters",
+ "pth文件路径不可包含中文": "The .pth file path cannot contain Chinese characters",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "Enter the GPU index(es) separated by '-', e.g., 0-0-1 to use 2 processes in GPU0 and 1 process in GPU1",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "Step 1: Fill in the experimental configuration. Experimental data is stored in the 'logs' folder, with each experiment having a separate folder. Manually enter the experiment name path, which contains the experimental configuration, logs, and trained model files.",
+ "step1:正在处理数据": "Step 1: Processing data",
+ "step2:正在提取音高&正在提取特征": "step2:Pitch extraction & feature extraction",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "Step 2a: Automatically traverse all files in the training folder that can be decoded into audio and perform slice normalization. Generates 2 wav folders in the experiment directory. Currently, only single-singer/speaker training is supported.",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "Step 2b: Use CPU to extract pitch (if the model has pitch), use GPU to extract features (select GPU index):",
+ "step3: 填写训练设置, 开始训练模型和索引": "Step 3: Fill in the training settings and start training the model and index",
+ "step3a:正在训练模型": "Step 3a: Model training started",
+ "step3b:正在训练索引": "step 3b: training the index",
+ "……仅显示最近10条失败记录": "…Showing only the 10 most recent failures",
+ "……已省略前%s行,仅显示最新状态": "…Omitted the first %s lines; showing the latest status only",
+ "一键训练": "One-click training",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "Multiple audio files can also be imported. If a folder path exists, this input is ignored.",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "Batch vocal/accompaniment separation using UVR5 models.
You can choose a vocal-preserving model, or use DeEcho/DeReverb models to remove echo and reverb.",
+ "仅支持pm和rmvpe音高提取算法": "Only the pm and rmvpe pitch extraction methods are supported",
+ "从训练检查点提取的模型": "Model extracted from a training checkpoint",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "Enter the GPU index(es) separated by '-', e.g., 0-1-2 to use GPU 0, 1, and 2:",
+ "任务": "Task",
+ "伴奏人声分离&去混响&去回声": "Vocals/Accompaniment Separation & Reverberation Removal",
+ "使用显卡:%s": "GPUs in use: %s",
+ "使用模型采样率": "Use model sample rate",
+ "使用设备采样率": "Use device sample rate",
+ "保存名": "Save name:",
+ "保存的文件名, 默认空为和源文件同名": "Save file name (default: same as the source file):",
+ "保存的模型名不带后缀": "Saved model name (without extension):",
+ "保存频率save_every_epoch": "Save frequency (save_every_epoch):",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "Protect voiceless consonants and breath sounds to prevent artifacts such as tearing in electronic music. Set to 0.5 to disable. Decrease the value to increase protection, but it may reduce indexing accuracy:",
+ "修改": "Modify",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "Modify model information (only supported for small model files extracted from the 'weights' folder)",
+ "停止一键训练": "Stop one-click training",
+ "停止处理数据": "Stop data preprocessing",
+ "停止特征提取": "Stop feature extraction",
+ "停止训练模型": "Stop model training",
+ "停止训练索引": "Stop index training",
+ "停止音频转换": "Stop audio conversion",
+ "全流程结束!": "All processes have been completed!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "Load model",
+ "加载预训练底模D路径": "Load pre-trained base model D path:",
+ "加载预训练底模G路径": "Load pre-trained base model G path:",
+ "单次推理": "Single Inference",
+ "卸载音色省显存": "Unload voice to save GPU memory:",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "Transpose (integer, number of semitones, raise by an octave: 12, lower by an octave: -12):",
+ "合成": "Synthesis",
+ "后处理重采样至最终采样率,0为不进行重采样": "Resample the output audio in post-processing to the final sample rate. Set to 0 for no resampling:",
+ "否": "No",
+ "响应阈值": "Response threshold",
+ "响度因子": "loudness factor",
+ "处理中": "Processing",
+ "处理数据": "Process data",
+ "失败": "Failed",
+ "失败记录": "Failure records",
+ "子进程执行失败,返回码:%s": "Child process failed with exit code: %s",
+ "导出文件格式": "Export file format",
+ "已停止": "Stopped",
+ "已加载判别器预训练模型:%s": "Loaded discriminator pretrained model: %s",
+ "已加载生成器预训练模型:%s": "Loaded generator pretrained model: %s",
+ "已启用索引检索": "Index search enabled",
+ "已完成": "Completed",
+ "已恢复判别器检查点": "Restored discriminator checkpoint",
+ "常见问题解答": "FAQ (Frequently Asked Questions)",
+ "常规设置": "General settings",
+ "开始音频转换": "Start audio conversion",
+ "当前": "Current",
+ "当前阶段": "Current stage",
+ "很遗憾您这没有能用的显卡来支持您训练": "Unfortunately, there is no compatible GPU available to support your training.",
+ "性别因子/声线粗细": "Gender factor / voice thickness",
+ "性能设置": "Performance settings",
+ "总训练轮数total_epoch": "Total training epochs (total_epoch):",
+ "成功": "Success",
+ "执行命令": "Command",
+ "批量推理": "Batch Inference",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "Batch conversion. Enter the folder containing the audio files to be converted or upload multiple audio files. The converted audio will be output in the specified folder (default: 'opt').",
+ "拖拽或点击上传待处理音频": "Drag and drop or click to upload audio for processing",
+ "指定输出主人声文件夹": "Specify the output folder for vocals:",
+ "指定输出文件夹": "Specify output folder:",
+ "指定输出非主人声文件夹": "Specify the output folder for accompaniment:",
+ "推理时间(ms):": "Inference time (ms):",
+ "推理耗时:%.2f秒": "Inference time: %.2f seconds",
+ "推理音色": "Inferencing voice:",
+ "提取": "Extract",
+ "提取音高和处理数据使用的CPU进程数": "Number of CPU processes used for pitch extraction and data processing:",
+ "数据切分": "Data slicing",
+ "无法停止": "Cannot stop",
+ "无法读取音频:%s": "Could not read audio: %s",
+ "是": "Yes",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "Save only the latest '.ckpt' file to save disk space:",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "Save a small final model to the 'weights' folder at each save point:",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "Cache all training sets to GPU memory. Caching small datasets (less than 10 minutes) can speed up training, but caching large datasets will consume a lot of GPU memory and may not provide much speed improvement:",
+ "显卡信息": "GPU Information",
+ "未使用": "Not used",
+ "未使用判别器预训练模型": "Discriminator pretrained model not used",
+ "未使用生成器预训练模型": "Generator pretrained model not used",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Roformer model configuration not found; using the built-in defaults",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "No supported GPU detected; training on the CPU may take much longer",
+ "未运行": "Not running",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "This software is open source under the MIT license. The author does not have any control over the software. Users who use the software and distribute the sounds exported by the software are solely responsible.
If you do not agree with this clause, you cannot use or reference any codes and files within the software package. See the root directory Agreement-LICENSE.txt for details.",
+ "查看": "View",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "View model information (only supported for small model files extracted from the 'weights' folder)",
+ "检测到模型类型:%s": "Detected model type: %s",
+ "检索特征占比": "Search feature ratio (controls accent strength, too high has artifacting):",
+ "模型": "Model",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "Model information: %s\nSample rate: %s\nPitch guidance: %s\nVersion: %s",
+ "模型推理": "Model Inference",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "Model extraction (enter the path of the large file model under the 'logs' folder). This is useful if you want to stop training halfway and manually extract and save a small model file, or if you want to test an intermediate model:",
+ "模型是否带音高指导": "Whether the model has pitch guidance:",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "Whether the model has pitch guidance (required for singing, optional for speech):",
+ "模型是否带音高指导,1是0否": "Whether the model has pitch guidance (1: yes, 0: no):",
+ "模型版本型号": "Model architecture version:",
+ "模型融合, 可用于测试音色融合": "Model fusion, can be used to test timbre fusion",
+ "模型融合失败:两个模型的结构不一致": "Model merge failed: the two model architectures do not match",
+ "模型训练": "Model training",
+ "模型路径": "Path to Model:",
+ "正在保存最终检查点:%s": "Saving final checkpoint: %s",
+ "正在保存检查点 %s_e%s:%s": "Saving checkpoint %s_e%s: %s",
+ "正在加载RMVPE模型": "Loading RMVPE model",
+ "正在加载模型": "Loading model",
+ "正在启动": "Starting",
+ "正在收尾": "Finalizing",
+ "每张显卡的batch_size": "Batch size per GPU:",
+ "淡入淡出长度": "Fade length",
+ "清理模型缓存": "Clearing model cache",
+ "版本": "Version",
+ "特征": "Features",
+ "特征提取": "Feature extraction",
+ "状态": "Status",
+ "独占 WASAPI 设备": "Exclusive WASAPI device",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "Recommended +12 key for male to female conversion, and -12 key for female to male conversion. If the sound range goes too far and the voice is distorted, you can also adjust it to the appropriate range by yourself.",
+ "目标采样率": "Target sample rate:",
+ "等待中": "Waiting",
+ "等待输入": "Waiting for input",
+ "算法延迟(ms):": "Algorithmic delays(ms):",
+ "索引": "Index",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "Invalid index: use added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "Index search failed",
+ "索引检索失败或未启用": "Index search failed or is disabled",
+ "索引训练": "Index training",
+ "耗时": "Elapsed time",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "Elapsed time: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "Fusion",
+ "要改的模型信息": "Model information to be modified:",
+ "要置入的模型信息": "Model information to be placed:",
+ "训练": "Train",
+ "训练已完成,正在保存最终模型": "Training completed; saving the final model",
+ "训练文件列表写入完成": "Training file list written successfully",
+ "训练模型": "Train model",
+ "训练特征索引": "Train feature index",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "Training complete. You can check the training logs in the console or the 'train.log' file under the experiment folder.",
+ "训练设备规则选择的精度:%s": "Training precision selected by device rules: %s",
+ "训练轮次:{} [{:.0f}%]": "Training epoch: {} [{:.0f}%]",
+ "设备类型": "Device type",
+ "请上传音频文件": "Upload an audio file",
+ "请填写输出文件夹路径": "Enter the output folder path",
+ "请指定说话人id": "Please specify the speaker/singer ID:",
+ "请选择index文件": "Please choose the .index file",
+ "请选择pth文件": "Please choose the .pth file",
+ "请选择说话人id": "Select Speaker/Singer ID:",
+ "转换": "Convert",
+ "输入实验名": "Enter the experiment name:",
+ "输入待处理音频文件夹路径": "Enter the path of the audio folder to be processed:",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "Enter the path of the audio folder to be processed (copy it from the address bar of the file manager):",
+ "输入待处理音频文件路径(默认是正确格式示例)": "Enter the path of the audio file to be processed (default is the correct format example):",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "Adjust the volume envelope scaling. Closer to 0, the more it mimicks the volume of the original vocals. Can help mask noise and make volume sound more natural when set relatively low. Closer to 1 will be more of a consistently loud volume:",
+ "输入监听": "Input voice monitor",
+ "输入训练文件夹路径": "Enter the path of the training folder:",
+ "输入设备": "Input device",
+ "输入设备:%s:%s": "Input device: %s:%s",
+ "输入降噪": "Input noise reduction",
+ "输出信息": "Output information",
+ "输出变声": "Output converted voice",
+ "输出设备": "Output device",
+ "输出设备:%s:%s": "Output device: %s:%s",
+ "输出降噪": "Output noise reduction",
+ "输出音频(右下角三个点,点了可以下载)": "Export audio (click on the three dots in the lower right corner to download)",
+ "运行中": "Running",
+ "进度": "Progress",
+ "选择.index文件": "Select the .index file",
+ "选择.pth文件": "Select the .pth file",
+ "选择模型": "Select model",
+ "选择索引": "Select index",
+ "选择音高提取算法": "Select the pitch extraction algorithm",
+ "采样率:": "Sample rate:",
+ "采样长度": "Sample length",
+ "重载设备列表": "Reload device list",
+ "错误信息:%s": "Error details: %s",
+ "错误:未知模型:%s": "Error: Unknown model: %s",
+ "音调设置": "Pitch settings",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "The audio has multiple channels, but the model is mono; all channels will be averaged",
+ "音频设备": "Audio device",
+ "音高全部为0,该音频无意义,跳过:%s": "All pitch values are zero; this audio is unusable and will be skipped: %s",
+ "音高算法": "pitch detection algorithm",
+ "额外推理时长": "Extra inference time",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "Dataset preprocessing produced no valid training audio. Check the dataset and preprocessing log.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "Dataset preprocessing produced no 16 kHz audio. Feature extraction and training have been stopped.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "Dataset preprocessing outputs do not match. Feature extraction and training have been stopped.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT feature extraction produced no valid results. Training has been stopped.",
+ "F0提取没有生成有效结果,已停止训练": "F0 extraction produced no valid results. Training has been stopped.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "No valid audio is available for training. Complete dataset preprocessing and feature extraction first.",
+ "已完成阶段": "Completed stages",
+ "已成功": "Succeeded",
+ "刷新音色列表": "Refresh voice list",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "Feature index path (automatically matched after selecting a model; editable)",
+ "[索引训练] 外部索引链接已存在:%s": "[Index training] External index link already exists: %s",
+ "[索引训练][跳过] added索引已存在:%s": "[Index training][Skipped] added index already exists: %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[Index training][Skipped] trained index already exists: %s",
+ "当前设备:%s | 推理精度:%s": "Current device: %s | Inference precision: %s"
+}
diff --git a/i18n/locale/es_ES.json b/i18n/locale/es_ES.json
new file mode 100644
index 0000000..57385cd
--- /dev/null
+++ b/i18n/locale/es_ES.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "Intensidad de extracción vocal",
+ "%s → 成功": "%s → Éxito",
+ "%s运行中,请先停止该任务": "%s está en ejecución; deténgalo antes de iniciar otra tarea",
+ "%s进程已终止": "El proceso %s ha finalizado",
+ "====> 轮次:{} {}": "====> Época: {} {}",
+ "A模型权重": "Un peso modelo para el modelo A.",
+ "A模型路径": "Modelo A ruta.",
+ "B模型路径": "Modelo B ruta.",
+ "CUDA可用:%s": "CUDA disponible: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "Extracción de F0 y características HuBERT",
+ "F0提取": "Extracción F0",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "Archivo de curva F0, opcional, un tono por línea, en lugar de F0 predeterminado y cambio de tono",
+ "HuBERT特征": "Características HuBERT",
+ "Index Rate": "Tasa de índice",
+ "RVC模型路径": "Ruta del modelo RVC",
+ "SOLA偏移:%d": "Desplazamiento SOLA: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Extracción F0] Completado | Éxito: %s | Omitidos: %s | Error: %s",
+ "[F0提取] 待处理:%s": "[Extracción F0] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[Extracción F0] No hay audio pendiente; se omitieron todos los archivos",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[Extracción F0] Progreso: %s/%s | Éxito: %s | Omitidos: %s | %s",
+ "[F0提取][失败] %s": "[Extracción F0][Error] %s",
+ "[F0提取][失败] %s\n%s": "[Extracción F0][Error] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Características HuBERT] Completado | Éxito: %s | Omitidos: %s | Error: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[Características HuBERT] No hay audio pendiente; omitidos: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[Características HuBERT] Cargando el modelo: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[Características HuBERT] Dispositivo: %s | Pending: %s | Omitidos: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[Características HuBERT] Progreso: %s/%s | Éxito: %s | Error: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[Características HuBERT][Error] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[Características HuBERT][Error] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[Características HuBERT][Error] Modelo not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[División de datos] Subtarea completada | Éxito: %s | Error: %s",
+ "[数据切分] 完成": "[División de datos] Completado",
+ "[数据切分] 开始": "[División de datos] Iniciado",
+ "[数据切分] 待处理:%s | 进程数:%s": "[División de datos] Pending: %s | Procesos: %s",
+ "[数据切分] 进度:%s/%s | %s": "[División de datos] Progreso: %s/%s | %s",
+ "[数据切分][失败] %s": "[División de datos][Error] %s",
+ "[数据切分][失败] %s\n%s": "[División de datos][Error] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[División de datos][Omitidos] Segmento de audio no válido o anómalo: %s_%s | Pico: %s",
+ "[索引训练] 写入进度:%s/%s": "[Entrenamiento del índice] Progreso de escritura: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[Entrenamiento del índice] Índice enlazado al directorio externo: %s",
+ "[索引训练] 成功构建索引:%s": "[Entrenamiento del índice] Índice creado correctamente: %s",
+ "[索引训练] 正在写入特征向量": "[Entrenamiento del índice] Añadiendo vectores de características",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[Entrenamiento del índice] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[Entrenamiento del índice] Entrenando el índice",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[Entrenamiento del índice] Forma de características: %s | Cantidad de IVF: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[Entrenamiento del índice][Error] No se pudo enlazar el índice al directorio externo: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[Entrenamiento del índice][Error] Falló el agrupamiento; se continuará con las características originales\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[Entrenamiento del índice][Error] Extraiga primero las características",
+ "ckpt处理": "Procesamiento de recibos",
+ "index文件路径不可包含中文": "La ruta del archivo .index no debe contener caracteres chinos.",
+ "pth文件路径不可包含中文": "La ruta del archivo .pth no debe contener caracteres chinos.",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "Separe los números de identificación de la GPU con '-' al ingresarlos. Por ejemplo, '0-1-2' significa usar GPU 0, GPU 1 y GPU 2.",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "Paso 1: Complete la configuración del experimento. Los datos del experimento se almacenan en el directorio 'logs', con cada experimento en una carpeta separada. La ruta del nombre del experimento debe ingresarse manualmente y debe contener la configuración del experimento, los registros y los archivos del modelo entrenado.",
+ "step1:正在处理数据": "Paso 1: Procesando datos",
+ "step2:正在提取音高&正在提取特征": "Paso 2: Extracción del tono y extracción de características",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "Paso 2a: Recorra automáticamente la carpeta de capacitación y corte y normalice todos los archivos de audio que se pueden decodificar en audio. Se generarán dos carpetas 'wav' en el directorio del experimento. Actualmente, solo se admite la capacitación de una sola persona.",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "Paso 2b: Use la CPU para extraer el tono (si el modelo tiene guía de tono) y la GPU para extraer características (seleccione el número de tarjeta).",
+ "step3: 填写训练设置, 开始训练模型和索引": "Paso 3: Complete la configuración de entrenamiento y comience a entrenar el modelo y el índice.",
+ "step3a:正在训练模型": "Paso 3a: Entrenando el modelo",
+ "step3b:正在训练索引": "paso 3b: entrenando el índice",
+ "……仅显示最近10条失败记录": "…Se muestran solo los 10 errores más recientes",
+ "……已省略前%s行,仅显示最新状态": "…Se omitieron las primeras %s líneas; se muestra solo el estado más reciente",
+ "一键训练": "Entrenamiento con un clic",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "También se pueden importar varios archivos de audio. Si existe una ruta de carpeta, esta entrada se ignora.",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "Procesamiento por lotes para separar voces y acompañamiento mediante modelos UVR5.
Puede elegir un modelo que conserve las voces o usar modelos DeEcho/DeReverb para eliminar el eco y la reverberación.",
+ "仅支持pm和rmvpe音高提取算法": "Solo se admiten los métodos de extracción de tono pm y rmvpe",
+ "从训练检查点提取的模型": "Modeloo extraído de un punto de control de entrenamiento",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "Separe los números de identificación de la GPU con '-' al ingresarlos. Por ejemplo, '0-1-2' significa usar GPU 0, GPU 1 y GPU 2.",
+ "任务": "Tarea",
+ "伴奏人声分离&去混响&去回声": "Separación de voz acompañante & eliminación de reverberación & eco",
+ "使用显卡:%s": "GPU en uso: %s",
+ "使用模型采样率": "Usar la frecuencia del modelo",
+ "使用设备采样率": "Usar la frecuencia del dispositivo",
+ "保存名": "Guardar nombre",
+ "保存的文件名, 默认空为和源文件同名": "Nombre del archivo que se guardará, el valor predeterminado es el mismo que el nombre del archivo de origen",
+ "保存的模型名不带后缀": "Nombre del modelo guardado sin extensión.",
+ "保存频率save_every_epoch": "Frecuencia de guardado (save_every_epoch)",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "Proteger las consonantes claras y la respiración, prevenir artefactos como la distorsión de sonido electrónico, 0.5 no está activado, reducir aumentará la protección pero puede reducir el efecto del índice",
+ "修改": "Modificar",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "Modificar la información del modelo (solo admite archivos de modelos pequeños extraídos en la carpeta weights)",
+ "停止一键训练": "Detener entrenamiento con un clic",
+ "停止处理数据": "Detener preprocesamiento de datos",
+ "停止特征提取": "Detener extracción de características",
+ "停止训练模型": "Detener entrenamiento del modelo",
+ "停止训练索引": "Detener entrenamiento del índice",
+ "停止音频转换": "Detener la conversión de audio",
+ "全流程结束!": "¡Todo el proceso ha terminado!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "Cargar modelo",
+ "加载预训练底模D路径": "Cargue la ruta del modelo D base pre-entrenada.",
+ "加载预训练底模G路径": "Cargue la ruta del modelo G base pre-entrenada.",
+ "单次推理": "Inferencia individual",
+ "卸载音色省显存": "Descargue la voz para ahorrar memoria GPU",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "Cambio de tono (entero, número de semitonos, subir una octava +12 o bajar una octava -12)",
+ "合成": "Síntesis",
+ "后处理重采样至最终采样率,0为不进行重采样": "Remuestreo posterior al proceso a la tasa de muestreo final, 0 significa no remuestrear",
+ "否": "No",
+ "响应阈值": "Umbral de respuesta",
+ "响度因子": "factor de sonoridad",
+ "处理中": "Procesando",
+ "处理数据": "Procesar datos",
+ "失败": "Error",
+ "失败记录": "Registro de errores",
+ "子进程执行失败,返回码:%s": "El subproceso falló con el código de salida: %s",
+ "导出文件格式": "Formato de archivo de exportación",
+ "已停止": "Detenido",
+ "已加载判别器预训练模型:%s": "Modeloo preentrenado del discriminador cargado: %s",
+ "已加载生成器预训练模型:%s": "Modeloo preentrenado del generador cargado: %s",
+ "已启用索引检索": "Búsqueda por índice activada",
+ "已完成": "Completado",
+ "已恢复判别器检查点": "Se restauró el punto de control del discriminador",
+ "常见问题解答": "Preguntas frecuentes",
+ "常规设置": "Configuración general",
+ "开始音频转换": "Iniciar conversión de audio",
+ "当前": "Actual",
+ "当前阶段": "Etapa actual",
+ "很遗憾您这没有能用的显卡来支持您训练": "Lamentablemente, no tiene una tarjeta gráfica adecuada para soportar su entrenamiento",
+ "性别因子/声线粗细": "Factor de género / grosor de la voz",
+ "性能设置": "Configuración de rendimiento",
+ "总训练轮数total_epoch": "Total de épocas de entrenamiento (total_epoch)",
+ "成功": "Éxito",
+ "执行命令": "Comando",
+ "批量推理": "Inferencia por lotes",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "Conversión por lotes, ingrese la carpeta que contiene los archivos de audio para convertir o cargue varios archivos de audio. El audio convertido se emitirá en la carpeta especificada (opción predeterminada).",
+ "拖拽或点击上传待处理音频": "Arrastre y suelte o haga clic para subir el audio que desea procesar",
+ "指定输出主人声文件夹": "Especifique la carpeta de salida para la voz principal",
+ "指定输出文件夹": "Especificar carpeta de salida",
+ "指定输出非主人声文件夹": "Especifique la carpeta de salida para las voces no principales",
+ "推理时间(ms):": "Inferir tiempo (ms):",
+ "推理耗时:%.2f秒": "Tiempo de inferencia: %.2f seconds",
+ "推理音色": "inferencia de voz",
+ "提取": "Extraer",
+ "提取音高和处理数据使用的CPU进程数": "Número de procesos de CPU utilizados para extraer el tono y procesar los datos",
+ "数据切分": "División de datos",
+ "无法停止": "No se puede detener",
+ "无法读取音频:%s": "No se pudo leer el audio: %s",
+ "是": "Sí",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "Guardar solo el archivo ckpt más reciente para ahorrar espacio en disco",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "Guardar pequeño modelo final en la carpeta 'weights' en cada punto de guardado",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "Si almacenar en caché todos los conjuntos de entrenamiento en la memoria de la GPU. Los conjuntos de datos pequeños (menos de 10 minutos) se pueden almacenar en caché para acelerar el entrenamiento, pero el almacenamiento en caché de conjuntos de datos grandes puede causar errores de memoria en la GPU y no aumenta la velocidad de manera significativa.",
+ "显卡信息": "información de la GPU",
+ "未使用": "No utilizado",
+ "未使用判别器预训练模型": "No se usó el modelo preentrenado del discriminador",
+ "未使用生成器预训练模型": "No se usó el modelo preentrenado del generador",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "No se encontró la configuración del modelo Roformer; se usarán los valores integrados",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "No se detectó una GPU compatible; entrenar con la CPU puede tardar mucho más",
+ "未运行": "No iniciado",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "Este software es de código abierto bajo la licencia MIT, el autor no tiene ningún control sobre el software, y aquellos que usan el software y difunden los sonidos exportados por el software son los únicos responsables.
Si no está de acuerdo con esta cláusula , no puede utilizar ni citar ningún código ni archivo del paquete de software Consulte el directorio raíz Agreement-LICENSE.txt para obtener más información.",
+ "查看": "Ver",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "Ver información del modelo (solo aplicable a archivos de modelos pequeños extraídos de la carpeta 'pesos')",
+ "检测到模型类型:%s": "Tipo de modelo detectado: %s",
+ "检索特征占比": "Proporción de función de búsqueda",
+ "模型": "Modelo",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "Información del modelo: %s\nFrecuencia de muestreo: %s\nGuía de tono: %s\nVersión: %s",
+ "模型推理": "inferencia del modelo",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "Extracción de modelo (ingrese la ruta de un archivo de modelo grande en la carpeta 'logs'), aplicable cuando desea extraer un archivo de modelo pequeño después de entrenar a mitad de camino y no se guardó automáticamente, o cuando desea probar un modelo intermedio",
+ "模型是否带音高指导": "Si el modelo tiene guía de tono.",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "Si el modelo tiene guía de tono (necesaria para cantar, pero no para hablar)",
+ "模型是否带音高指导,1是0否": "Si el modelo tiene guía de tono, 1 para sí, 0 para no",
+ "模型版本型号": "Versión y modelo del modelo",
+ "模型融合, 可用于测试音色融合": "Fusión de modelos, se puede utilizar para fusionar diferentes voces",
+ "模型融合失败:两个模型的结构不一致": "Error al fusionar modelos: las arquitecturas no coinciden",
+ "模型训练": "Entrenamiento del modelo",
+ "模型路径": "Ruta del modelo",
+ "正在保存最终检查点:%s": "Guardando el punto de control final: %s",
+ "正在保存检查点 %s_e%s:%s": "Guardando el punto de control %s_e%s: %s",
+ "正在加载RMVPE模型": "Cargando el modelo RMVPE",
+ "正在加载模型": "Cargando el modelo",
+ "正在启动": "Iniciando",
+ "正在收尾": "Finalizando",
+ "每张显卡的batch_size": "Tamaño del lote (batch_size) por tarjeta gráfica",
+ "淡入淡出长度": "Duración del fundido de entrada/salida",
+ "清理模型缓存": "Limpiando la caché del modelo",
+ "版本": "Versión",
+ "特征": "Características",
+ "特征提取": "Extracción de características",
+ "状态": "Estado",
+ "独占 WASAPI 设备": "Dispositivo WASAPI exclusivo",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "Tecla +12 recomendada para conversión de voz de hombre a mujer, tecla -12 para conversión de voz de mujer a hombre. Si el rango de tono es demasiado amplio y causa distorsión, ajústelo usted mismo a un rango adecuado.",
+ "目标采样率": "Tasa de muestreo objetivo",
+ "等待中": "En espera",
+ "等待输入": "Esperando entrada",
+ "算法延迟(ms):": "Latencia del algoritmo (ms):",
+ "索引": "Índice",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "Índice no válido: use added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "Falló la búsqueda por índice",
+ "索引检索失败或未启用": "La búsqueda por índice falló o está desactivada",
+ "索引训练": "Entrenamiento del índice",
+ "耗时": "Tiempo transcurrido",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "Tiempo transcurrido: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "Fusión",
+ "要改的模型信息": "Información del modelo a modificar",
+ "要置入的模型信息": "Información del modelo a colocar.",
+ "训练": "Entrenamiento",
+ "训练已完成,正在保存最终模型": "Entrenamiento completado; guardando el modelo final",
+ "训练文件列表写入完成": "Lista de archivos de entrenamiento guardada",
+ "训练模型": "Entrenar Modelo",
+ "训练特征索引": "Índice de características",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "Entrenamiento finalizado, puede ver el registro de entrenamiento en la consola o en el archivo train.log en la carpeta del experimento",
+ "训练设备规则选择的精度:%s": "Precisión de entrenamiento elegida según las reglas del dispositivo: %s",
+ "训练轮次:{} [{:.0f}%]": "Época de entrenamiento: {} [{:.0f}%]",
+ "设备类型": "Tipo de dispositivo",
+ "请上传音频文件": "Suba un archivo de audio",
+ "请填写输出文件夹路径": "Introduzca la ruta de la carpeta de salida",
+ "请指定说话人id": "ID del modelo",
+ "请选择index文件": "Seleccione el archivo .index",
+ "请选择pth文件": "Seleccione el archivo .pth",
+ "请选择说话人id": "Seleccione una identificación de altavoz",
+ "转换": "Conversión",
+ "输入实验名": "Ingrese el nombre del modelo",
+ "输入待处理音频文件夹路径": "Ingrese la ruta a la carpeta de audio que se procesará",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "Ingrese la ruta a la carpeta de audio que se procesará (simplemente cópiela desde la barra de direcciones del administrador de archivos)",
+ "输入待处理音频文件路径(默认是正确格式示例)": "Ingrese la ruta del archivo del audio que se procesará (el formato predeterminado es el ejemplo correcto)",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "Proporción de fusión para reemplazar el sobre de volumen de entrada con el sobre de volumen de salida, cuanto más cerca de 1, más se utiliza el sobre de salida",
+ "输入监听": "Monitorizar entrada",
+ "输入训练文件夹路径": "Introduzca la ruta de la carpeta de entrenamiento",
+ "输入设备": "Dispositivo de entrada",
+ "输入设备:%s:%s": "Dispositivo de entrada: %s:%s",
+ "输入降噪": "Reducción de ruido de entrada",
+ "输出信息": "Información de salida",
+ "输出变声": "Conversión de salida",
+ "输出设备": "Dispositivo de salida",
+ "输出设备:%s:%s": "Dispositivo de salida: %s:%s",
+ "输出降噪": "Reducción de ruido de salida",
+ "输出音频(右下角三个点,点了可以下载)": "Salida de audio (haga clic en los tres puntos en la esquina inferior derecha para descargar)",
+ "运行中": "En ejecución",
+ "进度": "Progreso",
+ "选择.index文件": "Seleccione el archivo .index",
+ "选择.pth文件": "Seleccione el archivo .pth",
+ "选择模型": "Seleccionar modelo",
+ "选择索引": "Seleccionar índice",
+ "选择音高提取算法": "Seleccione el algoritmo de extracción de tono",
+ "采样率:": "Frecuencia de muestreo:",
+ "采样长度": "Longitud de muestreo",
+ "重载设备列表": "Actualizar lista de dispositivos",
+ "错误信息:%s": "Detalles del error: %s",
+ "错误:未知模型:%s": "Error: modelo desconocido: %s",
+ "音调设置": "Ajuste de tono",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "El audio tiene varios canales, pero el modelo es mono; se promediarán todos los canales",
+ "音频设备": "Dispositivo de audio",
+ "音高全部为0,该音频无意义,跳过:%s": "Todos los valores de tono son cero; este audio no es útil y se omitirá: %s",
+ "音高算法": "Algoritmo de tono",
+ "额外推理时长": "Tiempo de inferencia adicional",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "El preprocesamiento no generó audio de entrenamiento válido. Revise el conjunto de datos y el registro.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "El preprocesamiento no generó audio de 16 kHz. Se detuvieron la extracción de características y el entrenamiento.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "Los archivos de salida del preprocesamiento no coinciden. Se detuvieron la extracción y el entrenamiento.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "La extracción de características HuBERT no generó resultados válidos. Se detuvo el entrenamiento.",
+ "F0提取没有生成有效结果,已停止训练": "La extracción de F0 no generó resultados válidos. Se detuvo el entrenamiento.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "No hay audio válido para entrenar. Complete primero el preprocesamiento y la extracción de características.",
+ "已完成阶段": "Etapas completadas",
+ "已成功": "Correcto",
+ "刷新音色列表": "Actualizar lista de voces",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "Ruta del índice de características (se detecta al elegir el modelo; editable)",
+ "[索引训练] 外部索引链接已存在:%s": "[Entrenamiento de índice] El enlace externo ya existe: %s",
+ "[索引训练][跳过] added索引已存在:%s": "[Entrenamiento de índice][Omitido] El índice added ya existe: %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[Entrenamiento de índice][Omitido] El índice trained ya existe: %s",
+ "当前设备:%s | 推理精度:%s": "Dispositivo actual: %s | Precisión de inferencia: %s"
+}
diff --git a/i18n/locale/fr_FR.json b/i18n/locale/fr_FR.json
new file mode 100644
index 0000000..ce1e903
--- /dev/null
+++ b/i18n/locale/fr_FR.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "Intensité de l’extraction vocale",
+ "%s → 成功": "%s → Réussite",
+ "%s运行中,请先停止该任务": "%s est en cours ; arrêtez cette tâche avant d'en démarrer une autre",
+ "%s进程已终止": "Le processus %s a été arrêté",
+ "====> 轮次:{} {}": "====> Époque : {} {}",
+ "A模型权重": "Poids (w) pour le modèle A :",
+ "A模型路径": "Chemin d'accès au modèle A :",
+ "B模型路径": "Chemin d'accès au modèle B :",
+ "CUDA可用:%s": "CUDA disponible : %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "Extraction F0 et caractéristiques HuBERT",
+ "F0提取": "Extraction F0",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "Fichier de courbe F0 (facultatif). Une hauteur par ligne. Remplace la fréquence fondamentale par défaut et la modulation de la hauteur :",
+ "HuBERT特征": "Caractéristiques HuBERT",
+ "Index Rate": "Taux d'indexation",
+ "RVC模型路径": "Chemin du modèle RVC :",
+ "SOLA偏移:%d": "Décalage SOLA : %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Extraction F0] Terminé | Réussite: %s | Ignorés: %s | Échec: %s",
+ "[F0提取] 待处理:%s": "[Extraction F0] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[Extraction F0] Aucun audio en attente ; tous les fichiers ont été ignorés",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[Extraction F0] Progression: %s/%s | Réussite: %s | Ignorés: %s | %s",
+ "[F0提取][失败] %s": "[Extraction F0][Échec] %s",
+ "[F0提取][失败] %s\n%s": "[Extraction F0][Échec] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Caractéristiques HuBERT] Terminé | Réussite: %s | Ignorés: %s | Échec: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[Caractéristiques HuBERT] Aucun audio en attente ; ignorés: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[Caractéristiques HuBERT] Chargement du modèle: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[Caractéristiques HuBERT] Périphérique: %s | Pending: %s | Ignorés: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[Caractéristiques HuBERT] Progression: %s/%s | Réussite: %s | Échec: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[Caractéristiques HuBERT][Échec] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[Caractéristiques HuBERT][Échec] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[Caractéristiques HuBERT][Échec] Modèle not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[Découpage des données] Sous-tâche terminée | Réussite: %s | Échec: %s",
+ "[数据切分] 完成": "[Découpage des données] Terminé",
+ "[数据切分] 开始": "[Découpage des données] Démarré",
+ "[数据切分] 待处理:%s | 进程数:%s": "[Découpage des données] Pending: %s | Processus: %s",
+ "[数据切分] 进度:%s/%s | %s": "[Découpage des données] Progression: %s/%s | %s",
+ "[数据切分][失败] %s": "[Découpage des données][Échec] %s",
+ "[数据切分][失败] %s\n%s": "[Découpage des données][Échec] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[Découpage des données][Ignorés] Segment audio invalide ou anormal: %s_%s | Crête: %s",
+ "[索引训练] 写入进度:%s/%s": "[Entraînement de l'index] Progressionion de l'écriture: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[Entraînement de l'index] Index lié au dossier externe: %s",
+ "[索引训练] 成功构建索引:%s": "[Entraînement de l'index] Index créé avec succès: %s",
+ "[索引训练] 正在写入特征向量": "[Entraînement de l'index] Ajout des vecteurs de caractéristiques",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[Entraînement de l'index] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[Entraînement de l'index] Entraînement de l'index",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[Entraînement de l'index] Forme des caractéristiques: %s | Nombre d'IVF: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[Entraînement de l'index][Échec] Impossible de lier l'index au dossier externe: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[Entraînement de l'index][Échec] Échec du regroupement ; poursuite avec les caractéristiques d'origine\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[Entraînement de l'index][Échec] Extrayez d'abord les caractéristiques",
+ "ckpt处理": "Traitement des fichiers .ckpt",
+ "index文件路径不可包含中文": "Le chemin du fichier d'index ne doit pas contenir de caractères chinois.",
+ "pth文件路径不可包含中文": "Le chemin du fichier .pth ne doit pas contenir de caractères chinois.",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "Configuration des numéros de carte RMVPE : séparez les index GPU par des tirets \"-\", par exemple, 0-0-1 pour utiliser 2 processus sur GPU0 et 1 processus sur GPU1.",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "Étape 1 : Remplissez la configuration expérimentale. Les données expérimentales sont stockées dans le dossier 'logs', avec chaque expérience ayant un dossier distinct. Entrez manuellement le chemin du nom de l'expérience, qui contient la configuration expérimentale, les journaux et les fichiers de modèle entraînés.",
+ "step1:正在处理数据": "Étape 1 : Traitement des données en cours.",
+ "step2:正在提取音高&正在提取特征": "Étape 2 : Extraction de la hauteur et extraction des caractéristiques en cours.",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "Étape 2a : Parcours automatique de tous les fichiers du dossier d'entraînement qui peuvent être décodés en fichiers audio et réalisation d'une normalisation par tranches. Génère 2 dossiers wav dans le répertoire de l'expérience. Actuellement, seule la formation avec un seul chanteur/locuteur est prise en charge.",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "Étape 2b : Utilisez le CPU pour extraire la hauteur (si le modèle le permet), utilisez le GPU pour extraire les caractéristiques (sélectionnez l'index du GPU) :",
+ "step3: 填写训练设置, 开始训练模型和索引": "Étape 3 : Remplissez les paramètres d'entraînement et démarrez l'entraînement du modèle ainsi que l'indexation.",
+ "step3a:正在训练模型": "Étape 3a : L'entraînement du modèle a commencé.",
+ "step3b:正在训练索引": "étape 3b : entraînement de l’index",
+ "……仅显示最近10条失败记录": "…Seuls les 10 échecs les plus récents sont affichés",
+ "……已省略前%s行,仅显示最新状态": "…Les %s premières lignes ont été omises ; seul l'état récent est affiché",
+ "一键训练": "Entraînement en un clic",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "Il est également possible d'importer plusieurs fichiers audio. Si un chemin de dossier existe, cette entrée est ignorée.",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "Séparation par lot des voix et de l’accompagnement à l’aide de modèles UVR5.
Vous pouvez choisir un modèle qui conserve les voix, ou utiliser les modèles DeEcho/DeReverb pour supprimer l’écho et la réverbération.",
+ "仅支持pm和rmvpe音高提取算法": "Seules les méthodes d'extraction de hauteur pm et rmvpe sont prises en charge",
+ "从训练检查点提取的模型": "Modèle extrait d'un point de contrôle d'entraînement",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "Entrez le(s) index GPU séparé(s) par '-', par exemple, 0-1-2 pour utiliser les GPU 0, 1 et 2 :",
+ "任务": "Tâche",
+ "伴奏人声分离&去混响&去回声": "Séparation des voix/accompagnement et suppression de la réverbération",
+ "使用显卡:%s": "GPU utilisés : %s",
+ "使用模型采样率": "Utiliser la fréquence du modèle",
+ "使用设备采样率": "Utiliser la fréquence du périphérique",
+ "保存名": "Nom de sauvegarde :",
+ "保存的文件名, 默认空为和源文件同名": "Nom du fichier de sauvegarde (par défaut : identique au nom du fichier source) :",
+ "保存的模型名不带后缀": "Nom du modèle enregistré (sans extension) :",
+ "保存频率save_every_epoch": "Fréquence de sauvegarde (save_every_epoch) :",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "Protéger les consonnes sourdes et les bruits de respiration pour éviter les artefacts tels que le déchirement dans la musique électronique. Réglez à 0,5 pour désactiver. Diminuez la valeur pour renforcer la protection, mais cela peut réduire la précision de l'indexation :",
+ "修改": "Modifier",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "Modifier les informations du modèle (uniquement pris en charge pour les petits fichiers de modèle extraits du dossier 'weights')",
+ "停止一键训练": "Arrêter l’entraînement en un clic",
+ "停止处理数据": "Arrêter le prétraitement des données",
+ "停止特征提取": "Arrêter l’extraction des caractéristiques",
+ "停止训练模型": "Arrêter l’entraînement du modèle",
+ "停止训练索引": "Arrêter l’entraînement de l’index",
+ "停止音频转换": "Arrêter la conversion audio",
+ "全流程结束!": "Toutes les étapes ont été terminées !",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "Charger le modèle.",
+ "加载预训练底模D路径": "Charger le chemin du modèle de base pré-entraîné D :",
+ "加载预训练底模G路径": "Charger le chemin du modèle de base pré-entraîné G :",
+ "单次推理": "Inférence unique",
+ "卸载音色省显存": "Décharger la voix pour économiser la mémoire GPU.",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "Transposer (entier, nombre de demi-tons, monter d'une octave : 12, descendre d'une octave : -12) :",
+ "合成": "Synthèse",
+ "后处理重采样至最终采样率,0为不进行重采样": "Rééchantillonner l'audio de sortie en post-traitement à la fréquence d'échantillonnage finale. Réglez sur 0 pour ne pas effectuer de rééchantillonnage :",
+ "否": "Non",
+ "响应阈值": "Seuil de réponse",
+ "响度因子": "Facteur de volume sonore",
+ "处理中": "Traitement en cours",
+ "处理数据": "Traitement des données",
+ "失败": "Échec",
+ "失败记录": "Détails des échecs",
+ "子进程执行失败,返回码:%s": "Le sous-processus a échoué avec le code de sortie: %s",
+ "导出文件格式": "Format de fichier d'exportation",
+ "已停止": "Arrêté",
+ "已加载判别器预训练模型:%s": "Modèle préentraîné du discriminateur chargé: %s",
+ "已加载生成器预训练模型:%s": "Modèle préentraîné du générateur chargé: %s",
+ "已启用索引检索": "Recherche par index activée",
+ "已完成": "Terminé",
+ "已恢复判别器检查点": "Point de contrôle du discriminateur restauré",
+ "常见问题解答": "FAQ (Foire Aux Questions)",
+ "常规设置": "Paramètres généraux",
+ "开始音频转换": "Démarrer la conversion audio.",
+ "当前": "Actuel",
+ "当前阶段": "Étape actuelle",
+ "很遗憾您这没有能用的显卡来支持您训练": "Malheureusement, il n'y a pas de GPU compatible disponible pour prendre en charge votre entrainement.",
+ "性别因子/声线粗细": "Facteur de genre / épaisseur de la voix",
+ "性能设置": "Paramètres de performance",
+ "总训练轮数total_epoch": "Nombre total d'époques d'entraînement (total_epoch) :",
+ "成功": "Réussite",
+ "执行命令": "Commande",
+ "批量推理": "Inférence par lot",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "Conversion en lot. Entrez le dossier contenant les fichiers audio à convertir ou téléchargez plusieurs fichiers audio. Les fichiers audio convertis seront enregistrés dans le dossier spécifié (par défaut : 'opt').",
+ "拖拽或点击上传待处理音频": "Glissez-déposez ou cliquez pour téléverser l’audio à traiter",
+ "指定输出主人声文件夹": "Spécifiez le dossier de sortie pour les fichiers de voix :",
+ "指定输出文件夹": "Spécifiez le dossier de sortie :",
+ "指定输出非主人声文件夹": "Spécifiez le dossier de sortie pour l'accompagnement :",
+ "推理时间(ms):": "Temps d'inférence (ms) :",
+ "推理耗时:%.2f秒": "Temps d'inférence: %.2f seconds",
+ "推理音色": "Voix pour l'inférence",
+ "提取": "Extraire",
+ "提取音高和处理数据使用的CPU进程数": "Nombre de processus CPU utilisés pour l'extraction de la hauteur et le traitement des données :",
+ "数据切分": "Découpage des données",
+ "无法停止": "Impossible à arrêter",
+ "无法读取音频:%s": "Impossible de lire l'audio: %s",
+ "是": "Oui",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "Enregistrer uniquement le dernier fichier '.ckpt' pour économiser de l'espace disque :",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "Enregistrer un petit modèle final dans le dossier 'weights' à chaque point de sauvegarde :",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "Mettre en cache tous les ensembles d'entrainement dans la mémoire GPU. Mettre en cache de petits ensembles de données (moins de 10 minutes) peut accélérer l'entrainement, mais mettre en cache de grands ensembles de données consommera beaucoup de mémoire GPU et peut ne pas apporter beaucoup d'amélioration de vitesse :",
+ "显卡信息": "Informations sur la carte graphique (GPU)",
+ "未使用": "Non utilisé",
+ "未使用判别器预训练模型": "Modèle préentraîné du discriminateur non utilisé",
+ "未使用生成器预训练模型": "Modèle préentraîné du générateur non utilisé",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Configuration du modèle Roformer introuvable ; utilisation des valeurs intégrées",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "Aucun GPU compatible détecté ; l'entraînement sur CPU peut être beaucoup plus long",
+ "未运行": "Non démarré",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "Ce logiciel est open source sous la licence MIT. L'auteur n'a aucun contrôle sur le logiciel. Les utilisateurs qui utilisent le logiciel et distribuent les sons exportés par le logiciel en sont entièrement responsables.
Si vous n'acceptez pas cette clause, vous ne pouvez pas utiliser ou faire référence à aucun code ni fichier contenu dans le package logiciel. Consultez le fichier Agreement-LICENSE.txt dans le répertoire racine pour plus de détails.",
+ "查看": "Voir",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "Afficher les informations sur le modèle (uniquement pour les petits fichiers de modèle extraits du dossier \"weights\")",
+ "检测到模型类型:%s": "Type de modèle détecté : %s",
+ "检索特征占比": "Rapport de recherche de caractéristiques (contrôle l'intensité de l'accent, un rapport trop élevé provoque des artefacts) :",
+ "模型": "Modèle",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "Informations du modèle: %s\nFréquence d'échantillonnage: %s\nGuidage de hauteur: %s\nVersion: %s",
+ "模型推理": "Inférence du modèle",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "Extraction du modèle (saisissez le chemin d'accès au modèle du grand fichier dans le dossier \"logs\"). Cette fonction est utile si vous souhaitez arrêter l'entrainement à mi-chemin et extraire et enregistrer manuellement un petit fichier de modèle, ou si vous souhaitez tester un modèle intermédiaire :",
+ "模型是否带音高指导": "Indique si le modèle dispose d'un guidage en hauteur :",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "Indique si le modèle dispose d'un système de guidage de la hauteur (obligatoire pour le chant, facultatif pour la parole) :",
+ "模型是否带音高指导,1是0否": "Le modèle dispose-t-il d'un guide de hauteur (1 : oui, 0 : non) ?",
+ "模型版本型号": "Version de l'architecture du modèle :",
+ "模型融合, 可用于测试音色融合": "Fusion de modèles, peut être utilisée pour tester la fusion de timbres",
+ "模型融合失败:两个模型的结构不一致": "Échec de la fusion : les architectures des deux modèles diffèrent",
+ "模型训练": "Entraînement du modèle",
+ "模型路径": "Le chemin vers le modèle :",
+ "正在保存最终检查点:%s": "Enregistrement du point de contrôle final: %s",
+ "正在保存检查点 %s_e%s:%s": "Enregistrement du point de contrôle %s_e%s: %s",
+ "正在加载RMVPE模型": "Chargement du modèle RMVPE",
+ "正在加载模型": "Chargement du modèle",
+ "正在启动": "Démarrage",
+ "正在收尾": "Finalisation",
+ "每张显卡的batch_size": "Taille du batch par GPU :",
+ "淡入淡出长度": "Longueur de la transition",
+ "清理模型缓存": "Nettoyage du cache du modèle",
+ "版本": "Version",
+ "特征": "Caractéristiques",
+ "特征提取": "Extraction des caractéristiques",
+ "状态": "État",
+ "独占 WASAPI 设备": "Périphérique WASAPI exclusif",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "Il est recommandé d'utiliser la clé +12 pour la conversion homme-femme et la clé -12 pour la conversion femme-homme. Si la plage sonore est trop large et que la voix est déformée, vous pouvez également l'ajuster vous-même à la plage appropriée.",
+ "目标采样率": "Taux d'échantillonnage cible :",
+ "等待中": "En attente",
+ "等待输入": "En attente d'une entrée",
+ "算法延迟(ms):": "Latence de l'algorithme (ms) :",
+ "索引": "Index",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "Index invalide : utilisez added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "Échec de la recherche par index",
+ "索引检索失败或未启用": "La recherche par index a échoué ou est désactivée",
+ "索引训练": "Entraînement de l'index",
+ "耗时": "Temps écoulé",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "Temps écoulé: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "Fusion",
+ "要改的模型信息": "Informations sur le modèle à modifier :",
+ "要置入的模型信息": "Informations sur le modèle à placer :",
+ "训练": "Entraîner",
+ "训练已完成,正在保存最终模型": "Entraînement terminé ; enregistrement du modèle final",
+ "训练文件列表写入完成": "Liste des fichiers d'entraînement enregistrée",
+ "训练模型": "Entraîner le modèle",
+ "训练特征索引": "Entraîner l'index des caractéristiques",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "Entraînement terminé. Vous pouvez consulter les rapports d'entraînement dans la console ou dans le fichier 'train.log' situé dans le dossier de l'expérience.",
+ "训练设备规则选择的精度:%s": "Précision d'entraînement choisie selon les règles du périphérique: %s",
+ "训练轮次:{} [{:.0f}%]": "Époque d'entraînement : {} [{:.0f}%]",
+ "设备类型": "Type de périphérique",
+ "请上传音频文件": "Téléversez un fichier audio",
+ "请填写输出文件夹路径": "Saisissez le chemin du dossier de sortie",
+ "请指定说话人id": "Veuillez spécifier l'ID de l'orateur ou du chanteur :",
+ "请选择index文件": "Veuillez sélectionner le fichier d'index",
+ "请选择pth文件": "Veuillez sélectionner le fichier pth",
+ "请选择说话人id": "Sélectionner l'ID de l'orateur ou du chanteur :",
+ "转换": "Convertir",
+ "输入实验名": "Saisissez le nom de l'expérience :",
+ "输入待处理音频文件夹路径": "Entrez le chemin du dossier audio à traiter :",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "Entrez le chemin du dossier audio à traiter (copiez-le depuis la barre d'adresse du gestionnaire de fichiers) :",
+ "输入待处理音频文件路径(默认是正确格式示例)": "Entrez le chemin d'accès du fichier audio à traiter (par défaut, l'exemple de format correct) :",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "Ajustez l'échelle de l'enveloppe de volume. Plus il est proche de 0, plus il imite le volume des voix originales. Cela peut aider à masquer les bruits et à rendre le volume plus naturel lorsqu'il est réglé relativement bas. Plus le volume est proche de 1, plus le volume sera fort et constant :",
+ "输入监听": "Écouter l'entrée",
+ "输入训练文件夹路径": "Indiquez le chemin d'accès au dossier d'entraînement :",
+ "输入设备": "Dispositif d'entrée",
+ "输入设备:%s:%s": "Périphérique d'entrée: %s:%s",
+ "输入降噪": "Réduction du bruit d'entrée",
+ "输出信息": "Informations sur la sortie",
+ "输出变声": "Conversion de sortie",
+ "输出设备": "Dispositif de sortie",
+ "输出设备:%s:%s": "Périphérique de sortie: %s:%s",
+ "输出降噪": "Réduction du bruit de sortie",
+ "输出音频(右下角三个点,点了可以下载)": "Exporter l'audio (cliquer sur les trois points dans le coin inférieur droit pour télécharger)",
+ "运行中": "En cours",
+ "进度": "Progression",
+ "选择.index文件": "Sélectionner le fichier .index",
+ "选择.pth文件": "Sélectionner le fichier .pth",
+ "选择模型": "Sélectionner le modèle",
+ "选择索引": "Sélectionner l'index",
+ "选择音高提取算法": "Sélectionnez l’algorithme d’extraction de la hauteur",
+ "采样率:": "Fréquence d'échantillonnage :",
+ "采样长度": "Longueur de l'échantillon",
+ "重载设备列表": "Recharger la liste des dispositifs",
+ "错误信息:%s": "Détails de l'erreur: %s",
+ "错误:未知模型:%s": "Erreur : modèle inconnu : %s",
+ "音调设置": "Réglages de la hauteur",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "L'audio comporte plusieurs canaux, mais le modèle est mono ; tous les canaux seront moyennés",
+ "音频设备": "Périphérique audio",
+ "音高全部为0,该音频无意义,跳过:%s": "Toutes les valeurs de hauteur sont nulles ; cet audio est inutilisable et sera ignoré: %s",
+ "音高算法": "Algorithme de hauteur",
+ "额外推理时长": "Temps d'inférence supplémentaire",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "Le prétraitement n'a produit aucun audio d'entraînement valide. Vérifiez le jeu de données et le journal.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "Le prétraitement n'a produit aucun audio 16 kHz. L'extraction et l'entraînement ont été arrêtés.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "Les fichiers de sortie du prétraitement ne correspondent pas. L'extraction et l'entraînement ont été arrêtés.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "L'extraction des caractéristiques HuBERT n'a produit aucun résultat valide. L'entraînement a été arrêté.",
+ "F0提取没有生成有效结果,已停止训练": "L'extraction F0 n'a produit aucun résultat valide. L'entraînement a été arrêté.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "Aucun audio valide n'est disponible pour l'entraînement. Terminez d'abord le prétraitement et l'extraction.",
+ "已完成阶段": "Étapes terminées",
+ "已成功": "Réussi",
+ "刷新音色列表": "Actualiser la liste des voix",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "Chemin de l’index de caractéristiques (détecté après sélection du modèle ; modifiable)",
+ "[索引训练] 外部索引链接已存在:%s": "[Entraînement de l’index] Le lien externe existe déjà : %s",
+ "[索引训练][跳过] added索引已存在:%s": "[Entraînement de l’index][Ignoré] L’index added existe déjà : %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[Entraînement de l’index][Ignoré] L’index trained existe déjà : %s",
+ "当前设备:%s | 推理精度:%s": "Périphérique actuel : %s | Précision d’inférence : %s"
+}
diff --git a/i18n/locale/it_IT.json b/i18n/locale/it_IT.json
new file mode 100644
index 0000000..97f32d0
--- /dev/null
+++ b/i18n/locale/it_IT.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "Intensità dell'estrazione vocale",
+ "%s → 成功": "%s → Successo",
+ "%s运行中,请先停止该任务": "%s è in esecuzione; interromperlo prima di avviare un'altra attività",
+ "%s进程已终止": "Il processo %s è stato terminato",
+ "====> 轮次:{} {}": "====> Epoca: {} {}",
+ "A模型权重": "Peso (w) per il modello A:",
+ "A模型路径": "Percorso per il modello A:",
+ "B模型路径": "Percorso per il modello B:",
+ "CUDA可用:%s": "CUDA disponibile: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "Estrazione F0 e caratteristiche HuBERT",
+ "F0提取": "Estrazione F0",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "File curva F0 (opzionale). ",
+ "HuBERT特征": "Caratteristiche HuBERT",
+ "Index Rate": "Tasso di indice",
+ "RVC模型路径": "Percorso modello RVC:",
+ "SOLA偏移:%d": "Offset SOLA: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Estrazione F0] Completato | Successo: %s | Ignorati: %s | Errore: %s",
+ "[F0提取] 待处理:%s": "[Estrazione F0] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[Estrazione F0] Nessun audio in attesa; tutti i file sono stati ignorati",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[Estrazione F0] Avanzamento: %s/%s | Successo: %s | Ignorati: %s | %s",
+ "[F0提取][失败] %s": "[Estrazione F0][Errore] %s",
+ "[F0提取][失败] %s\n%s": "[Estrazione F0][Errore] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Caratteristiche HuBERT] Completato | Successo: %s | Ignorati: %s | Errore: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[Caratteristiche HuBERT] Nessun audio in attesa; ignorati: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[Caratteristiche HuBERT] Caricamento del modello: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[Caratteristiche HuBERT] Dispositivo: %s | Pending: %s | Ignorati: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[Caratteristiche HuBERT] Avanzamento: %s/%s | Successo: %s | Errore: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[Caratteristiche HuBERT][Errore] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[Caratteristiche HuBERT][Errore] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[Caratteristiche HuBERT][Errore] Modello not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[Suddivisione dati] Sottoattività completata | Successo: %s | Errore: %s",
+ "[数据切分] 完成": "[Suddivisione dati] Completato",
+ "[数据切分] 开始": "[Suddivisione dati] Avviato",
+ "[数据切分] 待处理:%s | 进程数:%s": "[Suddivisione dati] Pending: %s | Processi: %s",
+ "[数据切分] 进度:%s/%s | %s": "[Suddivisione dati] Avanzamento: %s/%s | %s",
+ "[数据切分][失败] %s": "[Suddivisione dati][Errore] %s",
+ "[数据切分][失败] %s\n%s": "[Suddivisione dati][Errore] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[Suddivisione dati][Ignorati] Segmento audio non valido o anomalo: %s_%s | Picco: %s",
+ "[索引训练] 写入进度:%s/%s": "[Addestramento indice] Avanzamento scrittura: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[Addestramento indice] Indice collegato alla cartella esterna: %s",
+ "[索引训练] 成功构建索引:%s": "[Addestramento indice] Indice creato correttamente: %s",
+ "[索引训练] 正在写入特征向量": "[Addestramento indice] Aggiunta dei vettori delle caratteristiche",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[Addestramento indice] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[Addestramento indice] Addestramento dell'indice",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[Addestramento indice] Forma caratteristiche: %s | Numero IVF: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[Addestramento indice][Errore] Impossibile collegare l'indice alla cartella esterna: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[Addestramento indice][Errore] Clustering non riuscito; continuazione con le caratteristiche originali\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[Addestramento indice][Errore] Estrarre prima le caratteristiche",
+ "ckpt处理": "Elaborazione ckpt",
+ "index文件路径不可包含中文": "Il percorso del file index non può contenere caratteri cinesi",
+ "pth文件路径不可包含中文": "pth è un'app per il futuro",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "Configurazione GPU RMVPE separata da trattini; ad esempio 0-0-1 avvia due processi sulla GPU 0 e uno sulla GPU 1",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "Passaggio 1: compilare la configurazione sperimentale. ",
+ "step1:正在处理数据": "Passaggio 1: elaborazione dei dati",
+ "step2:正在提取音高&正在提取特征": "step2:Estrazione dell'intonazione e delle caratteristiche",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "Passaggio 2a: attraversa automaticamente tutti i file nella cartella di addestramento che possono essere decodificati in audio ed esegui la normalizzazione delle sezioni. ",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "Passaggio 2b: utilizzare la CPU per estrarre il tono (se il modello ha il tono), utilizzare la GPU per estrarre le caratteristiche (selezionare l'indice GPU):",
+ "step3: 填写训练设置, 开始训练模型和索引": "Passaggio 3: compilare le impostazioni di addestramento e avviare l'addestramento del modello e dell'indice",
+ "step3a:正在训练模型": "Passaggio 3a: è iniziato l'addestramento del modello",
+ "step3b:正在训练索引": "passaggio 3b: addestramento dell’indice",
+ "……仅显示最近10条失败记录": "…Vengono mostrati solo gli ultimi 10 errori",
+ "……已省略前%s行,仅显示最新状态": "…Omesse le prime %s righe; viene mostrato solo lo stato più recente",
+ "一键训练": "Addestramento con un clic",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "È anche possibile scegliere più file audio; la cartella ha la priorità",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "Separazione in batch di voce e accompagnamento tramite modelli UVR5.
È possibile scegliere un modello che preserva la voce oppure usare i modelli DeEcho/DeReverb per rimuovere eco e riverbero.",
+ "仅支持pm和rmvpe音高提取算法": "Sono supportati solo i metodi di estrazione dell'intonazione pm e rmvpe",
+ "从训练检查点提取的模型": "Modellolo estratto da un checkpoint di addestramento",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "Inserisci gli indici GPU separati da '-', ad esempio 0-1-2 per utilizzare GPU 0, 1 e 2:",
+ "任务": "Attività",
+ "伴奏人声分离&去混响&去回声": "Separazione voce/accompagnamento",
+ "使用显卡:%s": "GPU in uso: %s",
+ "使用模型采样率": "Usa frequenza del modello",
+ "使用设备采样率": "Usa frequenza del dispositivo",
+ "保存名": "Salva nome:",
+ "保存的文件名, 默认空为和源文件同名": "Salva il nome del file (predefinito: uguale al file di origine):",
+ "保存的模型名不带后缀": "Nome del modello salvato (senza estensione):",
+ "保存频率save_every_epoch": "Frequenza di salvataggio (save_every_epoch):",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "Proteggi le consonanti senza voce e i suoni del respiro per evitare artefatti come il tearing nella musica elettronica. ",
+ "修改": "Modificare",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "Modifica le informazioni sul modello (supportato solo per i file di modello di piccole dimensioni estratti dalla cartella 'weights')",
+ "停止一键训练": "Interrompi addestramento con un clic",
+ "停止处理数据": "Interrompi pre-elaborazione dei dati",
+ "停止特征提取": "Interrompi estrazione delle caratteristiche",
+ "停止训练模型": "Interrompi addestramento del modello",
+ "停止训练索引": "Interrompi addestramento dell’indice",
+ "停止音频转换": "Arresta la conversione audio",
+ "全流程结束!": "Tutti i processi sono stati completati!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "Carica modello",
+ "加载预训练底模D路径": "Carica il percorso D del modello base pre-addestrato:",
+ "加载预训练底模G路径": "Carica il percorso G del modello base pre-addestrato:",
+ "单次推理": "Inferenza singola",
+ "卸载音色省显存": "Scarica la voce per risparmiare memoria della GPU:",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "Trasposizione (numero intero, numero di semitoni, alza di un'ottava: 12, abbassa di un'ottava: -12):",
+ "合成": "Sintesi",
+ "后处理重采样至最终采样率,0为不进行重采样": "Ricampiona l'audio di output in post-elaborazione alla frequenza di campionamento finale. ",
+ "否": "NO",
+ "响应阈值": "Soglia di risposta",
+ "响度因子": "fattore di sonorità",
+ "处理中": "Elaborazione",
+ "处理数据": "Processa dati",
+ "失败": "Errore",
+ "失败记录": "Registro errori",
+ "子进程执行失败,返回码:%s": "Il sottoprocesso non è riuscito, codice di uscita: %s",
+ "导出文件格式": "Formato file di esportazione",
+ "已停止": "Interrotto",
+ "已加载判别器预训练模型:%s": "Modellolo preaddestrato del discriminatore caricato: %s",
+ "已加载生成器预训练模型:%s": "Modellolo preaddestrato del generatore caricato: %s",
+ "已启用索引检索": "Ricerca nell'indice attivata",
+ "已完成": "Completato",
+ "已恢复判别器检查点": "Checkpoint del discriminatore ripristinato",
+ "常见问题解答": "FAQ (Domande frequenti)",
+ "常规设置": "Impostazioni generali",
+ "开始音频转换": "Avvia la conversione audio",
+ "当前": "Attuale",
+ "当前阶段": "Fase attuale",
+ "很遗憾您这没有能用的显卡来支持您训练": "Sfortunatamente, non è disponibile alcuna GPU compatibile per supportare l'addestramento.",
+ "性别因子/声线粗细": "Fattore di genere / spessore della voce",
+ "性能设置": "Impostazioni delle prestazioni",
+ "总训练轮数total_epoch": "Epoch totali di addestramento (total_epoch):",
+ "成功": "Successo",
+ "执行命令": "Comando",
+ "批量推理": "Inferenza batch",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "Conversione massiva. Inserisci il percorso della cartella che contiene i file da convertire o carica più file audio. I file convertiti finiranno nella cartella specificata. (default: opt) ",
+ "拖拽或点击上传待处理音频": "Trascina qui o fai clic per caricare l’audio da elaborare",
+ "指定输出主人声文件夹": "Specifica la cartella di output per le voci:",
+ "指定输出文件夹": "Specifica la cartella di output:",
+ "指定输出非主人声文件夹": "Specificare la cartella di output per l'accompagnamento:",
+ "推理时间(ms):": "Tempo di inferenza (ms):",
+ "推理耗时:%.2f秒": "Tempo di inferenza: %.2f seconds",
+ "推理音色": "Voce di inferenza:",
+ "提取": "Estrai",
+ "提取音高和处理数据使用的CPU进程数": "Numero di processi CPU utilizzati per l'estrazione del tono e l'elaborazione dei dati:",
+ "数据切分": "Suddivisione dati",
+ "无法停止": "Impossibile interrompere",
+ "无法读取音频:%s": "Impossibile leggere l'audio: %s",
+ "是": "SÌ",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "Salva solo l'ultimo file '.ckpt' per risparmiare spazio su disco:",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "Salva un piccolo modello finale nella cartella \"weights\" in ogni punto di salvataggio:",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "Memorizza nella cache tutti i set di addestramento nella memoria della GPU. ",
+ "显卡信息": "Informazioni GPU",
+ "未使用": "Non usato",
+ "未使用判别器预训练模型": "Modello preaddestrato del discriminatore non usato",
+ "未使用生成器预训练模型": "Modello preaddestrato del generatore non usato",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Configurazione del modello Roformer non trovata; verranno usati i valori predefiniti integrati",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "Nessuna GPU supportata rilevata; l'addestramento sulla CPU può richiedere molto più tempo",
+ "未运行": "Non avviato",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "Questo software è open source con licenza MIT.
Se non si accetta questa clausola, non è possibile utilizzare o fare riferimento a codici e file all'interno del pacchetto software. Contratto-LICENZA.txt per dettagli.",
+ "查看": "Visualizzazione",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "Visualizza le informazioni sul modello (supportato solo per file di modello piccoli estratti dalla cartella 'weights')",
+ "检测到模型类型:%s": "Tipo di modello rilevato: %s",
+ "检索特征占比": "Rapporto funzionalità di ricerca (controlla la forza dell'accento, troppo alto ha artefatti):",
+ "模型": "Modello",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "Informazioni modello: %s\nFrequenza di campionamento: %s\nGuida intonazione: %s\nVersione: %s",
+ "模型推理": "Inferenza del modello",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "Estrazione del modello (inserire il percorso del modello di file di grandi dimensioni nella cartella \"logs\"). ",
+ "模型是否带音高指导": "Se il modello ha una guida del tono:",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "Se il modello ha una guida del tono (necessario per il canto, facoltativo per il parlato):",
+ "模型是否带音高指导,1是0否": "Se il modello ha una guida del tono (1: sì, 0: no):",
+ "模型版本型号": "Versione dell'architettura del modello:",
+ "模型融合, 可用于测试音色融合": "Model fusion, può essere utilizzato per testare la fusione timbrica",
+ "模型融合失败:两个模型的结构不一致": "Unione dei modelli non riuscita: le architetture non corrispondono",
+ "模型训练": "Addestramento modello",
+ "模型路径": "Percorso al modello:",
+ "正在保存最终检查点:%s": "Salvataggio del checkpoint finale: %s",
+ "正在保存检查点 %s_e%s:%s": "Salvataggio del checkpoint %s_e%s: %s",
+ "正在加载RMVPE模型": "Caricamento del modello RMVPE",
+ "正在加载模型": "Caricamento del modello",
+ "正在启动": "Avvio in corso",
+ "正在收尾": "Finalizzazione",
+ "每张显卡的batch_size": "Dimensione batch per GPU:",
+ "淡入淡出长度": "Lunghezza dissolvenza",
+ "清理模型缓存": "Pulizia della cache del modello",
+ "版本": "Versione",
+ "特征": "Caratteristiche",
+ "特征提取": "Estrazione delle caratteristiche",
+ "状态": "Stato",
+ "独占 WASAPI 设备": "Dispositivo WASAPI esclusivo",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "Tonalità +12 consigliata per la conversione da maschio a femmina e tonalità -12 per la conversione da femmina a maschio. ",
+ "目标采样率": "Frequenza di campionamento target:",
+ "等待中": "In attesa",
+ "等待输入": "In attesa dell'input",
+ "算法延迟(ms):": "Latenza algoritmo (ms):",
+ "索引": "Indice",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "Indice non valido: usare added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "Ricerca nell'indice non riuscita",
+ "索引检索失败或未启用": "Ricerca nell'indice non riuscita o disattivata",
+ "索引训练": "Addestramento indice",
+ "耗时": "Tempo trascorso",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "Tempo trascorso: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "Fusione",
+ "要改的模型信息": "Informazioni sul modello da modificare:",
+ "要置入的模型信息": "Informazioni sul modello da posizionare:",
+ "训练": "Addestramento",
+ "训练已完成,正在保存最终模型": "Addestramento completato; salvataggio del modello finale",
+ "训练文件列表写入完成": "Elenco dei file di addestramento salvato",
+ "训练模型": "Addestra modello",
+ "训练特征索引": "Addestra indice delle caratteristiche",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "Addestramento completato. ",
+ "训练设备规则选择的精度:%s": "Precisione di addestramento scelta dalle regole del dispositivo: %s",
+ "训练轮次:{} [{:.0f}%]": "Epoca di addestramento: {} [{:.0f}%]",
+ "设备类型": "Tipo di dispositivo",
+ "请上传音频文件": "Caricare un file audio",
+ "请填写输出文件夹路径": "Inserire il percorso della cartella di output",
+ "请指定说话人id": "Si prega di specificare l'ID del locutore/cantante:",
+ "请选择index文件": "Selezionare un file .index",
+ "请选择pth文件": "Selezionare un file .pth",
+ "请选择说话人id": "Seleziona ID locutore/cantante:",
+ "转换": "Convertire",
+ "输入实验名": "Inserisci il nome dell'esperimento:",
+ "输入待处理音频文件夹路径": "Immettere il percorso della cartella audio da elaborare:",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "Immettere il percorso della cartella audio da elaborare (copiarlo dalla barra degli indirizzi del file manager):",
+ "输入待处理音频文件路径(默认是正确格式示例)": "Immettere il percorso del file audio da elaborare (l'impostazione predefinita è l'esempio di formato corretto):",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "Regola il ridimensionamento dell'inviluppo del volume. ",
+ "输入监听": "Monitor ingresso",
+ "输入训练文件夹路径": "Inserisci il percorso della cartella di addestramento:",
+ "输入设备": "Dispositivo di input",
+ "输入设备:%s:%s": "Dispositivo di ingresso: %s:%s",
+ "输入降噪": "Riduzione del rumore in ingresso",
+ "输出信息": "Informazioni sull'uscita",
+ "输出变声": "Conversione uscita",
+ "输出设备": "Dispositivo di uscita",
+ "输出设备:%s:%s": "Dispositivo di uscita: %s:%s",
+ "输出降噪": "Riduzione del rumore in uscita",
+ "输出音频(右下角三个点,点了可以下载)": "Esporta audio (clicca sui tre puntini in basso a destra per scaricarlo)",
+ "运行中": "In esecuzione",
+ "进度": "Avanzamento",
+ "选择.index文件": "Seleziona il file .index",
+ "选择.pth文件": "Seleziona il file .pth",
+ "选择模型": "Seleziona modello",
+ "选择索引": "Seleziona indice",
+ "选择音高提取算法": "Seleziona l’algoritmo di estrazione dell’intonazione",
+ "采样率:": "Frequenza di campionamento:",
+ "采样长度": "Lunghezza del campione",
+ "重载设备列表": "Ricaricare l'elenco dei dispositivi",
+ "错误信息:%s": "Dettagli errore: %s",
+ "错误:未知模型:%s": "Errore: modello sconosciuto: %s",
+ "音调设置": "Impostazioni del tono",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "L'audio ha più canali, ma il modello è mono; verrà calcolata la media di tutti i canali",
+ "音频设备": "Dispositivo audio",
+ "音高全部为0,该音频无意义,跳过:%s": "Tutti i valori di intonazione sono zero; questo audio è inutilizzabile e verrà ignorato: %s",
+ "音高算法": "Algoritmo di intonazione",
+ "额外推理时长": "Tempo di inferenza extra",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "Il pre-processing non ha generato audio di addestramento valido. Controlla il dataset e il registro.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "Il pre-processing non ha generato audio a 16 kHz. Estrazione e addestramento sono stati interrotti.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "I file prodotti dal pre-processing non corrispondono. Estrazione e addestramento sono stati interrotti.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "Estrazione HuBERT senza risultati validi. Addestramento interrotto.",
+ "F0提取没有生成有效结果,已停止训练": "Estrazione F0 senza risultati validi. Addestramento interrotto.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "Nessun audio valido per l’addestramento. Completa prima pre-processing ed estrazione delle caratteristiche.",
+ "已完成阶段": "Fasi completate",
+ "已成功": "Completato",
+ "刷新音色列表": "Aggiorna elenco voci",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "Percorso indice caratteristiche (rilevato dopo la scelta del modello; modificabile)",
+ "[索引训练] 外部索引链接已存在:%s": "[Addestramento indice] Il collegamento esterno esiste già: %s",
+ "[索引训练][跳过] added索引已存在:%s": "[Addestramento indice][Saltato] L’indice added esiste già: %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[Addestramento indice][Saltato] L’indice trained esiste già: %s",
+ "当前设备:%s | 推理精度:%s": "Dispositivo corrente: %s | Precisione di inferenza: %s"
+}
diff --git a/i18n/locale/ja_JP.json b/i18n/locale/ja_JP.json
new file mode 100644
index 0000000..da01393
--- /dev/null
+++ b/i18n/locale/ja_JP.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "ボーカル抽出の強度",
+ "%s → 成功": "%s → 成功",
+ "%s运行中,请先停止该任务": "%sを実行中です。先に停止してください",
+ "%s进程已终止": "%sプロセスを終了しました",
+ "====> 轮次:{} {}": "====> エポック: {} {}",
+ "A模型权重": "Aモデルの重み",
+ "A模型路径": "Aモデルのパス",
+ "B模型路径": "Bモデルのパス",
+ "CUDA可用:%s": "CUDA利用可能: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "F0とHuBERT特徴量の抽出",
+ "F0提取": "F0抽出",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0(最低共振周波数)カーブファイル(オプション、1行に1ピッチ、デフォルトのF0(最低共振周波数)とエレベーションを置き換えます。)",
+ "HuBERT特征": "HuBERT特徴量",
+ "Index Rate": "Index Rate",
+ "RVC模型路径": "RVCモデルパス",
+ "SOLA偏移:%d": "SOLAオフセット: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0抽出] 完了 | 成功: %s | スキップ: %s | 失敗: %s",
+ "[F0提取] 待处理:%s": "[F0抽出] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0抽出] 処理対象の音声はなく、すべてスキップ済みです",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0抽出] 進捗: %s/%s | 成功: %s | スキップ: %s | %s",
+ "[F0提取][失败] %s": "[F0抽出][失敗] %s",
+ "[F0提取][失败] %s\n%s": "[F0抽出][失敗] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT特徴量] 完了 | 成功: %s | スキップ: %s | 失敗: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT特徴量] 処理対象の音声なし、スキップ済み: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT特徴量] モデルを読み込み中: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT特徴量] デバイス: %s | Pending: %s | スキップ: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT特徴量] 進捗: %s/%s | 成功: %s | 失敗: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT特徴量][失敗] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT特徴量][失敗] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT特徴量][失敗] モデル not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[データ分割] サブタスク完了 | 成功: %s | 失敗: %s",
+ "[数据切分] 完成": "[データ分割] 完了",
+ "[数据切分] 开始": "[データ分割] 開始",
+ "[数据切分] 待处理:%s | 进程数:%s": "[データ分割] Pending: %s | プロセス数: %s",
+ "[数据切分] 进度:%s/%s | %s": "[データ分割] 進捗: %s/%s | %s",
+ "[数据切分][失败] %s": "[データ分割][失敗] %s",
+ "[数据切分][失败] %s\n%s": "[データ分割][失敗] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[データ分割][スキップ] 無効または異常な音声区間: %s_%s | ピーク: %s",
+ "[索引训练] 写入进度:%s/%s": "[インデックス学習] 書き込み進捗: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[インデックス学習] インデックスを外部フォルダーへリンクしました: %s",
+ "[索引训练] 成功构建索引:%s": "[インデックス学習] インデックスを作成しました: %s",
+ "[索引训练] 正在写入特征向量": "[インデックス学習] 特徴ベクトルを追加中",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[インデックス学習] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[インデックス学習] インデックスを学習中",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[インデックス学習] 特徴量形状: %s | IVF数: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[インデックス学習][失敗] インデックスを外部フォルダーへリンクできませんでした: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[インデックス学習][失敗] クラスタリングに失敗したため、元の特徴量で続行します\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[インデックス学習][失敗] 先に特徴抽出を実行してください",
+ "ckpt处理": "ckptファイルの処理",
+ "index文件路径不可包含中文": "indexファイルのパスに漢字を含んではいけません",
+ "pth文件路径不可包含中文": "pthファイルのパスに漢字を含んではいけません",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "rmvpeカード番号設定:異なるプロセスに使用するカード番号を入力する。例えば、0-0-1でカード0に2つのプロセス、カード1に1つのプロセスを実行する。",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "ステップ1:実験設定を入力します。実験データはlogsに保存され、各実験にはフォルダーがあります。実験名のパスを手動で入力する必要があり、実験設定、ログ、トレーニングされたモデルファイルが含まれます。",
+ "step1:正在处理数据": "step1:処理中のデータ",
+ "step2:正在提取音高&正在提取特征": "step2:ピッチ抽出と特徴抽出",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "ステップ2a: 訓練フォルダー内のすべての音声ファイルを自動的に探索し、スライスと正規化を行い、2つのwavフォルダーを実験ディレクトリに生成します。現在は一人でのトレーニングのみをサポートしています。",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "ステップ2b: CPUを使用して音高を抽出する(モデルに音高がある場合)、GPUを使用して特徴を抽出する(GPUの番号を選択する)",
+ "step3: 填写训练设置, 开始训练模型和索引": "ステップ3: トレーニング設定を入力して、モデルとインデックスのトレーニングを開始します",
+ "step3a:正在训练模型": "step3a:トレーニング中のモデル",
+ "step3b:正在训练索引": "ステップ3b:インデックスをトレーニング中",
+ "……仅显示最近10条失败记录": "…直近10件の失敗のみ表示しています",
+ "……已省略前%s行,仅显示最新状态": "…先頭%s行を省略し、最新状態のみ表示しています",
+ "一键训练": "ワンクリックトレーニング",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "複数のオーディオファイルをインポートすることもできます。フォルダパスが存在する場合、この入力は無視されます。",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "UVR5モデルを使用して、ボーカルと伴奏を一括分離します。
ボーカル保持モデルを選択するか、DeEcho/DeReverbモデルでエコーや残響を除去できます。",
+ "仅支持pm和rmvpe音高提取算法": "ピッチ抽出方式はpmとrmvpeのみ対応しています",
+ "从训练检查点提取的模型": "学習チェックポイントから抽出したモデル",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "ハイフンで区切って使用するGPUの番号を入力します。例えば0-1-2はGPU0、GPU1、GPU2を使用します",
+ "任务": "タスク",
+ "伴奏人声分离&去混响&去回声": "伴奏ボーカル分離&残響除去&エコー除去",
+ "使用显卡:%s": "使用GPU: %s",
+ "使用模型采样率": "モデルのサンプルレートを使用",
+ "使用设备采样率": "デバイスのサンプルレートを使用",
+ "保存名": "保存ファイル名",
+ "保存的文件名, 默认空为和源文件同名": "保存するファイル名、デフォルトでは空欄で元のファイル名と同じ名前になります",
+ "保存的模型名不带后缀": "拡張子のない保存するモデル名",
+ "保存频率save_every_epoch": "エポックごとの保存頻度",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "明確な子音と呼吸音を保護し、電子音の途切れやその他のアーティファクトを防止します。0.5でオフになります。下げると保護が強化されますが、indexの効果が低下する可能性があります。",
+ "修改": "変更",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "モデル情報の修正(weightsフォルダから抽出された小さなモデルファイルのみ対応)",
+ "停止一键训练": "ワンクリック学習を停止",
+ "停止处理数据": "データ前処理を停止",
+ "停止特征提取": "特徴抽出を停止",
+ "停止训练模型": "モデル学習を停止",
+ "停止训练索引": "インデックス学習を停止",
+ "停止音频转换": "音声変換を停止",
+ "全流程结束!": "全工程が完了!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "モデルをロード",
+ "加载预训练底模D路径": "事前学習済みのDモデルのパス",
+ "加载预训练底模G路径": "事前学習済みのGモデルのパス",
+ "单次推理": "単発推論",
+ "卸载音色省显存": "音源を削除してメモリを節約",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "ピッチ変更(整数、半音数、上下オクターブ12-12)",
+ "合成": "合成",
+ "后处理重采样至最终采样率,0为不进行重采样": "最終的なサンプリングレートへのポストプロセッシングのリサンプリング リサンプリングしない場合は0",
+ "否": "いいえ",
+ "响应阈值": "反応閾値",
+ "响度因子": "ラウドネス係数",
+ "处理中": "処理中",
+ "处理数据": "データ処理",
+ "失败": "失敗",
+ "失败记录": "失敗記録",
+ "子进程执行失败,返回码:%s": "子プロセスが終了コード付きで失敗しました: %s",
+ "导出文件格式": "エクスポート形式",
+ "已停止": "停止済み",
+ "已加载判别器预训练模型:%s": "識別器の事前学習モデルを読み込みました: %s",
+ "已加载生成器预训练模型:%s": "生成器の事前学習モデルを読み込みました: %s",
+ "已启用索引检索": "インデックス検索を有効化しました",
+ "已完成": "完了",
+ "已恢复判别器检查点": "識別器チェックポイントを復元しました",
+ "常见问题解答": "よくある質問",
+ "常规设置": "一般設定",
+ "开始音频转换": "音声変換を開始",
+ "当前": "現在",
+ "当前阶段": "現在の段階",
+ "很遗憾您这没有能用的显卡来支持您训练": "トレーニングに対応したGPUが動作しないのは残念です。",
+ "性别因子/声线粗细": "性別係数/声質の太さ",
+ "性能设置": "パフォーマンス設定",
+ "总训练轮数total_epoch": "総エポック数",
+ "成功": "成功",
+ "执行命令": "実行コマンド",
+ "批量推理": "一括推論",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "一括変換、変換する音声フォルダを入力、または複数の音声ファイルをアップロードし、指定したフォルダ(デフォルトのopt)に変換した音声を出力します。",
+ "拖拽或点击上传待处理音频": "処理する音声をドラッグ&ドロップするか、クリックしてアップロード",
+ "指定输出主人声文件夹": "マスターの出力音声フォルダーを指定する",
+ "指定输出文件夹": "出力フォルダを指定してください",
+ "指定输出非主人声文件夹": "マスター以外の出力音声フォルダーを指定する",
+ "推理时间(ms):": "推論時間(ms):",
+ "推理耗时:%.2f秒": "推論時間: %.2f seconds",
+ "推理音色": "音源推論",
+ "提取": "抽出",
+ "提取音高和处理数据使用的CPU进程数": "ピッチの抽出やデータ処理に使用するCPUスレッド数",
+ "数据切分": "データ分割",
+ "无法停止": "停止できません",
+ "无法读取音频:%s": "音声を読み込めませんでした: %s",
+ "是": "はい",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "ハードディスク容量を節約するため、最新のckptファイルのみを保存しますか?",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "各保存時点の小モデルを全部weightsフォルダに保存するかどうか",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "すべてのトレーニングデータをメモリにキャッシュするかどうか。10分以下の小さなデータはキャッシュしてトレーニングを高速化できますが、大きなデータをキャッシュするとメモリが破裂し、あまり速度が上がりません。",
+ "显卡信息": "GPU情報",
+ "未使用": "未使用",
+ "未使用判别器预训练模型": "識別器の事前学習モデルは未使用です",
+ "未使用生成器预训练模型": "生成器の事前学習モデルは未使用です",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Roformerモデル設定が見つからないため、組み込みの既定値を使用します",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "対応GPUが検出されませんでした。CPUでの学習には時間がかかる場合があります",
+ "未运行": "未実行",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "本ソフトウェアはMITライセンスに基づくオープンソースであり、製作者は本ソフトウェアに対していかなる責任を持ちません。本ソフトウェアの利用者および本ソフトウェアから派生した音源(成果物)を配布する者は、本ソフトウェアに対して自身で責任を負うものとします。
この条項に同意しない場合、パッケージ内のコードやファイルを使用や参照を禁じます。詳しくはLICENSEをご覧ください。",
+ "查看": "表示",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "モデル情報を表示する(小さいモデルファイルはweightsフォルダーからのみサポートされています)",
+ "检测到模型类型:%s": "検出したモデル種別: %s",
+ "检索特征占比": "検索特徴率",
+ "模型": "モデル",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "モデル情報: %s\nサンプルレート: %s\nピッチガイド: %s\nバージョン: %s",
+ "模型推理": "モデル推論",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "モデル抽出(ログフォルダー内の大きなファイルのモデルパスを入力)、モデルを半分までトレーニングし、自動的に小さいファイルモデルを保存しなかったり、中間モデルをテストしたい場合に適用されます。",
+ "模型是否带音高指导": "モデルに音高ガイドを付けるかどうか",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "モデルに音高ガイドがあるかどうか(歌唱には必要ですが、音声には必要ありません)",
+ "模型是否带音高指导,1是0否": "モデルに音高ガイドを付けるかどうか、1は付ける、0は付けない",
+ "模型版本型号": "モデルのバージョン",
+ "模型融合, 可用于测试音色融合": "モデルのマージ、音源のマージテストに使用できます",
+ "模型融合失败:两个模型的结构不一致": "モデルの構造が一致しないため、結合に失敗しました",
+ "模型训练": "モデル学習",
+ "模型路径": "モデルパス",
+ "正在保存最终检查点:%s": "最終チェックポイントを保存中: %s",
+ "正在保存检查点 %s_e%s:%s": "チェックポイントを保存中 %s_e%s: %s",
+ "正在加载RMVPE模型": "RMVPEモデルを読み込み中",
+ "正在加载模型": "モデルを読み込み中",
+ "正在启动": "起動中",
+ "正在收尾": "終了処理中",
+ "每张显卡的batch_size": "GPUごとのバッチサイズ",
+ "淡入淡出长度": "フェードイン/フェードアウト長",
+ "清理模型缓存": "モデルキャッシュを消去中",
+ "版本": "バージョン",
+ "特征": "特徴量",
+ "特征提取": "特徴抽出",
+ "状态": "状態",
+ "独占 WASAPI 设备": "WASAPI排他デバイス",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "男性から女性へは+12キーをお勧めします。女性から男性へは-12キーをお勧めします。音域が広すぎて音質が劣化した場合は、適切な音域に自分で調整してください。",
+ "目标采样率": "目標サンプリングレート",
+ "等待中": "待機中",
+ "等待输入": "入力待ち",
+ "算法延迟(ms):": "アルゴリズム遅延(ms):",
+ "索引": "インデックス",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "無効なインデックスです。使用してください: added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "インデックス検索に失敗しました",
+ "索引检索失败或未启用": "インデックス検索に失敗したか無効です",
+ "索引训练": "インデックス学習",
+ "耗时": "所要時間",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "所要時間: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "マージ",
+ "要改的模型信息": "変更するモデル情報",
+ "要置入的模型信息": "挿入するモデル情報",
+ "训练": "トレーニング",
+ "训练已完成,正在保存最终模型": "学習が完了しました。最終モデルを保存しています",
+ "训练文件列表写入完成": "学習ファイル一覧を書き込みました",
+ "训练模型": "モデルのトレーニング",
+ "训练特征索引": "特徴インデックスのトレーニング",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "トレーニング終了時に、トレーニングログやフォルダ内のtrain.logを確認することができます",
+ "训练设备规则选择的精度:%s": "デバイス規則で選択された学習精度: %s",
+ "训练轮次:{} [{:.0f}%]": "学習エポック: {} [{:.0f}%]",
+ "设备类型": "デバイス種別",
+ "请上传音频文件": "音声ファイルをアップロードしてください",
+ "请填写输出文件夹路径": "出力フォルダーのパスを入力してください",
+ "请指定说话人id": "話者IDを指定してください",
+ "请选择index文件": "indexファイルを選択してください",
+ "请选择pth文件": "pthファイルを選択してください",
+ "请选择说话人id": "話者IDを選択してください",
+ "转换": "変換",
+ "输入实验名": "モデル名",
+ "输入待处理音频文件夹路径": "処理するオーディオファイルのフォルダパスを入力してください",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "処理対象音声フォルダーのパスを入力してください(エクスプローラーのアドレスバーからコピーしてください)",
+ "输入待处理音频文件路径(默认是正确格式示例)": "処理対象音声ファイルのパスを入力してください(デフォルトは正しいフォーマットの例です)",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "入力ソースの音量エンベロープと出力音量エンベロープの融合率 1に近づくほど、出力音量エンベロープの割合が高くなる",
+ "输入监听": "入力モニター",
+ "输入训练文件夹路径": "トレーニング用フォルダのパスを入力してください",
+ "输入设备": "入力デバイス",
+ "输入设备:%s:%s": "入力デバイス: %s:%s",
+ "输入降噪": "入力ノイズの低減",
+ "输出信息": "出力情報",
+ "输出变声": "出力変声",
+ "输出设备": "出力デバイス",
+ "输出设备:%s:%s": "出力デバイス: %s:%s",
+ "输出降噪": "出力ノイズの低減",
+ "输出音频(右下角三个点,点了可以下载)": "出力音声(右下の三点をクリックしてダウンロードできます)",
+ "运行中": "実行中",
+ "进度": "進捗",
+ "选择.index文件": ".indexファイルを選択",
+ "选择.pth文件": ".pthファイルを選択",
+ "选择模型": "モデルを選択",
+ "选择索引": "インデックスを選択",
+ "选择音高提取算法": "ピッチ抽出アルゴリズムを選択",
+ "采样率:": "サンプルレート:",
+ "采样长度": "サンプル長",
+ "重载设备列表": "デバイスリストをリロードする",
+ "错误信息:%s": "エラー詳細: %s",
+ "错误:未知模型:%s": "エラー: 不明なモデル: %s",
+ "音调设置": "音程設定",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "音声は複数チャンネルですがモデルはモノラルのため、全チャンネルを平均します",
+ "音频设备": "オーディオデバイス",
+ "音高全部为0,该音频无意义,跳过:%s": "ピッチがすべて0のため、この音声は無効としてスキップします: %s",
+ "音高算法": "ピッチ方式",
+ "额外推理时长": "追加推論時間",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "データ前処理で有効な学習音声が生成されませんでした。データセットと前処理ログを確認してください。",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "データ前処理で16 kHz音声が生成されなかったため、特徴抽出と学習を停止しました。",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "データ前処理の出力ファイルが一致しないため、特徴抽出と学習を停止しました。",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT特徴抽出で有効な結果が生成されなかったため、学習を停止しました。",
+ "F0提取没有生成有效结果,已停止训练": "F0抽出で有効な結果が生成されなかったため、学習を停止しました。",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "学習に使用できる有効な音声がありません。先にデータ前処理と特徴抽出を完了してください。",
+ "已完成阶段": "完了した段階",
+ "已成功": "成功",
+ "刷新音色列表": "音色リストを更新",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "特徴インデックスのパス(モデル選択後に自動照合、手動編集可)",
+ "[索引训练] 外部索引链接已存在:%s": "[インデックス学習] 外部インデックスリンクは既に存在します:%s",
+ "[索引训练][跳过] added索引已存在:%s": "[インデックス学習][スキップ] addedインデックスは既に存在します:%s",
+ "[索引训练][跳过] trained索引已存在:%s": "[インデックス学習][スキップ] trainedインデックスは既に存在します:%s",
+ "当前设备:%s | 推理精度:%s": "現在のデバイス:%s | 推論精度:%s"
+}
diff --git a/i18n/locale/ko_KR.json b/i18n/locale/ko_KR.json
new file mode 100644
index 0000000..b4b7b5c
--- /dev/null
+++ b/i18n/locale/ko_KR.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "보컬 추출 강도",
+ "%s → 成功": "%s → 성공",
+ "%s运行中,请先停止该任务": "%s이(가) 실행 중입니다. 먼저 중지하세요",
+ "%s进程已终止": "%s 프로세스가 종료되었습니다",
+ "====> 轮次:{} {}": "====> 에포크: {} {}",
+ "A模型权重": "A 모델 가중치",
+ "A模型路径": "A 모델 경로",
+ "B模型路径": "B 모델 경로",
+ "CUDA可用:%s": "CUDA 사용 가능: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "F0 및 HuBERT 특징 추출",
+ "F0提取": "F0 추출",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0 곡선 파일, 선택적, 한 줄에 하나의 피치, 기본 F0 및 음높이 조절 대체",
+ "HuBERT特征": "HuBERT 특징",
+ "Index Rate": "인덱스 비율",
+ "RVC模型路径": "RVC 모델 경로",
+ "SOLA偏移:%d": "SOLA 오프셋: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0 추출] 완료 | 성공: %s | 건너뜀: %s | 실패: %s",
+ "[F0提取] 待处理:%s": "[F0 추출] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0 추출] 처리할 오디오가 없으며 모든 파일을 건너뛰었습니다",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0 추출] 진행률: %s/%s | 성공: %s | 건너뜀: %s | %s",
+ "[F0提取][失败] %s": "[F0 추출][실패] %s",
+ "[F0提取][失败] %s\n%s": "[F0 추출][실패] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT 특징] 완료 | 성공: %s | 건너뜀: %s | 실패: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT 특징] 처리할 오디오 없음, 건너뜀: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT 특징] 모델 불러오는 중: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT 특징] 장치: %s | Pending: %s | 건너뜀: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT 특징] 진행률: %s/%s | 성공: %s | 실패: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT 특징][실패] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT 특징][실패] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT 특징][실패] 모델 not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[데이터 분할] 하위 작업 완료 | 성공: %s | 실패: %s",
+ "[数据切分] 完成": "[데이터 분할] 완료",
+ "[数据切分] 开始": "[데이터 분할] 시작",
+ "[数据切分] 待处理:%s | 进程数:%s": "[데이터 분할] Pending: %s | 프로세스 수: %s",
+ "[数据切分] 进度:%s/%s | %s": "[데이터 분할] 진행률: %s/%s | %s",
+ "[数据切分][失败] %s": "[데이터 분할][실패] %s",
+ "[数据切分][失败] %s\n%s": "[데이터 분할][실패] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[데이터 분할][건너뜀] 잘못되었거나 비정상적인 오디오 구간: %s_%s | 피크: %s",
+ "[索引训练] 写入进度:%s/%s": "[인덱스 학습] 쓰기 진행률: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[인덱스 학습] 인덱스를 외부 폴더에 연결했습니다: %s",
+ "[索引训练] 成功构建索引:%s": "[인덱스 학습] 인덱스를 성공적으로 생성했습니다: %s",
+ "[索引训练] 正在写入特征向量": "[인덱스 학습] 특징 벡터 추가 중",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[인덱스 학습] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[인덱스 학습] 인덱스 학습 중",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[인덱스 학습] 특징 형태: %s | IVF 수: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[인덱스 학습][실패] 인덱스를 외부 폴더에 연결하지 못했습니다: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[인덱스 학습][실패] 클러스터링에 실패하여 원본 특징으로 계속합니다\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[인덱스 학습][실패] 먼저 특징을 추출하세요",
+ "ckpt处理": "ckpt 처리",
+ "index文件路径不可包含中文": "index 파일 경로는 중국어를 포함할 수 없음",
+ "pth文件路径不可包含中文": "pth 파일 경로는 중국어를 포함할 수 없음",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "rmvpe 카드 번호 설정: -로 구분된 입력 사용 카드 번호, 예: 0-0-1은 카드 0에서 2개 프로세스, 카드 1에서 1개 프로세스 실행",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "step1: 실험 구성 작성. 실험 데이터는 logs에 저장, 각 실험은 하나의 폴더, 수동으로 실험 이름 경로 입력 필요, 실험 구성, 로그, 훈련된 모델 파일 포함.",
+ "step1:正在处理数据": "step1: 데이터 처리 중",
+ "step2:正在提取音高&正在提取特征": "step2: 음높이 추출 & 특징 추출 중",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "step2a: 훈련 폴더 아래 모든 오디오로 디코딩 가능한 파일을 자동 순회하며 슬라이스 정규화 진행, 실험 디렉토리 아래 2개의 wav 폴더 생성; 현재 단일 사용자 훈련만 지원.",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "step2b: CPU를 사용하여 음높이 추출(모델이 음높이 포함 시), GPU를 사용하여 특징 추출(카드 번호 선택)",
+ "step3: 填写训练设置, 开始训练模型和索引": "step3: 훈련 설정 작성, 모델 및 인덱스 훈련 시작",
+ "step3a:正在训练模型": "step3a: 모델 훈련 중",
+ "step3b:正在训练索引": "3b단계: 인덱스 학습 중",
+ "……仅显示最近10条失败记录": "…최근 실패 10개만 표시합니다",
+ "……已省略前%s行,仅显示最新状态": "…앞의 %s줄을 생략하고 최신 상태만 표시합니다",
+ "一键训练": "원클릭 훈련",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "여러 오디오 파일을 일괄 입력할 수도 있음, 둘 중 하나 선택, 폴더 우선 읽기",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "UVR5 모델을 사용하여 보컬과 반주를 일괄 분리합니다.
보컬 보존 모델을 선택하거나 DeEcho/DeReverb 모델로 에코와 잔향을 제거할 수 있습니다.",
+ "仅支持pm和rmvpe音高提取算法": "피치 추출 방식은 pm과 rmvpe만 지원합니다",
+ "从训练检查点提取的模型": "학습 체크포인트에서 추출한 모델",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "-로 구분하여 입력하는 카드 번호, 예: 0-1-2는 카드 0, 카드 1, 카드 2 사용",
+ "任务": "작업",
+ "伴奏人声分离&去混响&去回声": "반주 인간 목소리 분리 & 혼효음 제거 & 에코 제거",
+ "使用显卡:%s": "사용 GPU: %s",
+ "使用模型采样率": "모델 샘플링 레이트 사용",
+ "使用设备采样率": "장치 샘플링 레이트 사용",
+ "保存名": "저장 이름",
+ "保存的文件名, 默认空为和源文件同名": "저장될 파일명, 기본적으로 빈 공간은 원본 파일과 동일한 이름으로",
+ "保存的模型名不带后缀": "저장된 모델명은 접미사 없음",
+ "保存频率save_every_epoch": "저장 빈도 save_every_epoch",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "청자음과 호흡 소리를 보호, 전자음 찢김 등의 아티팩트 방지, 0.5까지 올려서 비활성화, 낮추면 보호 강도 증가하지만 인덱스 효과 감소 가능성 있음",
+ "修改": "수정",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "모델 정보 수정(오직 weights 폴더 아래에서 추출된 작은 모델 파일만 지원)",
+ "停止一键训练": "원클릭 학습 중지",
+ "停止处理数据": "데이터 전처리 중지",
+ "停止特征提取": "특징 추출 중지",
+ "停止训练模型": "모델 학습 중지",
+ "停止训练索引": "인덱스 학습 중지",
+ "停止音频转换": "오디오 변환 중지",
+ "全流程结束!": "전체 과정 완료!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "모델 로드",
+ "加载预训练底模D路径": "미리 훈련된 베이스 모델 D 경로 로드",
+ "加载预训练底模G路径": "미리 훈련된 베이스 모델 G 경로 로드",
+ "单次推理": "단일 추론",
+ "卸载音色省显存": "음색 언로드로 디스플레이 메모리 절약",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "키 변경(정수, 반음 수, 옥타브 상승 12, 옥타브 하강 -12)",
+ "合成": "합성",
+ "后处理重采样至最终采样率,0为不进行重采样": "후처리 재샘플링을 최종 샘플링 레이트로, 0은 재샘플링하지 않음",
+ "否": "아니오",
+ "响应阈值": "응답 임계값",
+ "响度因子": "음량 인자",
+ "处理中": "처리 중",
+ "处理数据": "데이터 처리",
+ "失败": "실패",
+ "失败记录": "실패 기록",
+ "子进程执行失败,返回码:%s": "하위 프로세스가 종료 코드와 함께 실패했습니다: %s",
+ "导出文件格式": "내보낼 파일 형식",
+ "已停止": "중지됨",
+ "已加载判别器预训练模型:%s": "판별기 사전 학습 모델을 불러왔습니다: %s",
+ "已加载生成器预训练模型:%s": "생성기 사전 학습 모델을 불러왔습니다: %s",
+ "已启用索引检索": "인덱스 검색 활성화됨",
+ "已完成": "완료",
+ "已恢复判别器检查点": "판별기 체크포인트를 복원했습니다",
+ "常见问题解答": "자주 묻는 질문",
+ "常规设置": "일반 설정",
+ "开始音频转换": "오디오 변환 시작",
+ "当前": "현재",
+ "当前阶段": "현재 단계",
+ "很遗憾您这没有能用的显卡来支持您训练": "사용 가능한 그래픽 카드가 없어 훈련을 지원할 수 없습니다",
+ "性别因子/声线粗细": "성별 계수 / 음색 굵기",
+ "性能设置": "성능 설정",
+ "总训练轮数total_epoch": "총 훈련 라운드 수 total_epoch",
+ "成功": "성공",
+ "执行命令": "실행 명령",
+ "批量推理": "일괄 추론",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "일괄 변환, 변환할 오디오 파일 폴더 입력 또는 여러 오디오 파일 업로드, 지정된 폴더(기본값 opt)에 변환된 오디오 출력.",
+ "拖拽或点击上传待处理音频": "처리할 오디오를 끌어다 놓거나 클릭하여 업로드",
+ "指定输出主人声文件夹": "주된 목소리 출력 폴더 지정",
+ "指定输出文件夹": "출력 파일 폴더 지정",
+ "指定输出非主人声文件夹": "주된 목소리가 아닌 출력 폴더 지정",
+ "推理时间(ms):": "추론 시간(ms):",
+ "推理耗时:%.2f秒": "추론 시간: %.2f seconds",
+ "推理音色": "추론 음색",
+ "提取": "추출",
+ "提取音高和处理数据使用的CPU进程数": "음높이 추출 및 데이터 처리에 사용되는 CPU 프로세스 수",
+ "数据切分": "데이터 분할",
+ "无法停止": "중지할 수 없음",
+ "无法读取音频:%s": "오디오를 읽지 못했습니다: %s",
+ "是": "예",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "디스크 공간을 절약하기 위해 최신 ckpt 파일만 저장할지 여부",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "저장 시마다 최종 소형 모델을 weights 폴더에 저장할지 여부",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "모든 훈련 세트를 VRAM에 캐시할지 여부. 10분 미만의 소량 데이터는 캐시하여 훈련 속도를 높일 수 있지만, 대량 데이터 캐시는 VRAM을 과부하시키고 속도를 크게 향상시키지 못함",
+ "显卡信息": "그래픽 카드 정보",
+ "未使用": "사용 안 함",
+ "未使用判别器预训练模型": "판별기 사전 학습 모델을 사용하지 않음",
+ "未使用生成器预训练模型": "생성기 사전 학습 모델을 사용하지 않음",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Roformer 모델 설정을 찾지 못해 내장 기본값을 사용합니다",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "지원되는 GPU가 감지되지 않았습니다. CPU 학습은 훨씬 오래 걸릴 수 있습니다",
+ "未运行": "실행 안 됨",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "이 소프트웨어는 MIT 라이선스로 공개되며, 저자는 소프트웨어에 대해 어떠한 통제권도 가지지 않습니다. 모든 귀책사유는 소프트웨어 사용자 및 소프트웨어에서 생성된 결과물을 사용하는 당사자에게 있습니다.
해당 조항을 인정하지 않는 경우, 소프트웨어 패키지의 어떠한 코드나 파일도 사용하거나 인용할 수 없습니다. 자세한 내용은 루트 디렉토리의 LICENSE를 참조하세요.",
+ "查看": "보기",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "모델 정보 보기(오직 weights 폴더에서 추출된 소형 모델 파일만 지원)",
+ "检测到模型类型:%s": "감지된 모델 유형: %s",
+ "检索特征占比": "검색 특징 비율",
+ "模型": "모델",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "모델 정보: %s\n샘플링 레이트: %s\n피치 가이드: %s\n버전: %s",
+ "模型推理": "모델 추론",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "모델 추출(logs 폴더 아래의 큰 파일 모델 경로 입력), 훈련 중간에 중단한 모델의 자동 추출 및 소형 파일 모델 저장이 안 되거나 중간 모델을 테스트하고 싶은 경우에 적합",
+ "模型是否带音高指导": "모델이 음높이 지도를 포함하는지 여부",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "모델이 음높이 지도를 포함하는지 여부(노래에는 반드시 필요, 음성에는 필요 없음)",
+ "模型是否带音高指导,1是0否": "모델이 음높이 지도를 포함하는지 여부, 1은 예, 0은 아니오",
+ "模型版本型号": "모델 버전 및 모델",
+ "模型融合, 可用于测试音色融合": "모델 융합, 음색 융합 테스트에 사용 가능",
+ "模型融合失败:两个模型的结构不一致": "두 모델의 구조가 일치하지 않아 모델 병합에 실패했습니다",
+ "模型训练": "모델 학습",
+ "模型路径": "모델 경로",
+ "正在保存最终检查点:%s": "최종 체크포인트 저장 중: %s",
+ "正在保存检查点 %s_e%s:%s": "체크포인트 저장 중 %s_e%s: %s",
+ "正在加载RMVPE模型": "RMVPE 모델 불러오는 중",
+ "正在加载模型": "모델 불러오는 중",
+ "正在启动": "시작 중",
+ "正在收尾": "마무리 중",
+ "每张显卡的batch_size": "각 그래픽 카드의 batch_size",
+ "淡入淡出长度": "페이드 인/아웃 길이",
+ "清理模型缓存": "모델 캐시 정리 중",
+ "版本": "버전",
+ "特征": "특징",
+ "特征提取": "특징 추출",
+ "状态": "상태",
+ "独占 WASAPI 设备": "WASAPI 독점 장치",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "남성에서 여성으로 변경 시 +12 키 권장, 여성에서 남성으로 변경 시 -12 키 권장, 음역대 폭발로 음색이 왜곡되면 적절한 음역대로 조정 가능.",
+ "目标采样率": "목표 샘플링률",
+ "等待中": "대기 중",
+ "等待输入": "입력 대기 중",
+ "算法延迟(ms):": "알고리즘 지연(ms):",
+ "索引": "인덱스",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "잘못된 인덱스입니다. 다음을 사용하세요: added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "인덱스 검색 실패",
+ "索引检索失败或未启用": "인덱스 검색 실패 또는 비활성화됨",
+ "索引训练": "인덱스 학습",
+ "耗时": "소요 시간",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "소요 시간: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "융합",
+ "要改的模型信息": "변경할 모델 정보",
+ "要置入的模型信息": "삽입할 모델 정보",
+ "训练": "훈련",
+ "训练已完成,正在保存最终模型": "학습 완료, 최종 모델을 저장합니다",
+ "训练文件列表写入完成": "학습 파일 목록 저장 완료",
+ "训练模型": "모델 훈련",
+ "训练特征索引": "특징 인덱스 훈련",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "훈련 완료, 콘솔 훈련 로그 또는 실험 폴더 내의 train.log 확인 가능",
+ "训练设备规则选择的精度:%s": "장치 규칙에 따라 선택한 학습 정밀도: %s",
+ "训练轮次:{} [{:.0f}%]": "학습 에포크: {} [{:.0f}%]",
+ "设备类型": "장치 유형",
+ "请上传音频文件": "오디오 파일을 업로드하세요",
+ "请填写输出文件夹路径": "출력 폴더 경로를 입력하세요",
+ "请指定说话人id": "화자 ID 지정 필요",
+ "请选择index文件": "index 파일 선택",
+ "请选择pth文件": "pth 파일 선택",
+ "请选择说话人id": "화자 ID 선택",
+ "转换": "변환",
+ "输入实验名": "실험명 입력",
+ "输入待处理音频文件夹路径": "처리할 오디오 파일 폴더 경로 입력",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "처리할 오디오 파일 폴더 경로 입력(파일 탐색기 주소 표시줄에서 복사)",
+ "输入待处理音频文件路径(默认是正确格式示例)": "처리할 오디오 파일 경로 입력(기본적으로 올바른 형식 예시)",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "입력 소스 볼륨 엔벨로프와 출력 볼륨 엔벨로프의 결합 비율 입력, 1에 가까울수록 출력 엔벨로프 사용",
+ "输入监听": "입력 모니터링",
+ "输入训练文件夹路径": "훈련 파일 폴더 경로 입력",
+ "输入设备": "입력 장치",
+ "输入设备:%s:%s": "입력 장치: %s:%s",
+ "输入降噪": "입력 노이즈 감소",
+ "输出信息": "출력 정보",
+ "输出变声": "출력 음성 변환",
+ "输出设备": "출력 장치",
+ "输出设备:%s:%s": "출력 장치: %s:%s",
+ "输出降噪": "출력 노이즈 감소",
+ "输出音频(右下角三个点,点了可以下载)": "출력 오디오(오른쪽 하단 세 개의 점, 클릭하면 다운로드 가능)",
+ "运行中": "실행 중",
+ "进度": "진행률",
+ "选择.index文件": ".index 파일 선택",
+ "选择.pth文件": ".pth 파일 선택",
+ "选择模型": "모델 선택",
+ "选择索引": "인덱스 선택",
+ "选择音高提取算法": "피치 추출 알고리즘 선택",
+ "采样率:": "샘플링 레이트:",
+ "采样长度": "샘플링 길이",
+ "重载设备列表": "장치 목록 재로드",
+ "错误信息:%s": "오류 정보: %s",
+ "错误:未知模型:%s": "오류: 알 수 없는 모델: %s",
+ "音调设置": "음조 설정",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "오디오가 다채널이지만 모델은 모노이므로 모든 채널의 평균을 사용합니다",
+ "音频设备": "오디오 장치",
+ "音高全部为0,该音频无意义,跳过:%s": "모든 피치 값이 0이므로 이 오디오는 무의미하여 건너뜁니다: %s",
+ "音高算法": "피치 알고리즘",
+ "额外推理时长": "추가 추론 시간",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "데이터 전처리에서 유효한 학습 오디오가 생성되지 않았습니다. 데이터셋과 전처리 로그를 확인하세요.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "데이터 전처리에서 16 kHz 오디오가 생성되지 않아 특징 추출과 학습을 중지했습니다.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "데이터 전처리 출력 파일이 일치하지 않아 특징 추출과 학습을 중지했습니다.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT 특징 추출에서 유효한 결과가 생성되지 않아 학습을 중지했습니다.",
+ "F0提取没有生成有效结果,已停止训练": "F0 추출에서 유효한 결과가 생성되지 않아 학습을 중지했습니다.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "학습에 사용할 유효한 오디오가 없습니다. 먼저 데이터 전처리와 특징 추출을 완료하세요.",
+ "已完成阶段": "완료된 단계",
+ "已成功": "성공",
+ "刷新音色列表": "음색 목록 새로고침",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "특징 인덱스 경로(모델 선택 후 자동 매칭, 수동 수정 가능)",
+ "[索引训练] 外部索引链接已存在:%s": "[인덱스 학습] 외부 인덱스 링크가 이미 있습니다: %s",
+ "[索引训练][跳过] added索引已存在:%s": "[인덱스 학습][건너뜀] added 인덱스가 이미 있습니다: %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[인덱스 학습][건너뜀] trained 인덱스가 이미 있습니다: %s",
+ "当前设备:%s | 推理精度:%s": "현재 장치: %s | 추론 정밀도: %s"
+}
diff --git a/i18n/locale/pt_BR.json b/i18n/locale/pt_BR.json
new file mode 100644
index 0000000..c4885a9
--- /dev/null
+++ b/i18n/locale/pt_BR.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "Intensidade da extração vocal",
+ "%s → 成功": "%s → Sucesso",
+ "%s运行中,请先停止该任务": "%s está em execução; interrompa antes de iniciar outra tarefa",
+ "%s进程已终止": "O processo %s foi encerrado",
+ "====> 轮次:{} {}": "====> Época: {} {}",
+ "A模型权重": "Peso (w) para o modelo A:",
+ "A模型路径": "Caminho para o Modelo A:",
+ "B模型路径": "Caminho para o Modelo B:",
+ "CUDA可用:%s": "CUDA disponível: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "Extração de F0 e recursos HuBERT",
+ "F0提取": "Extração F0",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "Arquivo de curva F0 (opcional). Um arremesso por linha. Substitui a modulação padrão F0 e tom:",
+ "HuBERT特征": "Recursos HuBERT",
+ "Index Rate": "Taxa do Index",
+ "RVC模型路径": "Caminho do Modelo RVC:",
+ "SOLA偏移:%d": "Deslocamento SOLA: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Extração F0] Concluído | Sucesso: %s | Ignorados: %s | Falha: %s",
+ "[F0提取] 待处理:%s": "[Extração F0] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[Extração F0] Nenhum áudio pendente; todos os arquivos foram ignorados",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[Extração F0] Progresso: %s/%s | Sucesso: %s | Ignorados: %s | %s",
+ "[F0提取][失败] %s": "[Extração F0][Falha] %s",
+ "[F0提取][失败] %s\n%s": "[Extração F0][Falha] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Recursos HuBERT] Concluído | Sucesso: %s | Ignorados: %s | Falha: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[Recursos HuBERT] Nenhum áudio pendente; ignorados: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[Recursos HuBERT] Carregando o modelo: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[Recursos HuBERT] Dispositivo: %s | Pending: %s | Ignorados: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[Recursos HuBERT] Progresso: %s/%s | Sucesso: %s | Falha: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[Recursos HuBERT][Falha] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[Recursos HuBERT][Falha] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[Recursos HuBERT][Falha] Modelo not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[Divisão de dados] Subtarefa concluída | Sucesso: %s | Falha: %s",
+ "[数据切分] 完成": "[Divisão de dados] Concluído",
+ "[数据切分] 开始": "[Divisão de dados] Iniciado",
+ "[数据切分] 待处理:%s | 进程数:%s": "[Divisão de dados] Pending: %s | Processos: %s",
+ "[数据切分] 进度:%s/%s | %s": "[Divisão de dados] Progresso: %s/%s | %s",
+ "[数据切分][失败] %s": "[Divisão de dados][Falha] %s",
+ "[数据切分][失败] %s\n%s": "[Divisão de dados][Falha] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[Divisão de dados][Ignorados] Segmento de áudio inválido ou anormal: %s_%s | Pico: %s",
+ "[索引训练] 写入进度:%s/%s": "[Treinamento do índice] Progressoo da gravação: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[Treinamento do índice] Índice vinculado à pasta externa: %s",
+ "[索引训练] 成功构建索引:%s": "[Treinamento do índice] Índice criado com sucesso: %s",
+ "[索引训练] 正在写入特征向量": "[Treinamento do índice] Adicionando vetores de recursos",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[Treinamento do índice] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[Treinamento do índice] Treinando o índice",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[Treinamento do índice] Forma dos recursos: %s | Quantidade de IVF: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[Treinamento do índice][Falha] Não foi possível vincular o índice à pasta externa: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[Treinamento do índice][Falha] O agrupamento falhou; continuando com os recursos originais\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[Treinamento do índice][Falha] Extraia os recursos primeiro",
+ "ckpt处理": "processamento ckpt",
+ "index文件路径不可包含中文": "O caminho do arquivo de Index não pode conter caracteres chineses",
+ "pth文件路径不可包含中文": "o caminho do arquivo pth não pode conter caracteres chineses",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "Configuração do número do cartão rmvpe: Use - para separar os números dos cartões de entrada de diferentes processos. Por exemplo, 0-0-1 é usado para executar 2 processos no cartão 0 e 1 processo no cartão 1.",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "Etapa 1: Preencha a configuração experimental. Os dados experimentais são armazenados na pasta 'logs', com cada experimento tendo uma pasta separada. Digite manualmente o caminho do nome do experimento, que contém a configuração experimental, os logs e os arquivos de modelo treinados.",
+ "step1:正在处理数据": "Etapa 1: Processamento de dados",
+ "step2:正在提取音高&正在提取特征": "step2:Extração de tom e características",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "Etapa 2a: Percorra automaticamente todos os arquivos na pasta de treinamento que podem ser decodificados em áudio e execute a normalização da fatia. Gera 2 pastas wav no diretório do experimento. Atualmente, apenas o treinamento de um único cantor/palestrante é suportado.",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "Etapa 2b: Use a CPU para extrair o tom (se o modelo tiver tom), use a GPU para extrair recursos (selecione o índice da GPU):",
+ "step3: 填写训练设置, 开始训练模型和索引": "Etapa 3: Preencha as configurações de treinamento e comece a treinar o modelo e o Index",
+ "step3a:正在训练模型": "Etapa 3a: Treinamento do modelo iniciado",
+ "step3b:正在训练索引": "etapa 3b: treinando o índice",
+ "……仅显示最近10条失败记录": "…Mostrando apenas as 10 falhas mais recentes",
+ "……已省略前%s行,仅显示最新状态": "…As primeiras %s linhas foram omitidas; mostrando apenas o estado mais recente",
+ "一键训练": "Treinamento com um clique",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "Você também pode inserir arquivos de áudio em lotes. Escolha uma das duas opções. É dada prioridade à leitura da pasta.",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "Processamento em lote para separar vocais e acompanhamento usando modelos UVR5.
Você pode escolher um modelo que preserve os vocais ou usar modelos DeEcho/DeReverb para remover eco e reverberação.",
+ "仅支持pm和rmvpe音高提取算法": "Somente os métodos de extração de tom pm e rmvpe são compatíveis",
+ "从训练检查点提取的模型": "Modeloo extraído de um checkpoint de treinamento",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "Digite o (s) índice(s) da GPU separados por '-', por exemplo, 0-1-2 para usar a GPU 0, 1 e 2:",
+ "任务": "Tarefa",
+ "伴奏人声分离&去混响&去回声": "UVR5",
+ "使用显卡:%s": "GPUs em uso: %s",
+ "使用模型采样率": "Usar taxa do modelo",
+ "使用设备采样率": "Usar taxa do dispositivo",
+ "保存名": "Salvar nome",
+ "保存的文件名, 默认空为和源文件同名": "Salvar nome do arquivo (padrão: igual ao arquivo de origem):",
+ "保存的模型名不带后缀": "Nome do modelo salvo (sem extensão):",
+ "保存频率save_every_epoch": "Faça backup a cada # de Epoch:",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "Proteja consoantes sem voz e sons respiratórios, evite artefatos como quebra de som eletrônico e desligue-o quando estiver cheio de 0,5. Diminua-o para aumentar a proteção, mas pode reduzir o efeito de indexação:",
+ "修改": "Editar",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "Modificar informações do modelo (suportado apenas para arquivos de modelo pequenos extraídos da pasta 'weights')",
+ "停止一键训练": "Parar treinamento com um clique",
+ "停止处理数据": "Parar pré-processamento de dados",
+ "停止特征提取": "Parar extração de características",
+ "停止训练模型": "Parar treinamento do modelo",
+ "停止训练索引": "Parar treinamento do índice",
+ "停止音频转换": "Conversão de áudio",
+ "全流程结束!": "Todos os processos foram concluídos!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "Modelo",
+ "加载预训练底模D路径": "Carregue o caminho D do modelo base pré-treinado:",
+ "加载预训练底模G路径": "Carregue o caminho G do modelo base pré-treinado:",
+ "单次推理": "Inferência única",
+ "卸载音色省显存": "Descarregue a voz para liberar a memória da GPU:",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "Mude o tom aqui. Se a voz for do mesmo sexo, não é necessario alterar (12 caso seja Masculino para feminino, -12 caso seja ao contrário).",
+ "合成": "Síntese",
+ "后处理重采样至最终采样率,0为不进行重采样": "Reamostragem pós-processamento para a taxa de amostragem final, 0 significa sem reamostragem:",
+ "否": "Não",
+ "响应阈值": "Limiar de resposta",
+ "响度因子": "Fator de volume",
+ "处理中": "Processando",
+ "处理数据": "Processar o Conjunto de Dados",
+ "失败": "Falha",
+ "失败记录": "Registro de falhas",
+ "子进程执行失败,返回码:%s": "O subprocesso falhou com o código de saída: %s",
+ "导出文件格式": "Qual formato de arquivo você prefere?",
+ "已停止": "Interrompido",
+ "已加载判别器预训练模型:%s": "Modeloo pré-treinado do discriminador carregado: %s",
+ "已加载生成器预训练模型:%s": "Modeloo pré-treinado do gerador carregado: %s",
+ "已启用索引检索": "Pesquisa por índice ativada",
+ "已完成": "Concluído",
+ "已恢复判别器检查点": "Checkpoint do discriminador restaurado",
+ "常见问题解答": "FAQ (Perguntas frequentes)",
+ "常规设置": "Configurações gerais",
+ "开始音频转换": "Iniciar conversão de áudio",
+ "当前": "Atual",
+ "当前阶段": "Etapa atual",
+ "很遗憾您这没有能用的显卡来支持您训练": "Infelizmente, não há GPU compatível disponível para apoiar o seu treinamento.",
+ "性别因子/声线粗细": "Fator de gênero / espessura da voz",
+ "性能设置": "Configurações de desempenho.",
+ "总训练轮数total_epoch": "Número total de ciclos(epoch) de treino (se escolher um valor alto demais, o seu modelo parecerá terrivelmente sobretreinado):",
+ "成功": "Sucesso",
+ "执行命令": "Comando",
+ "批量推理": "Inferência em lote",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "Conversão em Massa.",
+ "拖拽或点击上传待处理音频": "Arraste e solte ou clique para enviar o áudio a ser processado",
+ "指定输出主人声文件夹": "Especifique a pasta de saída para vocais:",
+ "指定输出文件夹": "Especifique a pasta de saída:",
+ "指定输出非主人声文件夹": "Informar a pasta de saída para acompanhamento:",
+ "推理时间(ms):": "Tempo de inferência (ms):",
+ "推理耗时:%.2f秒": "Tempo de inferência: %.2f seconds",
+ "推理音色": "Escolha o seu Modelo:",
+ "提取": "Extrato",
+ "提取音高和处理数据使用的CPU进程数": "Número de processos de CPU usados para extração de tom e processamento de dados:",
+ "数据切分": "Divisão de dados",
+ "无法停止": "Não é possível interromper",
+ "无法读取音频:%s": "Não foi possível ler o áudio: %s",
+ "是": "Sim",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "Só deve salvar apenas o arquivo ckpt mais recente para economizar espaço em disco:",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "Salve um pequeno modelo final na pasta 'weights' em cada ponto de salvamento:",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "Se deve armazenar em cache todos os conjuntos de treinamento na memória de vídeo. Pequenos dados com menos de 10 minutos podem ser armazenados em cache para acelerar o treinamento, e um cache de dados grande irá explodir a memória de vídeo e não aumentar muito a velocidade:",
+ "显卡信息": "Informações da GPU",
+ "未使用": "Não usado",
+ "未使用判别器预训练模型": "Modelo pré-treinado do discriminador não usado",
+ "未使用生成器预训练模型": "Modelo pré-treinado do gerador não usado",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Configuração do modelo Roformer não encontrada; usando os padrões internos",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "Nenhuma GPU compatível foi detectada; treinar na CPU pode demorar muito mais",
+ "未运行": "Não iniciado",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "The Mangio-RVC 💻 | Tradução por Krisp e Rafael Godoy Ebert | AI HUB BRASIL
Este software é de código aberto sob a licença MIT. O autor não tem qualquer controle sobre o software. Aqueles que usam o software e divulgam os sons exportados pelo software são totalmente responsáveis.
Se você não concorda com este termo, você não pode usar ou citar nenhum código e arquivo no pacote de software. Para obter detalhes, consulte o diretório raiz O acordo a ser seguido para uso LICENSE",
+ "查看": "Visualizar",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "Exibir informações do modelo (suportado apenas para arquivos de modelo pequenos extraídos da pasta 'weights')",
+ "检测到模型类型:%s": "Tipo de modelo detectado: %s",
+ "检索特征占比": "Taxa de recurso de recuperação:",
+ "模型": "Modelo",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "Informações do modelo: %s\nTaxa de amostragem: %s\nOrientação de tom: %s\nVersão: %s",
+ "模型推理": "Inference",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "Extração do modelo (insira o caminho do modelo de arquivo grande na pasta 'logs'). Isso é útil se você quiser interromper o treinamento no meio do caminho e extrair e salvar manualmente um arquivo de modelo pequeno, ou se quiser testar um modelo intermediário:",
+ "模型是否带音高指导": "Se o modelo tem orientação de tom:",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "Se o modelo tem orientação de tom (necessário para cantar, opcional para fala):",
+ "模型是否带音高指导,1是0否": "Se o modelo tem orientação de passo (1: sim, 0: não):",
+ "模型版本型号": "Versão:",
+ "模型融合, 可用于测试音色融合": "A fusão modelo, pode ser usada para testar a fusão do timbre",
+ "模型融合失败:两个模型的结构不一致": "Falha ao mesclar modelos: as arquiteturas não correspondem",
+ "模型训练": "Treinamento do modelo",
+ "模型路径": "Caminho para o Modelo:",
+ "正在保存最终检查点:%s": "Salvando o checkpoint final: %s",
+ "正在保存检查点 %s_e%s:%s": "Salvando o checkpoint %s_e%s: %s",
+ "正在加载RMVPE模型": "Carregando o modelo RMVPE",
+ "正在加载模型": "Carregando o modelo",
+ "正在启动": "Iniciando",
+ "正在收尾": "Finalizando",
+ "每张显卡的batch_size": "Batch Size (DEIXE COMO ESTÁ a menos que saiba o que está fazendo, no Colab pode deixar até 20!):",
+ "淡入淡出长度": "Comprimento de desvanecimento",
+ "清理模型缓存": "Limpando o cache do modelo",
+ "版本": "Versão",
+ "特征": "Recursos",
+ "特征提取": "Extrair Tom",
+ "状态": "Estado",
+ "独占 WASAPI 设备": "Dispositivo WASAPI exclusivo",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "Recomendado +12 chave para conversão de homem para mulher e -12 chave para conversão de mulher para homem. Se a faixa de som for muito longe e a voz estiver distorcida, você também pode ajustá-la à faixa apropriada por conta própria.",
+ "目标采样率": "Taxa de amostragem:",
+ "等待中": "Aguardando",
+ "等待输入": "Aguardando entrada",
+ "算法延迟(ms):": "Latência do algoritmo (ms):",
+ "索引": "Índice",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "Índice inválido: use added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "Falha na pesquisa por índice",
+ "索引检索失败或未启用": "A pesquisa por índice falhou ou está desativada",
+ "索引训练": "Treinamento do índice",
+ "耗时": "Tempo decorrido",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "Tempo decorrido: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "Fusão",
+ "要改的模型信息": "Informações do modelo a ser modificado:",
+ "要置入的模型信息": "Informações do modelo a ser colocado:",
+ "训练": "Treinar",
+ "训练已完成,正在保存最终模型": "Treinamento concluído; salvando o modelo final",
+ "训练文件列表写入完成": "Lista de arquivos de treinamento salva",
+ "训练模型": "Treinar Modelo",
+ "训练特征索引": "Treinar Index",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "Após o término do treinamento, você pode verificar o log de treinamento do console ou train.log na pasta de experimentos",
+ "训练设备规则选择的精度:%s": "Precisão de treinamento escolhida pelas regras do dispositivo: %s",
+ "训练轮次:{} [{:.0f}%]": "Época de treinamento: {} [{:.0f}%]",
+ "设备类型": "Tipo de dispositivo",
+ "请上传音频文件": "Envie um arquivo de áudio",
+ "请填写输出文件夹路径": "Informe o caminho da pasta de saída",
+ "请指定说话人id": "Especifique o ID do locutor/cantor:",
+ "请选择index文件": "Selecione o arquivo de Index",
+ "请选择pth文件": "Selecione o arquivo pth",
+ "请选择说话人id": "Selecione Palestrantes/Cantores ID:",
+ "转换": "Converter",
+ "输入实验名": "Nome da voz:",
+ "输入待处理音频文件夹路径": "Caminho da pasta de áudio a ser processada:",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "Caminho da pasta de áudio a ser processada (copie-o da barra de endereços do gerenciador de arquivos):",
+ "输入待处理音频文件路径(默认是正确格式示例)": "Caminho para o seu conjunto de dados (áudios, não zipado):",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "O envelope de volume da fonte de entrada substitui a taxa de fusão do envelope de volume de saída, quanto mais próximo de 1, mais o envelope de saída é usado:",
+ "输入监听": "Monitorar entrada",
+ "输入训练文件夹路径": "Caminho da pasta de treinamento:",
+ "输入设备": "Dispositivo de entrada",
+ "输入设备:%s:%s": "Dispositivo de entrada: %s:%s",
+ "输入降噪": "Redução de ruído de entrada",
+ "输出信息": "Informação de saída",
+ "输出变声": "Conversão de saída",
+ "输出设备": "Dispositivo de saída",
+ "输出设备:%s:%s": "Dispositivo de saída: %s:%s",
+ "输出降噪": "Redução de ruído de saída",
+ "输出音频(右下角三个点,点了可以下载)": "Exportar áudio (clique nos três pontos no canto inferior direito para baixar)",
+ "运行中": "Em execução",
+ "进度": "Progresso",
+ "选择.index文件": "Selecione o Index",
+ "选择.pth文件": "Selecione o Arquivo",
+ "选择模型": "Selecionar modelo",
+ "选择索引": "Selecionar índice",
+ "选择音高提取算法": "Selecione o algoritmo de extração de tom",
+ "采样率:": "Taxa de amostragem:",
+ "采样长度": "Comprimento da Amostra",
+ "重载设备列表": "Recarregar lista de dispositivos",
+ "错误信息:%s": "Detalhes do erro: %s",
+ "错误:未知模型:%s": "Erro: modelo desconhecido: %s",
+ "音调设置": "Configurações de tom",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "O áudio tem vários canais, mas o modelo é mono; todos os canais serão combinados pela média",
+ "音频设备": "Dispositivo de áudio",
+ "音高全部为0,该音频无意义,跳过:%s": "Todos os valores de tom são zero; este áudio não tem utilidade e será ignorado: %s",
+ "音高算法": "Algoritmo de tom",
+ "额外推理时长": "Tempo extra de inferência",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "O pré-processamento não gerou áudio de treinamento válido. Verifique o conjunto de dados e o log.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "O pré-processamento não gerou áudio de 16 kHz. A extração e o treinamento foram interrompidos.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "Os arquivos de saída do pré-processamento não correspondem. A extração e o treinamento foram interrompidos.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "A extração de recursos HuBERT não gerou resultados válidos. O treinamento foi interrompido.",
+ "F0提取没有生成有效结果,已停止训练": "A extração de F0 não gerou resultados válidos. O treinamento foi interrompido.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "Não há áudio válido para treinamento. Conclua primeiro o pré-processamento e a extração de recursos.",
+ "已完成阶段": "Etapas concluídas",
+ "已成功": "Concluído",
+ "刷新音色列表": "Atualizar lista de vozes",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "Caminho do índice de características (detectado após selecionar o modelo; editável)",
+ "[索引训练] 外部索引链接已存在:%s": "[Treinamento de índice] O link externo já existe: %s",
+ "[索引训练][跳过] added索引已存在:%s": "[Treinamento de índice][Ignorado] O índice added já existe: %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[Treinamento de índice][Ignorado] O índice trained já existe: %s",
+ "当前设备:%s | 推理精度:%s": "Dispositivo atual: %s | Precisão de inferência: %s"
+}
diff --git a/i18n/locale/ru_RU.json b/i18n/locale/ru_RU.json
new file mode 100644
index 0000000..c110a32
--- /dev/null
+++ b/i18n/locale/ru_RU.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "Интенсивность выделения вокала",
+ "%s → 成功": "%s → Успешно",
+ "%s运行中,请先停止该任务": "%s уже выполняется; сначала остановите эту задачу",
+ "%s进程已终止": "Процесс %s завершён",
+ "====> 轮次:{} {}": "====> Эпоха: {} {}",
+ "A模型权重": "Весы (w) модели А:",
+ "A模型路径": "Путь к модели А:",
+ "B模型路径": "Путь к модели Б:",
+ "CUDA可用:%s": "CUDA доступна: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "Извлечение F0 и признаков HuBERT",
+ "F0提取": "Извлечение F0",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "Файл дуги F0 (не обязательно). Одна тональность на каждую строчку. Заменяет обычный F0 и модуляцию тональности:",
+ "HuBERT特征": "Признаки HuBERT",
+ "Index Rate": "Темп индекса",
+ "RVC模型路径": "Путь к модели RVC:",
+ "SOLA偏移:%d": "Смещение SOLA: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Извлечение F0] Завершено | Успешно: %s | Пропущено: %s | Ошибка: %s",
+ "[F0提取] 待处理:%s": "[Извлечение F0] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[Извлечение F0] Нет ожидающих аудиофайлов; все файлы пропущены",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[Извлечение F0] Прогресс: %s/%s | Успешно: %s | Пропущено: %s | %s",
+ "[F0提取][失败] %s": "[Извлечение F0][Ошибка] %s",
+ "[F0提取][失败] %s\n%s": "[Извлечение F0][Ошибка] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[Признаки HuBERT] Завершено | Успешно: %s | Пропущено: %s | Ошибка: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[Признаки HuBERT] Нет ожидающих аудиофайлов; пропущено: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[Признаки HuBERT] Загрузка модели: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[Признаки HuBERT] Устройство: %s | Pending: %s | Пропущено: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[Признаки HuBERT] Прогресс: %s/%s | Успешно: %s | Ошибка: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[Признаки HuBERT][Ошибка] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[Признаки HuBERT][Ошибка] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[Признаки HuBERT][Ошибка] Модель not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[Нарезка данных] Подзадача завершена | Успешно: %s | Ошибка: %s",
+ "[数据切分] 完成": "[Нарезка данных] Завершено",
+ "[数据切分] 开始": "[Нарезка данных] Запущено",
+ "[数据切分] 待处理:%s | 进程数:%s": "[Нарезка данных] Pending: %s | Процессы: %s",
+ "[数据切分] 进度:%s/%s | %s": "[Нарезка данных] Прогресс: %s/%s | %s",
+ "[数据切分][失败] %s": "[Нарезка данных][Ошибка] %s",
+ "[数据切分][失败] %s\n%s": "[Нарезка данных][Ошибка] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[Нарезка данных][Пропущено] Недопустимый или аномальный аудиофрагмент: %s_%s | Пик: %s",
+ "[索引训练] 写入进度:%s/%s": "[Обучение индекса] Ход записи: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[Обучение индекса] Индекс связан с внешней папкой: %s",
+ "[索引训练] 成功构建索引:%s": "[Обучение индекса] Индекс успешно создан: %s",
+ "[索引训练] 正在写入特征向量": "[Обучение индекса] Добавление векторов признаков",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[Обучение индекса] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[Обучение индекса] Обучение индекса",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[Обучение индекса] Форма признаков: %s | Количество IVF: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[Обучение индекса][Ошибка] Не удалось связать индекс с внешней папкой: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[Обучение индекса][Ошибка] Сбой кластеризации; продолжение с исходными признаками\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[Обучение индекса][Ошибка] Сначала извлеките признаки",
+ "ckpt处理": "Обработка ckpt",
+ "index文件路径不可包含中文": "Путь к файлу индекса",
+ "pth文件路径不可包含中文": "Путь к файлу pth",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "Введите номера графических процессоров, разделенные символом «-», например, 0-0-1, чтобы запустить два процесса на GPU 0 и один процесс на GPU 1:",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "Шаг 1. Конфигурирование модели. Данные обучения модели сохраняются в папку 'logs', и для каждой модели создаётся отдельная папка. Введите вручную путь к настройкам для модели, в которой находятся логи и тренировочные файлы.",
+ "step1:正在处理数据": "Шаг 1. Переработка данных",
+ "step2:正在提取音高&正在提取特征": "step2:Извлечение высоты тона и признаков",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "Шаг 2А. Автоматическая обработка исходных аудиозаписей для обучения и выполнение нормализации среза. Создаст 2 папки wav в папке модели. В данный момент поддерживается обучение только на одноголосных записях.",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "Шаг 2Б. Оценка и извлечение тональности в аудиофайлах с помощью процессора (если включена поддержка изменения высоты звука), извлечение черт с помощью GPU (выберите номер GPU):",
+ "step3: 填写训练设置, 开始训练模型和索引": "Шаг 3. Заполнение дополнительных настроек обучения и запуск обучения модели и индекса",
+ "step3a:正在训练模型": "Шаг 3. Запуск обучения модели",
+ "step3b:正在训练索引": "шаг 3b: обучение индекса",
+ "……仅显示最近10条失败记录": "…Показаны только 10 последних ошибок",
+ "……已省略前%s行,仅显示最新状态": "…Первые %s строк пропущены; показано только последнее состояние",
+ "一键训练": "Обучение в одно нажатие",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "Можно также импортировать несколько аудиофайлов. Если путь к папке существует, то этот ввод игнорируется.",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "Пакетное разделение вокала и аккомпанемента с помощью моделей UVR5.
Можно выбрать модель с сохранением вокала или использовать модели DeEcho/DeReverb для удаления эха и реверберации.",
+ "仅支持pm和rmvpe音高提取算法": "Поддерживаются только методы извлечения высоты тона pm и rmvpe",
+ "从训练检查点提取的模型": "Модель извлечена из контрольной точки обучения",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "Введите, какие(-ую) GPU(-у) хотите использовать через '-', например 0-1-2, чтобы использовать GPU с номерами 0, 1 и 2:",
+ "任务": "Задача",
+ "伴奏人声分离&去混响&去回声": "Разделение вокала/аккомпанемента и удаление эхо",
+ "使用显卡:%s": "Используемые GPU: %s",
+ "使用模型采样率": "Использовать частоту модели",
+ "使用设备采样率": "Использовать частоту устройства",
+ "保存名": "Имя файла для сохранения:",
+ "保存的文件名, 默认空为和源文件同名": "Название сохранённого файла (по умолчанию: такое же, как и у входного):",
+ "保存的模型名不带后缀": "Имя файла модели для сохранения (без расширения):",
+ "保存频率save_every_epoch": "Частота сохранения (save_every_epoch):",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "Защитить глухие согласные и звуки дыхания для предотвращения артефактов, например, разрывания в электронной музыке. Поставьте на 0.5, чтобы выключить. Уменьшите значение для повышения защиты, но учтите, что при этом может ухудшиться точность индексирования:",
+ "修改": "Изменить",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "Изменить информацию о модели (работает только с маленькими моделями, взятыми из папки 'weights')",
+ "停止一键训练": "Остановить обучение в один клик",
+ "停止处理数据": "Остановить предобработку данных",
+ "停止特征提取": "Остановить извлечение признаков",
+ "停止训练模型": "Остановить обучение модели",
+ "停止训练索引": "Остановить обучение индекса",
+ "停止音频转换": "Закончить конвертацию аудио",
+ "全流程结束!": "Все процессы завершены!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "Загрузить модель",
+ "加载预训练底模D路径": "Путь к предварительно обученной базовой модели D:",
+ "加载预训练底模G路径": "Путь к предварительно обученной базовой модели G:",
+ "单次推理": "Одиночный инференс",
+ "卸载音色省显存": "Выгрузить модель из памяти GPU для освобождения ресурсов",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "Изменить высоту голоса (укажите количество полутонов; чтобы поднять голос на октаву, выберите 12, понизить на октаву — -12):",
+ "合成": "Синтез",
+ "后处理重采样至最终采样率,0为不进行重采样": "Изменить частоту дискретизации в выходном файле на финальную. Поставьте 0, чтобы ничего не изменялось:",
+ "否": "Нет",
+ "响应阈值": "Порог ответа",
+ "响度因子": "коэффициент громкости",
+ "处理中": "Обработка",
+ "处理数据": "Обработать данные",
+ "失败": "Ошибка",
+ "失败记录": "Список ошибок",
+ "子进程执行失败,返回码:%s": "Дочерний процесс завершился с кодом: %s",
+ "导出文件格式": "Формат выходных файлов",
+ "已停止": "Остановлено",
+ "已加载判别器预训练模型:%s": "Предобученная модель дискриминатора загружена: %s",
+ "已加载生成器预训练模型:%s": "Предобученная модель генератора загружена: %s",
+ "已启用索引检索": "Поиск по индексу включён",
+ "已完成": "Завершено",
+ "已恢复判别器检查点": "Контрольная точка дискриминатора восстановлена",
+ "常见问题解答": "ЧаВо (часто задаваемые вопросы)",
+ "常规设置": "Основные настройки",
+ "开始音频转换": "Начать конвертацию аудио",
+ "当前": "Текущий",
+ "当前阶段": "Текущий этап",
+ "很遗憾您这没有能用的显卡来支持您训练": "К сожалению, у вас нету графического процессора, который поддерживает обучение моделей.",
+ "性别因子/声线粗细": "Гендерный коэффициент / толщина голоса",
+ "性能设置": "Настройки быстроты",
+ "总训练轮数total_epoch": "Полное количество эпох (total_epoch):",
+ "成功": "Успешно",
+ "执行命令": "Команда",
+ "批量推理": "Пакетный инференс",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "Массовое преобразование. Введите путь к папке, в которой находятся файлы для преобразования голоса или выгрузите несколько аудиофайлов. Сконвертированные файлы будут сохранены в указанной папке (по умолчанию: 'opt').",
+ "拖拽或点击上传待处理音频": "Перетащите или нажмите, чтобы загрузить аудио для обработки",
+ "指定输出主人声文件夹": "Путь к папке для сохранения вокала:",
+ "指定输出文件夹": "Папка для результатов:",
+ "指定输出非主人声文件夹": "Путь к папке для сохранения аккомпанемента:",
+ "推理时间(ms):": "Время переработки (мс):",
+ "推理耗时:%.2f秒": "Время инференса: %.2f seconds",
+ "推理音色": "Желаемый голос:",
+ "提取": "Создать модель",
+ "提取音高和处理数据使用的CPU进程数": "Число процессов ЦП, используемое для оценки высоты голоса и обработки данных:",
+ "数据切分": "Нарезка данных",
+ "无法停止": "Невозможно остановить",
+ "无法读取音频:%s": "Не удалось прочитать аудио: %s",
+ "是": "Да",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "Сохранять только последний файл '.ckpt', чтобы сохранить место на диске:",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "Сохранять маленькую финальную модель в папку 'weights' на каждой точке сохранения:",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "Кэшировать все тренировочные сеты в видеопамять. Кэширование маленький датасетов (меньше 10 минут) может ускорить тренировку, но кэширование больших, наоборот, займёт много видеопамяти и не сильно ускорит тренировку:",
+ "显卡信息": "Информация о графических процессорах (GPUs):",
+ "未使用": "Не используется",
+ "未使用判别器预训练模型": "Предобученная модель дискриминатора не используется",
+ "未使用生成器预训练模型": "Предобученная модель генератора не используется",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Конфигурация модели Roformer не найдена; используются встроенные значения",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "Поддерживаемый GPU не обнаружен; обучение на CPU может занять значительно больше времени",
+ "未运行": "Не запущено",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "Это программное обеспечение с открытым исходным кодом распространяется по лицензии MIT. Автор никак не контролирует это программное обеспечение. Пользователи, которые используют эту программу и распространяют аудиозаписи, полученные с помощью этой программы, несут полную ответственность за это. Если вы не согласны с этим, вы не можете использовать какие-либо коды и файлы в рамках этой программы или ссылаться на них. Подробнее в файле Agreement-LICENSE.txt в корневом каталоге программы.",
+ "查看": "Просмотреть информацию",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "Просмотреть информацию о модели (работает только с маленькими моделями, взятыми из папки 'weights')",
+ "检测到模型类型:%s": "Обнаружен тип модели: %s",
+ "检索特征占比": "Соотношение поиска черт:",
+ "模型": "Модели",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "Информация о модели: %s\nЧастота дискретизации: %s\nУправление высотой тона: %s\nВерсия: %s",
+ "模型推理": "Изменение голоса",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "Создание модели из данных, полученных в процессе обучения (введите путь к большому файлу модели в папке 'logs'). Может пригодиться, если вам нужно завершить обучение и получить маленький файл готовой модели, или если вам нужно проверить недообученную модель:",
+ "模型是否带音高指导": "Поддерживает ли модель изменение высоты голоса (1: да, 0: нет):",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "Поддержка изменения высоты звука (обязательно для пения, необязательно для речи):",
+ "模型是否带音高指导,1是0否": "Поддерживает ли модель изменение высоты голоса (1: да, 0: нет):",
+ "模型版本型号": "Версия архитектуры модели:",
+ "模型融合, 可用于测试音色融合": "Слияние моделей, может быть использовано для проверки слияния тембра",
+ "模型融合失败:两个模型的结构不一致": "Не удалось объединить модели: их архитектуры не совпадают",
+ "模型训练": "Обучение модели",
+ "模型路径": "Путь к папке:",
+ "正在保存最终检查点:%s": "Сохранение итоговой контрольной точки: %s",
+ "正在保存检查点 %s_e%s:%s": "Сохранение контрольной точки %s_e%s: %s",
+ "正在加载RMVPE模型": "Загрузка модели RMVPE",
+ "正在加载模型": "Загрузка модели",
+ "正在启动": "Запуск",
+ "正在收尾": "Завершение",
+ "每张显卡的batch_size": "Размер пачки для GPU:",
+ "淡入淡出长度": "Длина затухания",
+ "清理模型缓存": "Очистка кэша модели",
+ "版本": "Версия архитектуры модели:",
+ "特征": "Признаки",
+ "特征提取": "Извлечь черты",
+ "状态": "Состояние",
+ "独占 WASAPI 设备": "Эксклюзивное устройство WASAPI",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "Рекомендуется выбрать +12 для конвертирования мужского голоса в женский и -12 для конвертирования женского в мужской. Если диапазон голоса слишком велик, и голос искажается, можно выбрать значение на свой вкус.",
+ "目标采样率": "Частота дискретизации аудио:",
+ "等待中": "Ожидание",
+ "等待输入": "Ожидание ввода",
+ "算法延迟(ms):": "Задержка алгоритма (мс):",
+ "索引": "Индекс",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "Недопустимый индекс: используйте added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "Сбой поиска по индексу",
+ "索引检索失败或未启用": "Поиск по индексу не выполнен или отключён",
+ "索引训练": "Обучение индекса",
+ "耗时": "Затраченное время",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "Затраченное время: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "Запустить слияние",
+ "要改的模型信息": "Информация, которая будет изменена:",
+ "要置入的模型信息": "Информация о модели:",
+ "训练": "Обучение модели",
+ "训练已完成,正在保存最终模型": "Обучение завершено; сохраняется итоговая модель",
+ "训练文件列表写入完成": "Список файлов обучения записан",
+ "训练模型": "Обучить модель",
+ "训练特征索引": "Обучить индекс черт",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "Обучение модели завершено. Журнал обучения можно просмотреть в консоли или в файле 'train.log' в папке с моделью.",
+ "训练设备规则选择的精度:%s": "Точность обучения выбрана по правилам устройства: %s",
+ "训练轮次:{} [{:.0f}%]": "Эпоха обучения: {} [{:.0f}%]",
+ "设备类型": "Тип устройства",
+ "请上传音频文件": "Загрузите аудиофайл",
+ "请填写输出文件夹路径": "Укажите путь к выходной папке",
+ "请指定说话人id": "Номер говорящего/поющего:",
+ "请选择index文件": "Пожалуйста, выберите файл индекса",
+ "请选择pth文件": "Пожалуйста, выберите файл pth",
+ "请选择说话人id": "Номер говорящего:",
+ "转换": "Преобразовать",
+ "输入实验名": "Название модели:",
+ "输入待处理音频文件夹路径": "Путь к папке с аудиофайлами для обработки:",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "Путь к папке с аудиофайлами для переработки (можно скопировать путь из адресной строки файлового менеджера):",
+ "输入待处理音频文件路径(默认是正确格式示例)": "Путь к аудиофайлу, который хотите обработать (ниже указан пример пути к файлу):",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "Использовать громкость входного файла для замены или перемешивания с громкостью выходного файла. Чем ближе соотношение к 1, тем больше используется звука из выходного файла:",
+ "输入监听": "Мониторинг входа",
+ "输入训练文件夹路径": "Путь к папке с аудиозаписями, на которых будет обучаться модель:",
+ "输入设备": "Входное устройство",
+ "输入设备:%s:%s": "Устройство ввода: %s:%s",
+ "输入降噪": "Уменьшение входного шума",
+ "输出信息": "Статистика",
+ "输出变声": "Преобразование выхода",
+ "输出设备": "Выходное устройство",
+ "输出设备:%s:%s": "Устройство вывода: %s:%s",
+ "输出降噪": "Уменьшение выходного шума",
+ "输出音频(右下角三个点,点了可以下载)": "Аудиофайл (чтобы скачать, нажмите на три точки справа в плеере)",
+ "运行中": "Выполняется",
+ "进度": "Прогресс",
+ "选择.index文件": "Выбрать файл .index",
+ "选择.pth文件": "Выбрать файл .pth",
+ "选择模型": "Выберите модель",
+ "选择索引": "Выберите индекс",
+ "选择音高提取算法": "Выберите алгоритм извлечения высоты тона",
+ "采样率:": "Частота дискретизации:",
+ "采样长度": "Длина сэмпла",
+ "重载设备列表": "Обновить список устройств",
+ "错误信息:%s": "Сведения об ошибке: %s",
+ "错误:未知模型:%s": "Ошибка: неизвестная модель: %s",
+ "音调设置": "Настройка высоты звука",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "Аудио многоканальное, но модель монофоническая; все каналы будут усреднены",
+ "音频设备": "Аудиоустройство",
+ "音高全部为0,该音频无意义,跳过:%s": "Все значения высоты тона равны нулю; это аудио бесполезно и будет пропущено: %s",
+ "音高算法": "Алгоритм высоты тона",
+ "额外推理时长": "Доп. время переработки",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "Предобработка не создала допустимые аудиофайлы для обучения. Проверьте набор данных и журнал.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "Предобработка не создала аудио 16 кГц. Извлечение признаков и обучение остановлены.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "Выходные файлы предобработки не совпадают. Извлечение признаков и обучение остановлены.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "Извлечение признаков HuBERT не дало допустимых результатов. Обучение остановлено.",
+ "F0提取没有生成有效结果,已停止训练": "Извлечение F0 не дало допустимых результатов. Обучение остановлено.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "Нет допустимых аудиофайлов для обучения. Сначала выполните предобработку и извлечение признаков.",
+ "已完成阶段": "Завершённые этапы",
+ "已成功": "Успешно",
+ "刷新音色列表": "Обновить список голосов",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "Путь к индексу признаков (подбирается после выбора модели; можно изменить)",
+ "[索引训练] 外部索引链接已存在:%s": "[Обучение индекса] Внешняя ссылка уже существует: %s",
+ "[索引训练][跳过] added索引已存在:%s": "[Обучение индекса][Пропущено] Индекс added уже существует: %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[Обучение индекса][Пропущено] Индекс trained уже существует: %s",
+ "当前设备:%s | 推理精度:%s": "Текущее устройство: %s | Точность вывода: %s"
+}
diff --git a/i18n/locale/tr_TR.json b/i18n/locale/tr_TR.json
new file mode 100644
index 0000000..a7fb23c
--- /dev/null
+++ b/i18n/locale/tr_TR.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "Vokal ayırma yoğunluğu",
+ "%s → 成功": "%s → Başarılı",
+ "%s运行中,请先停止该任务": "%s çalışıyor; başka bir görev başlatmadan önce durdurun",
+ "%s进程已终止": "%s işlemi sonlandırıldı",
+ "====> 轮次:{} {}": "====> Dönem: {} {}",
+ "A模型权重": "A Modeli Ağırlığı:",
+ "A模型路径": "A Modeli Yolu:",
+ "B模型路径": "B Modeli Yolu:",
+ "CUDA可用:%s": "CUDA kullanılabilir: %s",
+ "E:\\语音音频+标注\\米津玄师\\src": "C:\\Users\\Desktop\\src",
+ "F0与HuBERT特征提取": "F0 ve HuBERT özellik çıkarımı",
+ "F0提取": "F0 çıkarma",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0 eğrisi dosyası (isteğe bağlı). Her satırda bir pitch değeri bulunur. Varsayılan F0 ve pitch modülasyonunu değiştirir:",
+ "HuBERT特征": "HuBERT özellikleri",
+ "Index Rate": "Index Oranı",
+ "RVC模型路径": "RVC Model Yolu:",
+ "SOLA偏移:%d": "SOLA ofseti: %d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0 çıkarma] Tamamlandı | Başarılı: %s | Atlandı: %s | Başarısız: %s",
+ "[F0提取] 待处理:%s": "[F0 çıkarma] Pending: %s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0 çıkarma] Bekleyen ses yok; tüm dosyalar atlandı",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0 çıkarma] İlerleme: %s/%s | Başarılı: %s | Atlandı: %s | %s",
+ "[F0提取][失败] %s": "[F0 çıkarma][Başarısız] %s",
+ "[F0提取][失败] %s\n%s": "[F0 çıkarma][Başarısız] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT özellikleri] Tamamlandı | Başarılı: %s | Atlandı: %s | Başarısız: %s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT özellikleri] Bekleyen ses yok; atlanan: %s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT özellikleri] Model yükleniyor: %s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT özellikleri] Cihaz: %s | Pending: %s | Atlandı: %s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT özellikleri] İlerleme: %s/%s | Başarılı: %s | Başarısız: %s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT özellikleri][Başarısız] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT özellikleri][Başarısız] %s contains NaN values",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT özellikleri][Başarısız] Model not found: %s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[Veri dilimleme] Alt görev tamamlandı | Başarılı: %s | Başarısız: %s",
+ "[数据切分] 完成": "[Veri dilimleme] Tamamlandı",
+ "[数据切分] 开始": "[Veri dilimleme] Başlatıldı",
+ "[数据切分] 待处理:%s | 进程数:%s": "[Veri dilimleme] Pending: %s | İşlemler: %s",
+ "[数据切分] 进度:%s/%s | %s": "[Veri dilimleme] İlerleme: %s/%s | %s",
+ "[数据切分][失败] %s": "[Veri dilimleme][Başarısız] %s",
+ "[数据切分][失败] %s\n%s": "[Veri dilimleme][Başarısız] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[Veri dilimleme][Atlandı] Geçersiz veya anormal ses bölümü: %s_%s | Tepe: %s",
+ "[索引训练] 写入进度:%s/%s": "[İndeks eğitimi] Yazma ilerlemesi: %s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[İndeks eğitimi] İndeks harici klasöre bağlandı: %s",
+ "[索引训练] 成功构建索引:%s": "[İndeks eğitimi] İndeks başarıyla oluşturuldu: %s",
+ "[索引训练] 正在写入特征向量": "[İndeks eğitimi] Özellik vektörleri ekleniyor",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[İndeks eğitimi] Clustering %s feature vectors into 10,000 centers",
+ "[索引训练] 正在训练索引": "[İndeks eğitimi] İndeks eğitiliyor",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[İndeks eğitimi] Özellik şekli: %s | IVF sayısı: %s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[İndeks eğitimi][Başarısız] İndeks harici klasöre bağlanamadı: %s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[İndeks eğitimi][Başarısız] Kümeleme başarısız; özgün özelliklerle devam ediliyor\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[İndeks eğitimi][Başarısız] Önce özellikleri çıkarın",
+ "ckpt处理": "ckpt İşleme",
+ "index文件路径不可包含中文": ".index dosya yolu Çince karakter içeremez",
+ "pth文件路径不可包含中文": ".pth dosya yolu Çince karakter içeremez",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "RMVPE GPU yapılandırmasını tirelerle ayırın; örneğin 0-0-1, GPU 0'da iki ve GPU 1'de bir işlem çalıştırır",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "Adım 1: Deneysel yapılandırmayı doldurun. Deneysel veriler 'logs' klasöründe saklanır ve her bir deney için ayrı bir klasör vardır. Deneysel adı yolu manuel olarak girin; bu yol, deneysel yapılandırmayı, günlükleri ve eğitilmiş model dosyalarını içerir.",
+ "step1:正在处理数据": "Adım 1: Veri işleme",
+ "step2:正在提取音高&正在提取特征": "step2:Perde ve özellik çıkarımı",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "Adım 2a: Eğitim klasöründe ses dosyalarını otomatik olarak gezinerek dilimleme normalizasyonu yapın. Deney dizini içinde 2 wav klasörü oluşturur. Şu anda sadece tek kişilik eğitim desteklenmektedir.",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "Adım 2b: Ses yüksekliği (Pitch) çıkartmak için CPU kullanın (eğer model ses yüksekliği içeriyorsa), özellikleri çıkartmak için GPU kullanın (GPU indeksini seçin):",
+ "step3: 填写训练设置, 开始训练模型和索引": "Adım 3: Eğitim ayarlarını doldurun ve modeli ve dizini eğitmeye başlayın",
+ "step3a:正在训练模型": "Adım 3a: Model eğitimi başladı",
+ "step3b:正在训练索引": "adım 3b: indeks eğitiliyor",
+ "……仅显示最近10条失败记录": "…Yalnızca son 10 hata gösteriliyor",
+ "……已省略前%s行,仅显示最新状态": "…İlk %s satır atlandı; yalnızca en son durum gösteriliyor",
+ "一键训练": "Tek Tuşla Eğit",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "Ses dosyaları ayrıca toplu olarak, iki seçimle, öncelikli okuma klasörüyle içe aktarılabilir",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "UVR5 modelleriyle vokal ve eşliği toplu olarak ayırır.
Vokali koruyan bir model seçebilir veya yankı ve reverbi kaldırmak için DeEcho/DeReverb modellerini kullanabilirsiniz.",
+ "仅支持pm和rmvpe音高提取算法": "Yalnızca pm ve rmvpe perde çıkarma yöntemleri desteklenir",
+ "从训练检查点提取的模型": "Eğitim kontrol noktasından çıkarılan model",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "GPU indekslerini '-' ile ayırarak girin, örneğin 0-1-2, GPU 0, 1 ve 2'yi kullanmak için:",
+ "任务": "Görev",
+ "伴奏人声分离&去混响&去回声": "Vokal/Müzik Ayrıştırma ve Yankı Giderme",
+ "使用显卡:%s": "Kullanılan GPU: %s",
+ "使用模型采样率": "Model örnekleme hızını kullan",
+ "使用设备采样率": "Cihaz örnekleme hızını kullan",
+ "保存名": "Kaydetme Adı:",
+ "保存的文件名, 默认空为和源文件同名": "Kaydedilecek dosya adı (varsayılan: kaynak dosya ile aynı):",
+ "保存的模型名不带后缀": "Kaydedilecek model adı (uzantı olmadan):",
+ "保存频率save_every_epoch": "Kaydetme sıklığı (save_every_epoch):",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "Sessiz ünsüzleri ve nefes seslerini koruyarak elektronik müzikte yırtılma gibi sanal hataların oluşmasını engeller. 0.5 olarak ayarlandığında devre dışı kalır. Değerin azaltılması korumayı artırabilir, ancak indeksleme doğruluğunu azaltabilir:",
+ "修改": "Düzenle",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "Model bilgilerini düzenle (sadece 'weights' klasöründen çıkarılan küçük model dosyaları desteklenir)",
+ "停止一键训练": "Tek tıkla eğitimi durdur",
+ "停止处理数据": "Veri ön işlemeyi durdur",
+ "停止特征提取": "Özellik çıkarmayı durdur",
+ "停止训练模型": "Model eğitimini durdur",
+ "停止训练索引": "İndeks eğitimini durdur",
+ "停止音频转换": "Ses dönüştürmeyi durdur",
+ "全流程结束!": "Tüm işlemler tamamlandı!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "Discriminator pretrained model not found; it will not be used: assets/pretrained%s/%sD%s.pth",
+ "加载模型": "Model yükle",
+ "加载预训练底模D路径": "Önceden eğitilmiş temel D modelini yükleme yolu:",
+ "加载预训练底模G路径": "Önceden eğitilmiş temel G modelini yükleme yolu:",
+ "单次推理": "Tekli çıkarım",
+ "卸载音色省显存": "GPU bellek kullanımını azaltmak için sesi kaldır",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "Transpoze et (tamsayı, yarıton sayısıyla; bir oktav yükseltmek için: 12, bir oktav düşürmek için: -12):",
+ "合成": "Sentez",
+ "后处理重采样至最终采样率,0为不进行重采样": "Son işleme aşamasında çıktı sesini son örnekleme hızına yeniden örnekle. 0 değeri için yeniden örnekleme yapılmaz:",
+ "否": "Hayır",
+ "响应阈值": "Tepki eşiği",
+ "响度因子": "ses yüksekliği faktörü",
+ "处理中": "İşleniyor",
+ "处理数据": "Verileri işle",
+ "失败": "Başarısız",
+ "失败记录": "Hata kayıtları",
+ "子进程执行失败,返回码:%s": "Alt işlem şu çıkış koduyla başarısız oldu: %s",
+ "导出文件格式": "Dışa aktarma dosya formatı",
+ "已停止": "Durduruldu",
+ "已加载判别器预训练模型:%s": "Önceden eğitilmiş ayırıcı modeli yüklendi: %s",
+ "已加载生成器预训练模型:%s": "Önceden eğitilmiş üretici modeli yüklendi: %s",
+ "已启用索引检索": "İndeks araması etkin",
+ "已完成": "Tamamlandı",
+ "已恢复判别器检查点": "Ayırıcı kontrol noktası geri yüklendi",
+ "常见问题解答": "Sıkça Sorulan Sorular (SSS)",
+ "常规设置": "Genel ayarlar",
+ "开始音频转换": "Ses dönüştürmeyi başlat",
+ "当前": "Geçerli",
+ "当前阶段": "Geçerli aşama",
+ "很遗憾您这没有能用的显卡来支持您训练": "Maalesef, eğitiminizi desteklemek için uyumlu bir GPU bulunmamaktadır.",
+ "性别因子/声线粗细": "Cinsiyet faktörü / ses kalınlığı",
+ "性能设置": "Performans ayarları",
+ "总训练轮数total_epoch": "Toplam eğitim turu (total_epoch):",
+ "成功": "Başarılı",
+ "执行命令": "Komut",
+ "批量推理": "Toplu çıkarım",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "Toplu dönüştür. Dönüştürülecek ses dosyalarının bulunduğu klasörü girin veya birden çok ses dosyasını yükleyin. Dönüştürülen ses dosyaları belirtilen klasöre ('opt' varsayılan olarak) dönüştürülecektir",
+ "拖拽或点击上传待处理音频": "İşlenecek sesi sürükleyip bırakın veya yüklemek için tıklayın",
+ "指定输出主人声文件夹": "Vokal için çıkış klasörünü belirtin:",
+ "指定输出文件夹": "Çıkış klasörünü belirt:",
+ "指定输出非主人声文件夹": "Müzik ve diğer sesler için çıkış klasörünü belirtin:",
+ "推理时间(ms):": "Çıkarsama süresi (ms):",
+ "推理耗时:%.2f秒": "Çıkarım süresi: %.2f seconds",
+ "推理音色": "Ses çıkartma (Inference):",
+ "提取": "Çıkart",
+ "提取音高和处理数据使用的CPU进程数": "Ses yüksekliği çıkartmak (Pitch) ve verileri işlemek için kullanılacak CPU işlemci sayısı:",
+ "数据切分": "Veri dilimleme",
+ "无法停止": "Durdurulamıyor",
+ "无法读取音频:%s": "Ses okunamadı: %s",
+ "是": "Evet",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "Sadece en son '.ckpt' dosyasını kaydet:",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "Her kaydetme noktasında son küçük bir modeli 'weights' klasörüne kaydetmek için:",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "Tüm eğitim verilerini GPU belleğine önbelleğe alıp almayacağınızı belirtin. Küçük veri setlerini (10 dakikadan az) önbelleğe almak eğitimi hızlandırabilir, ancak büyük veri setlerini önbelleğe almak çok fazla GPU belleği tüketir ve çok fazla hız artışı sağlamaz:",
+ "显卡信息": "GPU Bilgisi",
+ "未使用": "Kullanılmadı",
+ "未使用判别器预训练模型": "Önceden eğitilmiş ayırıcı modeli kullanılmadı",
+ "未使用生成器预训练模型": "Önceden eğitilmiş üretici modeli kullanılmadı",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "Roformer model yapılandırması bulunamadı; yerleşik varsayılanlar kullanılacak",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "Desteklenen GPU algılanmadı; CPU ile eğitim çok daha uzun sürebilir",
+ "未运行": "Başlatılmadı",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "Bu yazılım, MIT lisansı altında açık kaynaklıdır. Yazarın yazılım üzerinde herhangi bir kontrolü yoktur. Yazılımı kullanan ve yazılım tarafından dışa aktarılan sesleri dağıtan kullanıcılar sorumludur.
Eğer bu maddeyle aynı fikirde değilseniz, yazılım paketi içindeki herhangi bir kod veya dosyayı kullanamaz veya referans göremezsiniz. Detaylar için kök dizindeki Agreement-LICENSE.txt dosyasına bakınız.",
+ "查看": "Görüntüle",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "Model bilgilerini görüntüle (sadece 'weights' klasöründen çıkarılan küçük model dosyaları desteklenir)",
+ "检测到模型类型:%s": "Algılanan model türü: %s",
+ "检索特征占比": "Arama özelliği oranı (vurgu gücünü kontrol eder, çok yüksek olması sanal etkilere neden olur)",
+ "模型": "Model",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "Model bilgisi: %s\nÖrnekleme hızı: %s\nPerde yönlendirmesi: %s\nSürüm: %s",
+ "模型推理": "Model çıkartma (Inference)",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "Model çıkartma (büyük dosya modeli yolunu 'logs' klasöründe girin). Bu, eğitimi yarıda bırakmak istediğinizde ve manuel olarak küçük bir model dosyası çıkartmak ve kaydetmek istediğinizde veya bir ara modeli test etmek istediğinizde kullanışlıdır:",
+ "模型是否带音高指导": "Modelin ses yüksekliği rehberi içerip içermediği:",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "Modelin ses yüksekliği (Pitch) rehberliği içerip içermediği (şarkı söyleme için şarttır, konuşma için isteğe bağlıdır):",
+ "模型是否带音高指导,1是0否": "Modelin ses yüksekliği rehberi içerip içermediği (1: evet, 0: hayır):",
+ "模型版本型号": "Model mimari versiyonu:",
+ "模型融合, 可用于测试音色融合": "Model birleştirme, ses rengi birleştirmesi için kullanılabilir",
+ "模型融合失败:两个模型的结构不一致": "Model birleştirme başarısız: iki modelin mimarisi eşleşmiyor",
+ "模型训练": "Model eğitimi",
+ "模型路径": "Model Yolu:",
+ "正在保存最终检查点:%s": "Son kontrol noktası kaydediliyor: %s",
+ "正在保存检查点 %s_e%s:%s": "Kontrol noktası kaydediliyor %s_e%s: %s",
+ "正在加载RMVPE模型": "RMVPE modeli yükleniyor",
+ "正在加载模型": "Model yükleniyor",
+ "正在启动": "Başlatılıyor",
+ "正在收尾": "Tamamlanıyor",
+ "每张显卡的batch_size": "Her GPU için yığın boyutu (batch_size):",
+ "淡入淡出长度": "Geçiş (Fade) uzunluğu",
+ "清理模型缓存": "Model önbelleği temizleniyor",
+ "版本": "Sürüm",
+ "特征": "Özellikler",
+ "特征提取": "Özellik çıkartma",
+ "状态": "Durum",
+ "独占 WASAPI 设备": "Özel WASAPI cihazı",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "Generator pretrained model not found; it will not be used: assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "Erkekten kadına çevirmek için +12 tuş önerilir, kadından erkeğe çevirmek için ise -12 tuş önerilir. Eğer ses aralığı çok fazla genişler ve ses bozulursa, isteğe bağlı olarak uygun aralığa kendiniz de ayarlayabilirsiniz.",
+ "目标采样率": "Hedef örnekleme oranı:",
+ "等待中": "Bekliyor",
+ "等待输入": "Girdi bekleniyor",
+ "算法延迟(ms):": "Algoritma gecikmesi (ms):",
+ "索引": "İndeks",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "Geçersiz indeks: şunu kullanın added_xxxx.index, not trained_xxxx.index",
+ "索引检索失败": "İndeks araması başarısız",
+ "索引检索失败或未启用": "İndeks araması başarısız veya devre dışı",
+ "索引训练": "İndeks eğitimi",
+ "耗时": "Geçen süre",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "Geçen süre: features=%.3fs, index=%.3fs, pitch=%.3fs, model=%.3fs",
+ "融合": "Birleştir",
+ "要改的模型信息": "Düzenlenecek model bilgileri:",
+ "要置入的模型信息": "Eklemek için model bilgileri:",
+ "训练": "Eğitim",
+ "训练已完成,正在保存最终模型": "Eğitim tamamlandı; son model kaydediliyor",
+ "训练文件列表写入完成": "Eğitim dosyası listesi yazıldı",
+ "训练模型": "Modeli Eğit",
+ "训练特征索引": "Özellik Dizinini Eğit",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "Eğitim tamamlandı. Eğitim günlüklerini konsolda veya deney klasörü altındaki train.log dosyasında kontrol edebilirsiniz.",
+ "训练设备规则选择的精度:%s": "Eğitim hassasiyeti cihaz kurallarına göre seçildi: %s",
+ "训练轮次:{} [{:.0f}%]": "Eğitim dönemi: {} [{:.0f}%]",
+ "设备类型": "Cihaz türü",
+ "请上传音频文件": "Bir ses dosyası yükleyin",
+ "请填写输出文件夹路径": "Çıktı klasörü yolunu girin",
+ "请指定说话人id": "Lütfen konuşmacı/sanatçı no belirtin:",
+ "请选择index文件": "Lütfen .index dosyası seçin",
+ "请选择pth文件": "Lütfen .pth dosyası seçin",
+ "请选择说话人id": "Konuşmacı/Şarkıcı No seçin:",
+ "转换": "Dönüştür",
+ "输入实验名": "Deneysel adı girin:",
+ "输入待处理音频文件夹路径": "İşlenecek ses klasörünün yolunu girin:",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "İşlenecek ses klasörünün yolunu girin (dosya yöneticisinin adres çubuğundan kopyalayın):",
+ "输入待处理音频文件路径(默认是正确格式示例)": "İşlenecek ses dosyasının yolunu girin (varsayılan doğru format örneğidir):",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "Sesin hacim zarfını ayarlayın. 0'a yakın değerler, sesin orijinal vokallerin hacmine benzer olmasını sağlar. Düşük bir değerle ses gürültüsünü maskeleyebilir ve hacmi daha doğal bir şekilde duyulabilir hale getirebilirsiniz. 1'e yaklaştıkça sürekli bir yüksek ses seviyesi elde edilir:",
+ "输入监听": "Girişi izle",
+ "输入训练文件夹路径": "Eğitim klasörünün yolunu girin:",
+ "输入设备": "Giriş cihazı",
+ "输入设备:%s:%s": "Giriş cihazı: %s:%s",
+ "输入降噪": "Giriş gürültü azaltma",
+ "输出信息": "Çıkış bilgisi",
+ "输出变声": "Çıkış ses dönüşümü",
+ "输出设备": "Çıkış cihazı",
+ "输出设备:%s:%s": "Çıkış cihazı: %s:%s",
+ "输出降噪": "Çıkış gürültü azaltma",
+ "输出音频(右下角三个点,点了可以下载)": "Ses dosyasını dışa aktar (indirmek için sağ alt köşedeki üç noktaya tıklayın)",
+ "运行中": "Çalışıyor",
+ "进度": "İlerleme",
+ "选择.index文件": ".index dosyası seç",
+ "选择.pth文件": ".pth dosyası seç",
+ "选择模型": "Model seçin",
+ "选择索引": "İndeks seçin",
+ "选择音高提取算法": "Perde çıkarma algoritmasını seçin",
+ "采样率:": "Örnekleme hızı:",
+ "采样长度": "Örnekleme uzunluğu",
+ "重载设备列表": "Cihaz listesini yeniden yükle",
+ "错误信息:%s": "Hata ayrıntıları: %s",
+ "错误:未知模型:%s": "Hata: bilinmeyen model: %s",
+ "音调设置": "Pitch ayarları",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "Ses birden fazla kanala sahip ancak model mono; tüm kanalların ortalaması alınacak",
+ "音频设备": "Ses cihazı",
+ "音高全部为0,该音频无意义,跳过:%s": "Tüm perde değerleri sıfır; bu ses kullanılamaz ve atlanacak: %s",
+ "音高算法": "Perde algoritması",
+ "额外推理时长": "Ekstra çıkartma süresi",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "Veri ön işleme geçerli eğitim sesi üretmedi. Veri kümesini ve günlüğü kontrol edin.",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "Veri ön işleme 16 kHz ses üretmedi. Özellik çıkarma ve eğitim durduruldu.",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "Veri ön işleme çıktı dosyaları eşleşmiyor. Özellik çıkarma ve eğitim durduruldu.",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT özellik çıkarma geçerli sonuç üretmedi. Eğitim durduruldu.",
+ "F0提取没有生成有效结果,已停止训练": "F0 çıkarma geçerli sonuç üretmedi. Eğitim durduruldu.",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "Eğitim için geçerli ses yok. Önce veri ön işleme ve özellik çıkarmayı tamamlayın.",
+ "已完成阶段": "Tamamlanan aşamalar",
+ "已成功": "Başarılı",
+ "刷新音色列表": "Ses listesini yenile",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "Özellik dizini yolu (model seçilince otomatik eşleşir; düzenlenebilir)",
+ "[索引训练] 外部索引链接已存在:%s": "[Dizin eğitimi] Harici dizin bağlantısı zaten var: %s",
+ "[索引训练][跳过] added索引已存在:%s": "[Dizin eğitimi][Atlandı] added dizini zaten var: %s",
+ "[索引训练][跳过] trained索引已存在:%s": "[Dizin eğitimi][Atlandı] trained dizini zaten var: %s",
+ "当前设备:%s | 推理精度:%s": "Geçerli cihaz: %s | Çıkarım hassasiyeti: %s"
+}
diff --git a/i18n/locale/zh_CN.json b/i18n/locale/zh_CN.json
new file mode 100644
index 0000000..f64bbb3
--- /dev/null
+++ b/i18n/locale/zh_CN.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "人声提取激进程度",
+ "%s → 成功": "%s → 成功",
+ "%s运行中,请先停止该任务": "%s运行中,请先停止该任务",
+ "%s进程已终止": "%s进程已终止",
+ "====> 轮次:{} {}": "====> 轮次:{} {}",
+ "A模型权重": "A模型权重",
+ "A模型路径": "A模型路径",
+ "B模型路径": "B模型路径",
+ "CUDA可用:%s": "CUDA可用:%s",
+ "E:\\语音音频+标注\\米津玄师\\src": "E:\\语音音频+标注\\米津玄师\\src",
+ "F0与HuBERT特征提取": "F0与HuBERT特征提取",
+ "F0提取": "F0提取",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调",
+ "HuBERT特征": "HuBERT特征",
+ "Index Rate": "检索特征占比",
+ "RVC模型路径": "RVC模型路径",
+ "SOLA偏移:%d": "SOLA偏移:%d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s",
+ "[F0提取] 待处理:%s": "[F0提取] 待处理:%s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0提取] 无待处理音频,已全部跳过",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s",
+ "[F0提取][失败] %s": "[F0提取][失败] %s",
+ "[F0提取][失败] %s\n%s": "[F0提取][失败] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT特征] 无待处理音频,已全部跳过:%s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT特征] 正在加载模型:%s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT特征][失败] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT特征][失败] %s 包含NaN",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT特征][失败] 模型不存在:%s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[数据切分] 子任务完成 | 成功:%s | 失败:%s",
+ "[数据切分] 完成": "[数据切分] 完成",
+ "[数据切分] 开始": "[数据切分] 开始",
+ "[数据切分] 待处理:%s | 进程数:%s": "[数据切分] 待处理:%s | 进程数:%s",
+ "[数据切分] 进度:%s/%s | %s": "[数据切分] 进度:%s/%s | %s",
+ "[数据切分][失败] %s": "[数据切分][失败] %s",
+ "[数据切分][失败] %s\n%s": "[数据切分][失败] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s",
+ "[索引训练] 写入进度:%s/%s": "[索引训练] 写入进度:%s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[索引训练] 已链接索引到外部目录:%s",
+ "[索引训练] 成功构建索引:%s": "[索引训练] 成功构建索引:%s",
+ "[索引训练] 正在写入特征向量": "[索引训练] 正在写入特征向量",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[索引训练] 正在将%s条特征聚类为10000个中心",
+ "[索引训练] 正在训练索引": "[索引训练] 正在训练索引",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[索引训练] 特征形状:%s | IVF数量:%s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[索引训练][失败] 无法链接索引到外部目录:%s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[索引训练][失败] 请先进行特征提取",
+ "ckpt处理": "ckpt处理",
+ "index文件路径不可包含中文": "index文件路径不可包含中文",
+ "pth文件路径不可包含中文": "pth文件路径不可包含中文",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ",
+ "step1:正在处理数据": "step1:正在处理数据",
+ "step2:正在提取音高&正在提取特征": "step2:正在提取音高&正在提取特征",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)",
+ "step3: 填写训练设置, 开始训练模型和索引": "step3: 填写训练设置, 开始训练模型和索引",
+ "step3a:正在训练模型": "step3a:正在训练模型",
+ "step3b:正在训练索引": "step3b:正在训练索引",
+ "……仅显示最近10条失败记录": "……仅显示最近10条失败记录",
+ "……已省略前%s行,仅显示最新状态": "……已省略前%s行,仅显示最新状态",
+ "一键训练": "一键训练",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "也可批量输入音频文件, 二选一, 优先读文件夹",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。",
+ "仅支持pm和rmvpe音高提取算法": "仅支持pm和rmvpe音高提取算法",
+ "从训练检查点提取的模型": "从训练检查点提取的模型",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2",
+ "任务": "任务",
+ "伴奏人声分离&去混响&去回声": "伴奏人声分离&去混响&去回声",
+ "使用显卡:%s": "使用显卡:%s",
+ "使用模型采样率": "使用模型采样率",
+ "使用设备采样率": "使用设备采样率",
+ "保存名": "保存名",
+ "保存的文件名, 默认空为和源文件同名": "保存的文件名, 默认空为和源文件同名",
+ "保存的模型名不带后缀": "保存的模型名不带后缀",
+ "保存频率save_every_epoch": "保存频率save_every_epoch",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果",
+ "修改": "修改",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "修改模型信息(仅支持weights文件夹下提取的小模型文件)",
+ "停止一键训练": "停止一键训练",
+ "停止处理数据": "停止处理数据",
+ "停止特征提取": "停止特征提取",
+ "停止训练模型": "停止训练模型",
+ "停止训练索引": "停止训练索引",
+ "停止音频转换": "停止音频转换",
+ "全流程结束!": "全流程结束!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth",
+ "加载模型": "加载模型",
+ "加载预训练底模D路径": "加载预训练底模D路径",
+ "加载预训练底模G路径": "加载预训练底模G路径",
+ "单次推理": "单次推理",
+ "卸载音色省显存": "卸载音色省显存",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "变调(整数, 半音数量, 升八度12降八度-12)",
+ "合成": "合成",
+ "后处理重采样至最终采样率,0为不进行重采样": "后处理重采样至最终采样率,0为不进行重采样",
+ "否": "否",
+ "响应阈值": "响应阈值",
+ "响度因子": "响度因子",
+ "处理中": "处理中",
+ "处理数据": "处理数据",
+ "失败": "失败",
+ "失败记录": "失败记录",
+ "子进程执行失败,返回码:%s": "子进程执行失败,返回码:%s",
+ "导出文件格式": "导出文件格式",
+ "已停止": "已停止",
+ "已加载判别器预训练模型:%s": "已加载判别器预训练模型:%s",
+ "已加载生成器预训练模型:%s": "已加载生成器预训练模型:%s",
+ "已启用索引检索": "已启用索引检索",
+ "已完成": "已完成",
+ "已恢复判别器检查点": "已恢复判别器检查点",
+ "常见问题解答": "常见问题解答",
+ "常规设置": "常规设置",
+ "开始音频转换": "开始音频转换",
+ "当前": "当前",
+ "当前阶段": "当前阶段",
+ "很遗憾您这没有能用的显卡来支持您训练": "很遗憾您这没有能用的显卡来支持您训练",
+ "性别因子/声线粗细": "性别因子/声线粗细",
+ "性能设置": "性能设置",
+ "总训练轮数total_epoch": "总训练轮数total_epoch",
+ "成功": "成功",
+ "执行命令": "执行命令",
+ "批量推理": "批量推理",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ",
+ "拖拽或点击上传待处理音频": "拖拽或点击上传待处理音频",
+ "指定输出主人声文件夹": "指定输出主人声文件夹",
+ "指定输出文件夹": "指定输出文件夹",
+ "指定输出非主人声文件夹": "指定输出非主人声文件夹",
+ "推理时间(ms):": "推理时间(ms):",
+ "推理耗时:%.2f秒": "推理耗时:%.2f秒",
+ "推理音色": "推理音色",
+ "提取": "提取",
+ "提取音高和处理数据使用的CPU进程数": "提取音高和处理数据使用的CPU进程数",
+ "数据切分": "数据切分",
+ "无法停止": "无法停止",
+ "无法读取音频:%s": "无法读取音频:%s",
+ "是": "是",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "是否仅保存最新的ckpt文件以节省硬盘空间",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "是否在每次保存时间点将最终小模型保存至weights文件夹",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速",
+ "显卡信息": "显卡信息",
+ "未使用": "未使用",
+ "未使用判别器预训练模型": "未使用判别器预训练模型",
+ "未使用生成器预训练模型": "未使用生成器预训练模型",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "未找到Roformer模型配置文件,正在使用内置默认配置",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "未检测到可用显卡,将使用CPU训练,耗时可能较长",
+ "未运行": "未运行",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.",
+ "查看": "查看",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "查看模型信息(仅支持weights文件夹下提取的小模型文件)",
+ "检测到模型类型:%s": "检测到模型类型:%s",
+ "检索特征占比": "检索特征占比",
+ "模型": "模型",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s",
+ "模型推理": "模型推理",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况",
+ "模型是否带音高指导": "模型是否带音高指导",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "模型是否带音高指导(唱歌一定要, 语音可以不要)",
+ "模型是否带音高指导,1是0否": "模型是否带音高指导,1是0否",
+ "模型版本型号": "模型版本型号",
+ "模型融合, 可用于测试音色融合": "模型融合, 可用于测试音色融合",
+ "模型融合失败:两个模型的结构不一致": "模型融合失败:两个模型的结构不一致",
+ "模型训练": "模型训练",
+ "模型路径": "模型路径",
+ "正在保存最终检查点:%s": "正在保存最终检查点:%s",
+ "正在保存检查点 %s_e%s:%s": "正在保存检查点 %s_e%s:%s",
+ "正在加载RMVPE模型": "正在加载RMVPE模型",
+ "正在加载模型": "正在加载模型",
+ "正在启动": "正在启动",
+ "正在收尾": "正在收尾",
+ "每张显卡的batch_size": "每张显卡的batch_size",
+ "淡入淡出长度": "淡入淡出长度",
+ "清理模型缓存": "清理模型缓存",
+ "版本": "版本",
+ "特征": "特征",
+ "特征提取": "特征提取",
+ "状态": "状态",
+ "独占 WASAPI 设备": "独占 WASAPI 设备",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ",
+ "目标采样率": "目标采样率",
+ "等待中": "等待中",
+ "等待输入": "等待输入",
+ "算法延迟(ms):": "算法延迟(ms):",
+ "索引": "索引",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index",
+ "索引检索失败": "索引检索失败",
+ "索引检索失败或未启用": "索引检索失败或未启用",
+ "索引训练": "索引训练",
+ "耗时": "耗时",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒",
+ "融合": "融合",
+ "要改的模型信息": "要改的模型信息",
+ "要置入的模型信息": "要置入的模型信息",
+ "训练": "训练",
+ "训练已完成,正在保存最终模型": "训练已完成,正在保存最终模型",
+ "训练文件列表写入完成": "训练文件列表写入完成",
+ "训练模型": "训练模型",
+ "训练特征索引": "训练特征索引",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log",
+ "训练设备规则选择的精度:%s": "训练设备规则选择的精度:%s",
+ "训练轮次:{} [{:.0f}%]": "训练轮次:{} [{:.0f}%]",
+ "设备类型": "设备类型",
+ "请上传音频文件": "请上传音频文件",
+ "请填写输出文件夹路径": "请填写输出文件夹路径",
+ "请指定说话人id": "请指定说话人id",
+ "请选择index文件": "请选择index文件",
+ "请选择pth文件": "请选择pth文件",
+ "请选择说话人id": "请选择说话人id",
+ "转换": "转换",
+ "输入实验名": "输入实验名",
+ "输入待处理音频文件夹路径": "输入待处理音频文件夹路径",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)",
+ "输入待处理音频文件路径(默认是正确格式示例)": "输入待处理音频文件路径(默认是正确格式示例)",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络",
+ "输入监听": "输入监听",
+ "输入训练文件夹路径": "输入训练文件夹路径",
+ "输入设备": "输入设备",
+ "输入设备:%s:%s": "输入设备:%s:%s",
+ "输入降噪": "输入降噪",
+ "输出信息": "输出信息",
+ "输出变声": "输出变声",
+ "输出设备": "输出设备",
+ "输出设备:%s:%s": "输出设备:%s:%s",
+ "输出降噪": "输出降噪",
+ "输出音频(右下角三个点,点了可以下载)": "输出音频(右下角三个点,点了可以下载)",
+ "运行中": "运行中",
+ "进度": "进度",
+ "选择.index文件": "选择.index文件",
+ "选择.pth文件": "选择.pth文件",
+ "选择模型": "选择模型",
+ "选择索引": "选择索引",
+ "选择音高提取算法": "选择音高提取算法",
+ "采样率:": "采样率:",
+ "采样长度": "采样长度",
+ "重载设备列表": "重载设备列表",
+ "错误信息:%s": "错误信息:%s",
+ "错误:未知模型:%s": "错误:未知模型:%s",
+ "音调设置": "音调设置",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值",
+ "音频设备": "音频设备",
+ "音高全部为0,该音频无意义,跳过:%s": "音高全部为0,该音频无意义,跳过:%s",
+ "音高算法": "音高算法",
+ "额外推理时长": "额外推理时长",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "数据切分没有生成有效训练音频,请检查训练集和数据切分日志",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "数据切分没有生成16k音频,已停止后续特征提取和训练",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "数据切分输出文件不匹配,已停止后续特征提取和训练",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT特征提取没有生成有效结果,已停止训练",
+ "F0提取没有生成有效结果,已停止训练": "F0提取没有生成有效结果,已停止训练",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "没有可用于训练的有效音频,请先完成数据切分和特征提取",
+ "已完成阶段": "已完成阶段",
+ "已成功": "已成功",
+ "刷新音色列表": "刷新音色列表",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "特征检索库文件路径(选择模型后自动匹配,可手动修改)",
+ "[索引训练] 外部索引链接已存在:%s": "[索引训练] 外部索引链接已存在:%s",
+ "[索引训练][跳过] added索引已存在:%s": "[索引训练][跳过] added索引已存在:%s",
+ "[索引训练][跳过] trained索引已存在:%s": "[索引训练][跳过] trained索引已存在:%s",
+ "当前设备:%s | 推理精度:%s": "当前设备:%s | 推理精度:%s"
+}
diff --git a/i18n/locale/zh_HK.json b/i18n/locale/zh_HK.json
new file mode 100644
index 0000000..cac4134
--- /dev/null
+++ b/i18n/locale/zh_HK.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "人聲提取激進程度",
+ "%s → 成功": "%s → 成功",
+ "%s运行中,请先停止该任务": "%s运行中,请先停止该任务",
+ "%s进程已终止": "%s進程已终止",
+ "====> 轮次:{} {}": "====> 輪次:{} {}",
+ "A模型权重": "A模型權重",
+ "A模型路径": "A模型路徑",
+ "B模型路径": "B模型路徑",
+ "CUDA可用:%s": "CUDA可用:%s",
+ "E:\\语音音频+标注\\米津玄师\\src": "E:\\语音音频+标注\\米津玄师\\src",
+ "F0与HuBERT特征提取": "F0與HuBERT特徵提取",
+ "F0提取": "F0提取",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0曲線檔案,可選,一行一個音高,代替預設的F0及升降調",
+ "HuBERT特征": "HuBERT特徵",
+ "Index Rate": "Index Rate",
+ "RVC模型路径": "RVC模型路径",
+ "SOLA偏移:%d": "SOLA偏移:%d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0提取] 完成 | 成功:%s | 跳過:%s | 失敗:%s",
+ "[F0提取] 待处理:%s": "[F0提取] 待处理:%s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0提取] 無待处理音頻,已全部跳過",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0提取] 進度:%s/%s | 成功:%s | 跳過:%s | %s",
+ "[F0提取][失败] %s": "[F0提取][失敗] %s",
+ "[F0提取][失败] %s\n%s": "[F0提取][失敗] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT特徵] 完成 | 成功:%s | 跳過:%s | 失敗:%s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT特徵] 無待处理音頻,已全部跳過:%s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT特徵] 正在載入模型:%s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT特徵] 设备:%s | 待处理:%s | 已跳過:%s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT特徵] 進度:%s/%s | 成功:%s | 失敗:%s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT特徵][失敗] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT特徵][失敗] %s 包含NaN",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT特徵][失敗] 模型不存在:%s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[數據切分] 子任务完成 | 成功:%s | 失敗:%s",
+ "[数据切分] 完成": "[數據切分] 完成",
+ "[数据切分] 开始": "[數據切分] 开始",
+ "[数据切分] 待处理:%s | 进程数:%s": "[數據切分] 待处理:%s | 進程数:%s",
+ "[数据切分] 进度:%s/%s | %s": "[數據切分] 進度:%s/%s | %s",
+ "[数据切分][失败] %s": "[數據切分][失敗] %s",
+ "[数据切分][失败] %s\n%s": "[數據切分][失敗] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[數據切分][跳過] 無效或異常音頻片段:%s_%s | 峰值:%s",
+ "[索引训练] 写入进度:%s/%s": "[索引訓練] 写入進度:%s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[索引訓練] 已連結索引到外部目录:%s",
+ "[索引训练] 成功构建索引:%s": "[索引訓練] 成功构建索引:%s",
+ "[索引训练] 正在写入特征向量": "[索引訓練] 正在写入特徵向量",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[索引訓練] 正在将%s條特徵聚类為10000个中心",
+ "[索引训练] 正在训练索引": "[索引訓練] 正在訓練索引",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[索引訓練] 特徵形状:%s | IVF數量:%s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[索引訓練][失敗] 無法連結索引到外部目录:%s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[索引訓練][失敗] 聚类失敗,将使用原始特徵繼續\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[索引訓練][失敗] 请先進行特徵提取",
+ "ckpt处理": "ckpt處理",
+ "index文件路径不可包含中文": "index文件路径不可包含中文",
+ "pth文件路径不可包含中文": "pth文件路径不可包含中文",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "rmvpe卡號配置:以-分隔輸入使用的不同進程卡號,例如0-0-1使用在卡0上跑2個進程並在卡1上跑1個進程",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "step1:填寫實驗配置。實驗數據放在logs下,每個實驗一個資料夾,需手動輸入實驗名路徑,內含實驗配置、日誌、訓練得到的模型檔案。",
+ "step1:正在处理数据": "step1:正在处理数据",
+ "step2:正在提取音高&正在提取特征": "step2:正在提取音高&正在提取特征",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "step2a:自動遍歷訓練資料夾下所有可解碼成音頻的檔案並進行切片歸一化,在實驗目錄下生成2個wav資料夾;暫時只支援單人訓練。",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "步驟2b: 使用CPU提取音高(如果模型帶音高), 使用GPU提取特徵(選擇卡號)",
+ "step3: 填写训练设置, 开始训练模型和索引": "步驟3: 填寫訓練設定, 開始訓練模型和索引",
+ "step3a:正在训练模型": "step3a:正在训练模型",
+ "step3b:正在训练索引": "step3b:正在訓練索引",
+ "……仅显示最近10条失败记录": "……僅顯示最近10條失敗記錄",
+ "……已省略前%s行,仅显示最新状态": "……已省略前%s行,僅顯示最新状态",
+ "一键训练": "一鍵訓練",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "也可批量输入音频文件, 二选一, 优先读文件夹",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "批次處理人聲與伴奏分離,使用UVR5模型。
可選擇保留人聲的模型,或使用DeEcho、DeReverb模型去除回音和混響。",
+ "仅支持pm和rmvpe音高提取算法": "僅支援pm和rmvpe音高提取算法",
+ "从训练检查点提取的模型": "从訓練检查点提取的模型",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "以-分隔輸入使用的卡號, 例如 0-1-2 使用卡0和卡1和卡2",
+ "任务": "任务",
+ "伴奏人声分离&去混响&去回声": "伴奏人聲分離&去混響&去回聲",
+ "使用显卡:%s": "使用顯示卡:%s",
+ "使用模型采样率": "使用模型采样率",
+ "使用设备采样率": "使用设备采样率",
+ "保存名": "儲存名",
+ "保存的文件名, 默认空为和源文件同名": "儲存的檔案名,預設空為與來源檔案同名",
+ "保存的模型名不带后缀": "儲存的模型名不帶副檔名",
+ "保存频率save_every_epoch": "保存頻率save_every_epoch",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "保護清輔音和呼吸聲,防止電音撕裂等artifact,拉滿0.5不開啟,調低加大保護力度但可能降低索引效果",
+ "修改": "修改",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "修改模型資訊(僅支援weights資料夾下提取的小模型檔案)",
+ "停止一键训练": "停止一鍵訓練",
+ "停止处理数据": "停止處理資料",
+ "停止特征提取": "停止特徵擷取",
+ "停止训练模型": "停止訓練模型",
+ "停止训练索引": "停止訓練索引",
+ "停止音频转换": "停止音訊轉換",
+ "全流程结束!": "全流程结束!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "判别器预訓練模型不存在,将不使用:assets/pretrained%s/%sD%s.pth",
+ "加载模型": "載入模型",
+ "加载预训练底模D路径": "加載預訓練底模D路徑",
+ "加载预训练底模G路径": "加載預訓練底模G路徑",
+ "单次推理": "单次推理",
+ "卸载音色省显存": "卸載音色節省 VRAM",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "變調(整數、半音數量、升八度12降八度-12)",
+ "合成": "合成",
+ "后处理重采样至最终采样率,0为不进行重采样": "後處理重採樣至最終採樣率,0為不進行重採樣",
+ "否": "否",
+ "响应阈值": "響應閾值",
+ "响度因子": "響度因子",
+ "处理中": "處理中",
+ "处理数据": "處理資料",
+ "失败": "失敗",
+ "失败记录": "失敗記錄",
+ "子进程执行失败,返回码:%s": "子進程执行失敗,返回码:%s",
+ "导出文件格式": "導出檔格式",
+ "已停止": "已停止",
+ "已加载判别器预训练模型:%s": "已載入判别器预訓練模型:%s",
+ "已加载生成器预训练模型:%s": "已載入生成器预訓練模型:%s",
+ "已启用索引检索": "已启用索引检索",
+ "已完成": "已完成",
+ "已恢复判别器检查点": "已恢复判别器检查点",
+ "常见问题解答": "常見問題解答",
+ "常规设置": "一般設定",
+ "开始音频转换": "開始音訊轉換",
+ "当前": "目前",
+ "当前阶段": "目前阶段",
+ "很遗憾您这没有能用的显卡来支持您训练": "很遗憾您这没有能用的显卡来支持您训练",
+ "性别因子/声线粗细": "性別因子/聲線粗細",
+ "性能设置": "效能設定",
+ "总训练轮数total_epoch": "總訓練輪數total_epoch",
+ "成功": "成功",
+ "执行命令": "执行命令",
+ "批量推理": "批量推理",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "批量轉換,輸入待轉換音頻資料夾,或上傳多個音頻檔案,在指定資料夾(默認opt)下輸出轉換的音頻。",
+ "拖拽或点击上传待处理音频": "拖放或按一下上載待處理音訊",
+ "指定输出主人声文件夹": "指定输出主人声文件夹",
+ "指定输出文件夹": "指定輸出資料夾",
+ "指定输出非主人声文件夹": "指定输出非主人声文件夹",
+ "推理时间(ms):": "推理時間(ms):",
+ "推理耗时:%.2f秒": "推理耗时:%.2f秒",
+ "推理音色": "推理音色",
+ "提取": "提取",
+ "提取音高和处理数据使用的CPU进程数": "提取音高和處理數據使用的CPU進程數",
+ "数据切分": "數據切分",
+ "无法停止": "無法停止",
+ "无法读取音频:%s": "無法读取音頻:%s",
+ "是": "是",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "是否僅保存最新的ckpt檔案以節省硬碟空間",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "是否在每次保存時間點將最終小模型保存至weights檔夾",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "是否緩存所有訓練集至 VRAM。小於10分鐘的小數據可緩存以加速訓練,大數據緩存會爆 VRAM 也加不了多少速度",
+ "显卡信息": "顯示卡資訊",
+ "未使用": "未使用",
+ "未使用判别器预训练模型": "未使用判别器预訓練模型",
+ "未使用生成器预训练模型": "未使用生成器预訓練模型",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "未找到Roformer模型配置檔案,正在使用内置預設配置",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "未偵測到可用顯示卡,将使用CPU訓練,耗时可能较长",
+ "未运行": "未运行",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "本軟體以MIT協議開源,作者不對軟體具備任何控制力,使用軟體者、傳播軟體導出的聲音者自負全責。
如不認可該條款,則不能使用或引用軟體包內任何程式碼和檔案。詳見根目錄使用需遵守的協議-LICENSE.txt。",
+ "查看": "查看",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "查看模型資訊(僅支援weights資料夾下提取的小模型檔案)",
+ "检测到模型类型:%s": "偵測到模型类型:%s",
+ "检索特征占比": "檢索特徵佔比",
+ "模型": "模型",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "模型資訊:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s",
+ "模型推理": "模型推理",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "模型提取(輸入logs資料夾下大檔案模型路徑),適用於訓一半不想訓了模型沒有自動提取儲存小檔案模型,或者想測試中間模型的情況",
+ "模型是否带音高指导": "模型是否帶音高指導",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "模型是否帶音高指導(唱歌一定要,語音可以不要)",
+ "模型是否带音高指导,1是0否": "模型是否帶音高指導,1是0否",
+ "模型版本型号": "模型版本型號",
+ "模型融合, 可用于测试音色融合": "模型融合,可用於測試音色融合",
+ "模型融合失败:两个模型的结构不一致": "模型融合失敗:两个模型的结构不一致",
+ "模型训练": "模型訓練",
+ "模型路径": "模型路徑",
+ "正在保存最终检查点:%s": "正在儲存最终检查点:%s",
+ "正在保存检查点 %s_e%s:%s": "正在儲存检查点 %s_e%s:%s",
+ "正在加载RMVPE模型": "正在載入RMVPE模型",
+ "正在加载模型": "正在載入模型",
+ "正在启动": "正在启动",
+ "正在收尾": "正在收尾",
+ "每张显卡的batch_size": "每张显卡的batch_size",
+ "淡入淡出长度": "淡入淡出長度",
+ "清理模型缓存": "清理模型缓存",
+ "版本": "版本",
+ "特征": "特徵",
+ "特征提取": "特徵提取",
+ "状态": "状态",
+ "独占 WASAPI 设备": "独占 WASAPI 设备",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "生成器预訓練模型不存在,将不使用:assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "男性轉女性推薦+12key,女性轉男性推薦-12key,如果音域爆炸導致音色失真也可以自己調整到合適音域。",
+ "目标采样率": "目標取樣率",
+ "等待中": "等待中",
+ "等待输入": "等待輸入",
+ "算法延迟(ms):": "算法延迟(ms):",
+ "索引": "索引",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "索引無效:必须使用added_xxxx.index,不能使用trained_xxxx.index",
+ "索引检索失败": "索引检索失敗",
+ "索引检索失败或未启用": "索引检索失敗或未启用",
+ "索引训练": "索引訓練",
+ "耗时": "耗时",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "耗时:特徵=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒",
+ "融合": "融合",
+ "要改的模型信息": "要改的模型資訊",
+ "要置入的模型信息": "要置入的模型資訊",
+ "训练": "訓練",
+ "训练已完成,正在保存最终模型": "訓練已完成,正在儲存最终模型",
+ "训练文件列表写入完成": "訓練檔案列表写入完成",
+ "训练模型": "訓練模型",
+ "训练特征索引": "訓練特徵索引",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log",
+ "训练设备规则选择的精度:%s": "訓練设备规则选择的精度:%s",
+ "训练轮次:{} [{:.0f}%]": "訓練輪次:{} [{:.0f}%]",
+ "设备类型": "设备类型",
+ "请上传音频文件": "请上传音頻檔案",
+ "请填写输出文件夹路径": "请填写輸出資料夾路径",
+ "请指定说话人id": "請指定說話人id",
+ "请选择index文件": "请选择index文件",
+ "请选择pth文件": "请选择pth文件",
+ "请选择说话人id": "請選擇說話人ID",
+ "转换": "轉換",
+ "输入实验名": "輸入實驗名稱",
+ "输入待处理音频文件夹路径": "輸入待處理音頻資料夾路徑",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "輸入待處理音頻資料夾路徑(去檔案管理器地址欄拷貝即可)",
+ "输入待处理音频文件路径(默认是正确格式示例)": "輸入待處理音頻檔案路徑(預設是正確格式示例)",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "輸入源音量包絡替換輸出音量包絡融合比例,越靠近1越使用輸出包絡",
+ "输入监听": "输入监听",
+ "输入训练文件夹路径": "輸入訓練檔案夾路徑",
+ "输入设备": "輸入設備",
+ "输入设备:%s:%s": "輸入设备:%s:%s",
+ "输入降噪": "輸入降噪",
+ "输出信息": "輸出訊息",
+ "输出变声": "输出变声",
+ "输出设备": "輸出設備",
+ "输出设备:%s:%s": "輸出设备:%s:%s",
+ "输出降噪": "輸出降噪",
+ "输出音频(右下角三个点,点了可以下载)": "輸出音頻(右下角三個點,點了可以下載)",
+ "运行中": "运行中",
+ "进度": "進度",
+ "选择.index文件": "選擇 .index 檔案",
+ "选择.pth文件": "選擇 .pth 檔案",
+ "选择模型": "选择模型",
+ "选择索引": "选择索引",
+ "选择音高提取算法": "選擇音高擷取演算法",
+ "采样率:": "采样率:",
+ "采样长度": "取樣長度",
+ "重载设备列表": "重載設備列表",
+ "错误信息:%s": "錯誤資訊:%s",
+ "错误:未知模型:%s": "錯誤:未知模型:%s",
+ "音调设置": "音調設定",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "音頻包含多个声道,但模型僅支援单声道,将对所有声道取平均值",
+ "音频设备": "音訊設備",
+ "音高全部为0,该音频无意义,跳过:%s": "音高全部為0,该音頻無意义,跳過:%s",
+ "音高算法": "音高演算法",
+ "额外推理时长": "額外推理時長",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "資料切分沒有產生有效訓練音訊,請檢查訓練集和資料切分日誌",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "資料切分沒有產生16k音訊,已停止後續特徵提取和訓練",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "資料切分輸出檔案不匹配,已停止後續特徵提取和訓練",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT特徵提取沒有產生有效結果,已停止訓練",
+ "F0提取没有生成有效结果,已停止训练": "F0提取沒有產生有效結果,已停止訓練",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "沒有可用於訓練的有效音訊,請先完成資料切分和特徵提取",
+ "已完成阶段": "已完成階段",
+ "已成功": "已成功",
+ "刷新音色列表": "重新整理音色列表",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "特徵索引庫檔案路徑(選擇模型後自動配對,可手動修改)",
+ "[索引训练] 外部索引链接已存在:%s": "[索引訓練] 外部索引連結已存在:%s",
+ "[索引训练][跳过] added索引已存在:%s": "[索引訓練][跳過] added索引已存在:%s",
+ "[索引训练][跳过] trained索引已存在:%s": "[索引訓練][跳過] trained索引已存在:%s",
+ "当前设备:%s | 推理精度:%s": "目前裝置:%s | 推理精度:%s"
+}
diff --git a/i18n/locale/zh_SG.json b/i18n/locale/zh_SG.json
new file mode 100644
index 0000000..57e46ab
--- /dev/null
+++ b/i18n/locale/zh_SG.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "人声提取激进程度",
+ "%s → 成功": "%s → 成功",
+ "%s运行中,请先停止该任务": "%s运行中,请先停止该任务",
+ "%s进程已终止": "%s进程已终止",
+ "====> 轮次:{} {}": "====> 轮次:{} {}",
+ "A模型权重": "A模型權重",
+ "A模型路径": "A模型路徑",
+ "B模型路径": "B模型路徑",
+ "CUDA可用:%s": "CUDA可用:%s",
+ "E:\\语音音频+标注\\米津玄师\\src": "E:\\语音音频+标注\\米津玄师\\src",
+ "F0与HuBERT特征提取": "F0与HuBERT特征提取",
+ "F0提取": "F0提取",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0曲線檔案,可選,一行一個音高,代替預設的F0及升降調",
+ "HuBERT特征": "HuBERT特征",
+ "Index Rate": "Index Rate",
+ "RVC模型路径": "RVC模型路径",
+ "SOLA偏移:%d": "SOLA偏移:%d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s",
+ "[F0提取] 待处理:%s": "[F0提取] 待处理:%s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0提取] 无待处理音频,已全部跳过",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s",
+ "[F0提取][失败] %s": "[F0提取][失败] %s",
+ "[F0提取][失败] %s\n%s": "[F0提取][失败] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT特征] 无待处理音频,已全部跳过:%s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT特征] 正在加载模型:%s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT特征][失败] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT特征][失败] %s 包含NaN",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT特征][失败] 模型不存在:%s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[数据切分] 子任务完成 | 成功:%s | 失败:%s",
+ "[数据切分] 完成": "[数据切分] 完成",
+ "[数据切分] 开始": "[数据切分] 开始",
+ "[数据切分] 待处理:%s | 进程数:%s": "[数据切分] 待处理:%s | 进程数:%s",
+ "[数据切分] 进度:%s/%s | %s": "[数据切分] 进度:%s/%s | %s",
+ "[数据切分][失败] %s": "[数据切分][失败] %s",
+ "[数据切分][失败] %s\n%s": "[数据切分][失败] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s",
+ "[索引训练] 写入进度:%s/%s": "[索引训练] 写入进度:%s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[索引训练] 已链接索引到外部目录:%s",
+ "[索引训练] 成功构建索引:%s": "[索引训练] 成功构建索引:%s",
+ "[索引训练] 正在写入特征向量": "[索引训练] 正在写入特征向量",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[索引训练] 正在将%s条特征聚类为10000个中心",
+ "[索引训练] 正在训练索引": "[索引训练] 正在训练索引",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[索引训练] 特征形状:%s | IVF数量:%s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[索引训练][失败] 无法链接索引到外部目录:%s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[索引训练][失败] 请先进行特征提取",
+ "ckpt处理": "ckpt處理",
+ "index文件路径不可包含中文": "index文件路径不可包含中文",
+ "pth文件路径不可包含中文": "pth文件路径不可包含中文",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "rmvpe卡號配置:以-分隔輸入使用的不同進程卡號,例如0-0-1使用在卡0上跑2個進程並在卡1上跑1個進程",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "step1:填寫實驗配置。實驗數據放在logs下,每個實驗一個資料夾,需手動輸入實驗名路徑,內含實驗配置、日誌、訓練得到的模型檔案。",
+ "step1:正在处理数据": "step1:正在处理数据",
+ "step2:正在提取音高&正在提取特征": "step2:正在提取音高&正在提取特征",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "step2a:自動遍歷訓練資料夾下所有可解碼成音頻的檔案並進行切片歸一化,在實驗目錄下生成2個wav資料夾;暫時只支援單人訓練。",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "步驟2b: 使用CPU提取音高(如果模型帶音高), 使用GPU提取特徵(選擇卡號)",
+ "step3: 填写训练设置, 开始训练模型和索引": "步驟3: 填寫訓練設定, 開始訓練模型和索引",
+ "step3a:正在训练模型": "step3a:正在训练模型",
+ "step3b:正在训练索引": "step3b:正在训练索引",
+ "……仅显示最近10条失败记录": "……仅显示最近10条失败记录",
+ "……已省略前%s行,仅显示最新状态": "……已省略前%s行,仅显示最新状态",
+ "一键训练": "一鍵訓練",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "也可批量输入音频文件, 二选一, 优先读文件夹",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。",
+ "仅支持pm和rmvpe音高提取算法": "仅支持pm和rmvpe音高提取算法",
+ "从训练检查点提取的模型": "从训练检查点提取的模型",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "以-分隔輸入使用的卡號, 例如 0-1-2 使用卡0和卡1和卡2",
+ "任务": "任务",
+ "伴奏人声分离&去混响&去回声": "伴奏人聲分離&去混響&去回聲",
+ "使用显卡:%s": "使用显卡:%s",
+ "使用模型采样率": "使用模型采样率",
+ "使用设备采样率": "使用设备采样率",
+ "保存名": "儲存名",
+ "保存的文件名, 默认空为和源文件同名": "儲存的檔案名,預設空為與來源檔案同名",
+ "保存的模型名不带后缀": "儲存的模型名不帶副檔名",
+ "保存频率save_every_epoch": "保存頻率save_every_epoch",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "保護清輔音和呼吸聲,防止電音撕裂等artifact,拉滿0.5不開啟,調低加大保護力度但可能降低索引效果",
+ "修改": "修改",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "修改模型資訊(僅支援weights資料夾下提取的小模型檔案)",
+ "停止一键训练": "停止一键训练",
+ "停止处理数据": "停止处理数据",
+ "停止特征提取": "停止特征提取",
+ "停止训练模型": "停止训练模型",
+ "停止训练索引": "停止训练索引",
+ "停止音频转换": "停止音訊轉換",
+ "全流程结束!": "全流程结束!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth",
+ "加载模型": "載入模型",
+ "加载预训练底模D路径": "加載預訓練底模D路徑",
+ "加载预训练底模G路径": "加載預訓練底模G路徑",
+ "单次推理": "单次推理",
+ "卸载音色省显存": "卸載音色節省 VRAM",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "變調(整數、半音數量、升八度12降八度-12)",
+ "合成": "合成",
+ "后处理重采样至最终采样率,0为不进行重采样": "後處理重採樣至最終採樣率,0為不進行重採樣",
+ "否": "否",
+ "响应阈值": "響應閾值",
+ "响度因子": "響度因子",
+ "处理中": "处理中",
+ "处理数据": "處理資料",
+ "失败": "失败",
+ "失败记录": "失败记录",
+ "子进程执行失败,返回码:%s": "子进程执行失败,返回码:%s",
+ "导出文件格式": "導出檔格式",
+ "已停止": "已停止",
+ "已加载判别器预训练模型:%s": "已加载判别器预训练模型:%s",
+ "已加载生成器预训练模型:%s": "已加载生成器预训练模型:%s",
+ "已启用索引检索": "已启用索引检索",
+ "已完成": "已完成",
+ "已恢复判别器检查点": "已恢复判别器检查点",
+ "常见问题解答": "常見問題解答",
+ "常规设置": "一般設定",
+ "开始音频转换": "開始音訊轉換",
+ "当前": "当前",
+ "当前阶段": "当前阶段",
+ "很遗憾您这没有能用的显卡来支持您训练": "很遗憾您这没有能用的显卡来支持您训练",
+ "性别因子/声线粗细": "性别因子/声线粗细",
+ "性能设置": "效能設定",
+ "总训练轮数total_epoch": "總訓練輪數total_epoch",
+ "成功": "成功",
+ "执行命令": "执行命令",
+ "批量推理": "批量推理",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "批量轉換,輸入待轉換音頻資料夾,或上傳多個音頻檔案,在指定資料夾(默認opt)下輸出轉換的音頻。",
+ "拖拽或点击上传待处理音频": "拖拽或点击上传待处理音频",
+ "指定输出主人声文件夹": "指定输出主人声文件夹",
+ "指定输出文件夹": "指定輸出資料夾",
+ "指定输出非主人声文件夹": "指定输出非主人声文件夹",
+ "推理时间(ms):": "推理時間(ms):",
+ "推理耗时:%.2f秒": "推理耗时:%.2f秒",
+ "推理音色": "推理音色",
+ "提取": "提取",
+ "提取音高和处理数据使用的CPU进程数": "提取音高和處理數據使用的CPU進程數",
+ "数据切分": "数据切分",
+ "无法停止": "无法停止",
+ "无法读取音频:%s": "无法读取音频:%s",
+ "是": "是",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "是否僅保存最新的ckpt檔案以節省硬碟空間",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "是否在每次保存時間點將最終小模型保存至weights檔夾",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "是否緩存所有訓練集至 VRAM。小於10分鐘的小數據可緩存以加速訓練,大數據緩存會爆 VRAM 也加不了多少速度",
+ "显卡信息": "顯示卡資訊",
+ "未使用": "未使用",
+ "未使用判别器预训练模型": "未使用判别器预训练模型",
+ "未使用生成器预训练模型": "未使用生成器预训练模型",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "未找到Roformer模型配置文件,正在使用内置默认配置",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "未检测到可用显卡,将使用CPU训练,耗时可能较长",
+ "未运行": "未运行",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "本軟體以MIT協議開源,作者不對軟體具備任何控制力,使用軟體者、傳播軟體導出的聲音者自負全責。
如不認可該條款,則不能使用或引用軟體包內任何程式碼和檔案。詳見根目錄使用需遵守的協議-LICENSE.txt。",
+ "查看": "查看",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "查看模型資訊(僅支援weights資料夾下提取的小模型檔案)",
+ "检测到模型类型:%s": "检测到模型类型:%s",
+ "检索特征占比": "檢索特徵佔比",
+ "模型": "模型",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s",
+ "模型推理": "模型推理",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "模型提取(輸入logs資料夾下大檔案模型路徑),適用於訓一半不想訓了模型沒有自動提取儲存小檔案模型,或者想測試中間模型的情況",
+ "模型是否带音高指导": "模型是否帶音高指導",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "模型是否帶音高指導(唱歌一定要,語音可以不要)",
+ "模型是否带音高指导,1是0否": "模型是否帶音高指導,1是0否",
+ "模型版本型号": "模型版本型號",
+ "模型融合, 可用于测试音色融合": "模型融合,可用於測試音色融合",
+ "模型融合失败:两个模型的结构不一致": "模型融合失败:两个模型的结构不一致",
+ "模型训练": "模型训练",
+ "模型路径": "模型路徑",
+ "正在保存最终检查点:%s": "正在保存最终检查点:%s",
+ "正在保存检查点 %s_e%s:%s": "正在保存检查点 %s_e%s:%s",
+ "正在加载RMVPE模型": "正在加载RMVPE模型",
+ "正在加载模型": "正在加载模型",
+ "正在启动": "正在启动",
+ "正在收尾": "正在收尾",
+ "每张显卡的batch_size": "每张显卡的batch_size",
+ "淡入淡出长度": "淡入淡出長度",
+ "清理模型缓存": "清理模型缓存",
+ "版本": "版本",
+ "特征": "特征",
+ "特征提取": "特徵提取",
+ "状态": "状态",
+ "独占 WASAPI 设备": "独占 WASAPI 设备",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "男性轉女性推薦+12key,女性轉男性推薦-12key,如果音域爆炸導致音色失真也可以自己調整到合適音域。",
+ "目标采样率": "目標取樣率",
+ "等待中": "等待中",
+ "等待输入": "等待输入",
+ "算法延迟(ms):": "算法延迟(ms):",
+ "索引": "索引",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index",
+ "索引检索失败": "索引检索失败",
+ "索引检索失败或未启用": "索引检索失败或未启用",
+ "索引训练": "索引训练",
+ "耗时": "耗时",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒",
+ "融合": "融合",
+ "要改的模型信息": "要改的模型資訊",
+ "要置入的模型信息": "要置入的模型資訊",
+ "训练": "訓練",
+ "训练已完成,正在保存最终模型": "训练已完成,正在保存最终模型",
+ "训练文件列表写入完成": "训练文件列表写入完成",
+ "训练模型": "訓練模型",
+ "训练特征索引": "訓練特徵索引",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log",
+ "训练设备规则选择的精度:%s": "训练设备规则选择的精度:%s",
+ "训练轮次:{} [{:.0f}%]": "训练轮次:{} [{:.0f}%]",
+ "设备类型": "设备类型",
+ "请上传音频文件": "请上传音频文件",
+ "请填写输出文件夹路径": "请填写输出文件夹路径",
+ "请指定说话人id": "請指定說話人id",
+ "请选择index文件": "请选择index文件",
+ "请选择pth文件": "请选择pth文件",
+ "请选择说话人id": "請選擇說話人ID",
+ "转换": "轉換",
+ "输入实验名": "輸入實驗名稱",
+ "输入待处理音频文件夹路径": "輸入待處理音頻資料夾路徑",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "輸入待處理音頻資料夾路徑(去檔案管理器地址欄拷貝即可)",
+ "输入待处理音频文件路径(默认是正确格式示例)": "輸入待處理音頻檔案路徑(預設是正確格式示例)",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "輸入源音量包絡替換輸出音量包絡融合比例,越靠近1越使用輸出包絡",
+ "输入监听": "输入监听",
+ "输入训练文件夹路径": "輸入訓練檔案夾路徑",
+ "输入设备": "輸入設備",
+ "输入设备:%s:%s": "输入设备:%s:%s",
+ "输入降噪": "輸入降噪",
+ "输出信息": "輸出訊息",
+ "输出变声": "输出变声",
+ "输出设备": "輸出設備",
+ "输出设备:%s:%s": "输出设备:%s:%s",
+ "输出降噪": "輸出降噪",
+ "输出音频(右下角三个点,点了可以下载)": "輸出音頻(右下角三個點,點了可以下載)",
+ "运行中": "运行中",
+ "进度": "进度",
+ "选择.index文件": "選擇 .index 檔案",
+ "选择.pth文件": "選擇 .pth 檔案",
+ "选择模型": "选择模型",
+ "选择索引": "选择索引",
+ "选择音高提取算法": "选择音高提取算法",
+ "采样率:": "采样率:",
+ "采样长度": "取樣長度",
+ "重载设备列表": "重載設備列表",
+ "错误信息:%s": "错误信息:%s",
+ "错误:未知模型:%s": "错误:未知模型:%s",
+ "音调设置": "音調設定",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值",
+ "音频设备": "音訊設備",
+ "音高全部为0,该音频无意义,跳过:%s": "音高全部为0,该音频无意义,跳过:%s",
+ "音高算法": "音高演算法",
+ "额外推理时长": "額外推理時長",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "数据切分没有生成有效训练音频,请检查训练集和数据切分日志",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "数据切分没有生成16k音频,已停止后续特征提取和训练",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "数据切分输出文件不匹配,已停止后续特征提取和训练",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT特征提取没有生成有效结果,已停止训练",
+ "F0提取没有生成有效结果,已停止训练": "F0提取没有生成有效结果,已停止训练",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "没有可用于训练的有效音频,请先完成数据切分和特征提取",
+ "已完成阶段": "已完成阶段",
+ "已成功": "已成功",
+ "刷新音色列表": "刷新音色列表",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "特征检索库文件路径(选择模型后自动匹配,可手动修改)",
+ "[索引训练] 外部索引链接已存在:%s": "[索引训练] 外部索引链接已存在:%s",
+ "[索引训练][跳过] added索引已存在:%s": "[索引训练][跳过] added索引已存在:%s",
+ "[索引训练][跳过] trained索引已存在:%s": "[索引训练][跳过] trained索引已存在:%s",
+ "当前设备:%s | 推理精度:%s": "当前设备:%s | 推理精度:%s"
+}
diff --git a/i18n/locale/zh_TW.json b/i18n/locale/zh_TW.json
new file mode 100644
index 0000000..db3c5cb
--- /dev/null
+++ b/i18n/locale/zh_TW.json
@@ -0,0 +1,261 @@
+{
+ "人声提取激进程度": "人聲提取激進程度",
+ "%s → 成功": "%s → 成功",
+ "%s运行中,请先停止该任务": "%s运行中,请先停止该任务",
+ "%s进程已终止": "%s進程已终止",
+ "====> 轮次:{} {}": "====> 輪次:{} {}",
+ "A模型权重": "A模型權重",
+ "A模型路径": "A模型路徑",
+ "B模型路径": "B模型路徑",
+ "CUDA可用:%s": "CUDA可用:%s",
+ "E:\\语音音频+标注\\米津玄师\\src": "E:\\语音音频+标注\\米津玄师\\src",
+ "F0与HuBERT特征提取": "F0與HuBERT特徵提取",
+ "F0提取": "F0提取",
+ "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调": "F0曲線檔案,可選,一行一個音高,代替預設的F0及升降調",
+ "HuBERT特征": "HuBERT特徵",
+ "Index Rate": "Index Rate",
+ "RVC模型路径": "RVC模型路径",
+ "SOLA偏移:%d": "SOLA偏移:%d",
+ "[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[F0提取] 完成 | 成功:%s | 跳過:%s | 失敗:%s",
+ "[F0提取] 待处理:%s": "[F0提取] 待处理:%s",
+ "[F0提取] 无待处理音频,已全部跳过": "[F0提取] 無待处理音頻,已全部跳過",
+ "[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s": "[F0提取] 進度:%s/%s | 成功:%s | 跳過:%s | %s",
+ "[F0提取][失败] %s": "[F0提取][失敗] %s",
+ "[F0提取][失败] %s\n%s": "[F0提取][失敗] %s\n%s",
+ "[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s": "[HuBERT特徵] 完成 | 成功:%s | 跳過:%s | 失敗:%s",
+ "[HuBERT特征] 无待处理音频,已全部跳过:%s": "[HuBERT特徵] 無待处理音頻,已全部跳過:%s",
+ "[HuBERT特征] 正在加载模型:%s": "[HuBERT特徵] 正在載入模型:%s",
+ "[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s": "[HuBERT特徵] 设备:%s | 待处理:%s | 已跳過:%s",
+ "[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s": "[HuBERT特徵] 進度:%s/%s | 成功:%s | 失敗:%s | %s | %s",
+ "[HuBERT特征][失败] %s\n%s": "[HuBERT特徵][失敗] %s\n%s",
+ "[HuBERT特征][失败] %s 包含NaN": "[HuBERT特徵][失敗] %s 包含NaN",
+ "[HuBERT特征][失败] 模型不存在:%s": "[HuBERT特徵][失敗] 模型不存在:%s",
+ "[数据切分] 子任务完成 | 成功:%s | 失败:%s": "[數據切分] 子任务完成 | 成功:%s | 失敗:%s",
+ "[数据切分] 完成": "[數據切分] 完成",
+ "[数据切分] 开始": "[數據切分] 开始",
+ "[数据切分] 待处理:%s | 进程数:%s": "[數據切分] 待处理:%s | 進程数:%s",
+ "[数据切分] 进度:%s/%s | %s": "[數據切分] 進度:%s/%s | %s",
+ "[数据切分][失败] %s": "[數據切分][失敗] %s",
+ "[数据切分][失败] %s\n%s": "[數據切分][失敗] %s\n%s",
+ "[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s": "[數據切分][跳過] 無效或異常音頻片段:%s_%s | 峰值:%s",
+ "[索引训练] 写入进度:%s/%s": "[索引訓練] 写入進度:%s/%s",
+ "[索引训练] 已链接索引到外部目录:%s": "[索引訓練] 已連結索引到外部目录:%s",
+ "[索引训练] 成功构建索引:%s": "[索引訓練] 成功构建索引:%s",
+ "[索引训练] 正在写入特征向量": "[索引訓練] 正在写入特徵向量",
+ "[索引训练] 正在将%s条特征聚类为10000个中心": "[索引訓練] 正在将%s條特徵聚类為10000个中心",
+ "[索引训练] 正在训练索引": "[索引訓練] 正在訓練索引",
+ "[索引训练] 特征形状:%s | IVF数量:%s": "[索引訓練] 特徵形状:%s | IVF數量:%s",
+ "[索引训练][失败] 无法链接索引到外部目录:%s\n%s": "[索引訓練][失敗] 無法連結索引到外部目录:%s\n%s",
+ "[索引训练][失败] 聚类失败,将使用原始特征继续\n%s": "[索引訓練][失敗] 聚类失敗,将使用原始特徵繼續\n%s",
+ "[索引训练][失败] 请先进行特征提取": "[索引訓練][失敗] 请先進行特徵提取",
+ "ckpt处理": "ckpt處理",
+ "index文件路径不可包含中文": "index文件路径不可包含中文",
+ "pth文件路径不可包含中文": "pth文件路径不可包含中文",
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程": "rmvpe卡號配置:以-分隔輸入使用的不同進程卡號,例如0-0-1使用在卡0上跑2個進程並在卡1上跑1個進程",
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. ": "step1:填寫實驗配置。實驗數據放在logs下,每個實驗一個資料夾,需手動輸入實驗名路徑,內含實驗配置、日誌、訓練得到的模型檔案。",
+ "step1:正在处理数据": "step1:正在处理数据",
+ "step2:正在提取音高&正在提取特征": "step2:正在提取音高&正在提取特征",
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. ": "step2a:自動遍歷訓練資料夾下所有可解碼成音頻的檔案並進行切片歸一化,在實驗目錄下生成2個wav資料夾;暫時只支援單人訓練。",
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)": "步驟2b: 使用CPU提取音高(如果模型帶音高), 使用GPU提取特徵(選擇卡號)",
+ "step3: 填写训练设置, 开始训练模型和索引": "步驟3: 填寫訓練設定, 開始訓練模型和索引",
+ "step3a:正在训练模型": "step3a:正在训练模型",
+ "step3b:正在训练索引": "step3b:正在訓練索引",
+ "……仅显示最近10条失败记录": "……僅顯示最近10條失敗記錄",
+ "……已省略前%s行,仅显示最新状态": "……已省略前%s行,僅顯示最新状态",
+ "一键训练": "一鍵訓練",
+ "也可批量输入音频文件, 二选一, 优先读文件夹": "也可批量输入音频文件, 二选一, 优先读文件夹",
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。": "批次處理人聲與伴奏分離,使用UVR5模型。
可選擇保留人聲的模型,或使用DeEcho、DeReverb模型去除回音和混響。",
+ "仅支持pm和rmvpe音高提取算法": "僅支援pm和rmvpe音高提取算法",
+ "从训练检查点提取的模型": "从訓練检查点提取的模型",
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2": "以-分隔輸入使用的卡號, 例如 0-1-2 使用卡0和卡1和卡2",
+ "任务": "任务",
+ "伴奏人声分离&去混响&去回声": "伴奏人聲分離&去混響&去回聲",
+ "使用显卡:%s": "使用顯示卡:%s",
+ "使用模型采样率": "使用模型采样率",
+ "使用设备采样率": "使用设备采样率",
+ "保存名": "儲存名",
+ "保存的文件名, 默认空为和源文件同名": "儲存的檔案名,預設空為與來源檔案同名",
+ "保存的模型名不带后缀": "儲存的模型名不帶副檔名",
+ "保存频率save_every_epoch": "保存頻率save_every_epoch",
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果": "保護清輔音和呼吸聲,防止電音撕裂等artifact,拉滿0.5不開啟,調低加大保護力度但可能降低索引效果",
+ "修改": "修改",
+ "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "修改模型資訊(僅支援weights資料夾下提取的小模型檔案)",
+ "停止一键训练": "停止一鍵訓練",
+ "停止处理数据": "停止處理資料",
+ "停止特征提取": "停止特徵擷取",
+ "停止训练模型": "停止訓練模型",
+ "停止训练索引": "停止訓練索引",
+ "停止音频转换": "停止音訊轉換",
+ "全流程结束!": "全流程结束!",
+ "判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth": "判别器预訓練模型不存在,将不使用:assets/pretrained%s/%sD%s.pth",
+ "加载模型": "載入模型",
+ "加载预训练底模D路径": "加載預訓練底模D路徑",
+ "加载预训练底模G路径": "加載預訓練底模G路徑",
+ "单次推理": "单次推理",
+ "卸载音色省显存": "卸載音色節省 VRAM",
+ "变调(整数, 半音数量, 升八度12降八度-12)": "變調(整數、半音數量、升八度12降八度-12)",
+ "合成": "合成",
+ "后处理重采样至最终采样率,0为不进行重采样": "後處理重採樣至最終採樣率,0為不進行重採樣",
+ "否": "否",
+ "响应阈值": "響應閾值",
+ "响度因子": "響度因子",
+ "处理中": "處理中",
+ "处理数据": "處理資料",
+ "失败": "失敗",
+ "失败记录": "失敗記錄",
+ "子进程执行失败,返回码:%s": "子進程执行失敗,返回码:%s",
+ "导出文件格式": "導出檔格式",
+ "已停止": "已停止",
+ "已加载判别器预训练模型:%s": "已載入判别器预訓練模型:%s",
+ "已加载生成器预训练模型:%s": "已載入生成器预訓練模型:%s",
+ "已启用索引检索": "已启用索引检索",
+ "已完成": "已完成",
+ "已恢复判别器检查点": "已恢复判别器检查点",
+ "常见问题解答": "常見問題解答",
+ "常规设置": "一般設定",
+ "开始音频转换": "開始音訊轉換",
+ "当前": "目前",
+ "当前阶段": "目前阶段",
+ "很遗憾您这没有能用的显卡来支持您训练": "很遗憾您这没有能用的显卡来支持您训练",
+ "性别因子/声线粗细": "性別因子/聲線粗細",
+ "性能设置": "效能設定",
+ "总训练轮数total_epoch": "總訓練輪數total_epoch",
+ "成功": "成功",
+ "执行命令": "执行命令",
+ "批量推理": "批量推理",
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ": "批量轉換,輸入待轉換音頻資料夾,或上傳多個音頻檔案,在指定資料夾(默認opt)下輸出轉換的音頻。",
+ "拖拽或点击上传待处理音频": "拖放或點擊上傳待處理音訊",
+ "指定输出主人声文件夹": "指定输出主人声文件夹",
+ "指定输出文件夹": "指定輸出資料夾",
+ "指定输出非主人声文件夹": "指定输出非主人声文件夹",
+ "推理时间(ms):": "推理時間(ms):",
+ "推理耗时:%.2f秒": "推理耗时:%.2f秒",
+ "推理音色": "推理音色",
+ "提取": "提取",
+ "提取音高和处理数据使用的CPU进程数": "提取音高和處理數據使用的CPU進程數",
+ "数据切分": "數據切分",
+ "无法停止": "無法停止",
+ "无法读取音频:%s": "無法读取音頻:%s",
+ "是": "是",
+ "是否仅保存最新的ckpt文件以节省硬盘空间": "是否僅保存最新的ckpt檔案以節省硬碟空間",
+ "是否在每次保存时间点将最终小模型保存至weights文件夹": "是否在每次保存時間點將最終小模型保存至weights檔夾",
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速": "是否緩存所有訓練集至 VRAM。小於10分鐘的小數據可緩存以加速訓練,大數據緩存會爆 VRAM 也加不了多少速度",
+ "显卡信息": "顯示卡資訊",
+ "未使用": "未使用",
+ "未使用判别器预训练模型": "未使用判别器预訓練模型",
+ "未使用生成器预训练模型": "未使用生成器预訓練模型",
+ "未找到Roformer模型配置文件,正在使用内置默认配置": "未找到Roformer模型配置檔案,正在使用内置預設配置",
+ "未检测到可用显卡,将使用CPU训练,耗时可能较长": "未偵測到可用顯示卡,将使用CPU訓練,耗时可能较长",
+ "未运行": "未运行",
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE.": "本軟體以MIT協議開源,作者不對軟體具備任何控制力,使用軟體者、傳播軟體導出的聲音者自負全責。
如不認可該條款,則不能使用或引用軟體包內任何程式碼和檔案。詳見根目錄使用需遵守的協議-LICENSE.txt。",
+ "查看": "查看",
+ "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "查看模型資訊(僅支援weights資料夾下提取的小模型檔案)",
+ "检测到模型类型:%s": "偵測到模型类型:%s",
+ "检索特征占比": "檢索特徵佔比",
+ "模型": "模型",
+ "模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s": "模型資訊:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s",
+ "模型推理": "模型推理",
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "模型提取(輸入logs資料夾下大檔案模型路徑),適用於訓一半不想訓了模型沒有自動提取儲存小檔案模型,或者想測試中間模型的情況",
+ "模型是否带音高指导": "模型是否帶音高指導",
+ "模型是否带音高指导(唱歌一定要, 语音可以不要)": "模型是否帶音高指導(唱歌一定要,語音可以不要)",
+ "模型是否带音高指导,1是0否": "模型是否帶音高指導,1是0否",
+ "模型版本型号": "模型版本型號",
+ "模型融合, 可用于测试音色融合": "模型融合,可用於測試音色融合",
+ "模型融合失败:两个模型的结构不一致": "模型融合失敗:两个模型的结构不一致",
+ "模型训练": "模型訓練",
+ "模型路径": "模型路徑",
+ "正在保存最终检查点:%s": "正在儲存最终检查点:%s",
+ "正在保存检查点 %s_e%s:%s": "正在儲存检查点 %s_e%s:%s",
+ "正在加载RMVPE模型": "正在載入RMVPE模型",
+ "正在加载模型": "正在載入模型",
+ "正在启动": "正在启动",
+ "正在收尾": "正在收尾",
+ "每张显卡的batch_size": "每张显卡的batch_size",
+ "淡入淡出长度": "淡入淡出長度",
+ "清理模型缓存": "清理模型缓存",
+ "版本": "版本",
+ "特征": "特徵",
+ "特征提取": "特徵提取",
+ "状态": "状态",
+ "独占 WASAPI 设备": "独占 WASAPI 设备",
+ "生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth": "生成器预訓練模型不存在,将不使用:assets/pretrained%s/%sG%s.pth",
+ "男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ": "男性轉女性推薦+12key,女性轉男性推薦-12key,如果音域爆炸導致音色失真也可以自己調整到合適音域。",
+ "目标采样率": "目標取樣率",
+ "等待中": "等待中",
+ "等待输入": "等待輸入",
+ "算法延迟(ms):": "算法延迟(ms):",
+ "索引": "索引",
+ "索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index": "索引無效:必须使用added_xxxx.index,不能使用trained_xxxx.index",
+ "索引检索失败": "索引检索失敗",
+ "索引检索失败或未启用": "索引检索失敗或未启用",
+ "索引训练": "索引訓練",
+ "耗时": "耗时",
+ "耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒": "耗时:特徵=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒",
+ "融合": "融合",
+ "要改的模型信息": "要改的模型資訊",
+ "要置入的模型信息": "要置入的模型資訊",
+ "训练": "訓練",
+ "训练已完成,正在保存最终模型": "訓練已完成,正在儲存最终模型",
+ "训练文件列表写入完成": "訓練檔案列表写入完成",
+ "训练模型": "訓練模型",
+ "训练特征索引": "訓練特徵索引",
+ "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log": "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log",
+ "训练设备规则选择的精度:%s": "訓練设备规则选择的精度:%s",
+ "训练轮次:{} [{:.0f}%]": "訓練輪次:{} [{:.0f}%]",
+ "设备类型": "设备类型",
+ "请上传音频文件": "请上传音頻檔案",
+ "请填写输出文件夹路径": "请填写輸出資料夾路径",
+ "请指定说话人id": "請指定說話人id",
+ "请选择index文件": "请选择index文件",
+ "请选择pth文件": "请选择pth文件",
+ "请选择说话人id": "請選擇說話人ID",
+ "转换": "轉換",
+ "输入实验名": "輸入實驗名稱",
+ "输入待处理音频文件夹路径": "輸入待處理音頻資料夾路徑",
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "輸入待處理音頻資料夾路徑(去檔案管理器地址欄拷貝即可)",
+ "输入待处理音频文件路径(默认是正确格式示例)": "輸入待處理音頻檔案路徑(預設是正確格式示例)",
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络": "輸入源音量包絡替換輸出音量包絡融合比例,越靠近1越使用輸出包絡",
+ "输入监听": "输入监听",
+ "输入训练文件夹路径": "輸入訓練檔案夾路徑",
+ "输入设备": "輸入設備",
+ "输入设备:%s:%s": "輸入设备:%s:%s",
+ "输入降噪": "輸入降噪",
+ "输出信息": "輸出訊息",
+ "输出变声": "输出变声",
+ "输出设备": "輸出設備",
+ "输出设备:%s:%s": "輸出设备:%s:%s",
+ "输出降噪": "輸出降噪",
+ "输出音频(右下角三个点,点了可以下载)": "輸出音頻(右下角三個點,點了可以下載)",
+ "运行中": "运行中",
+ "进度": "進度",
+ "选择.index文件": "選擇 .index 檔案",
+ "选择.pth文件": "選擇 .pth 檔案",
+ "选择模型": "选择模型",
+ "选择索引": "选择索引",
+ "选择音高提取算法": "選擇音高擷取演算法",
+ "采样率:": "采样率:",
+ "采样长度": "取樣長度",
+ "重载设备列表": "重載設備列表",
+ "错误信息:%s": "錯誤資訊:%s",
+ "错误:未知模型:%s": "錯誤:未知模型:%s",
+ "音调设置": "音調設定",
+ "音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值": "音頻包含多个声道,但模型僅支援单声道,将对所有声道取平均值",
+ "音频设备": "音訊設備",
+ "音高全部为0,该音频无意义,跳过:%s": "音高全部為0,该音頻無意义,跳過:%s",
+ "音高算法": "音高演算法",
+ "额外推理时长": "額外推理時長",
+ "数据切分没有生成有效训练音频,请检查训练集和数据切分日志": "資料切分沒有產生有效訓練音訊,請檢查訓練集和資料切分日誌",
+ "数据切分没有生成16k音频,已停止后续特征提取和训练": "資料切分沒有產生16k音訊,已停止後續特徵提取和訓練",
+ "数据切分输出文件不匹配,已停止后续特征提取和训练": "資料切分輸出檔案不匹配,已停止後續特徵提取和訓練",
+ "HuBERT特征提取没有生成有效结果,已停止训练": "HuBERT特徵提取沒有產生有效結果,已停止訓練",
+ "F0提取没有生成有效结果,已停止训练": "F0提取沒有產生有效結果,已停止訓練",
+ "没有可用于训练的有效音频,请先完成数据切分和特征提取": "沒有可用於訓練的有效音訊,請先完成資料切分和特徵提取",
+ "已完成阶段": "已完成階段",
+ "已成功": "已成功",
+ "刷新音色列表": "重新整理音色列表",
+ "特征检索库文件路径(选择模型后自动匹配,可手动修改)": "特徵索引庫檔案路徑(選擇模型後自動配對,可手動修改)",
+ "[索引训练] 外部索引链接已存在:%s": "[索引訓練] 外部索引連結已存在:%s",
+ "[索引训练][跳过] added索引已存在:%s": "[索引訓練][跳過] added索引已存在:%s",
+ "[索引训练][跳过] trained索引已存在:%s": "[索引訓練][跳過] trained索引已存在:%s",
+ "当前设备:%s | 推理精度:%s": "目前裝置:%s | 推論精度:%s"
+}
diff --git a/infer/audio.py b/infer/audio.py
new file mode 100644
index 0000000..450e13e
--- /dev/null
+++ b/infer/audio.py
@@ -0,0 +1,51 @@
+import platform, os
+import ffmpeg
+import numpy as np
+import av
+from io import BytesIO
+
+
+def wav2(i, o, format):
+ inp = av.open(i, "r")
+ if format == "m4a":
+ format = "mp4"
+ out = av.open(o, "w", format=format)
+ if format == "ogg":
+ format = "libvorbis"
+ if format == "mp4":
+ format = "aac"
+
+ ostream = out.add_stream(format)
+
+ for frame in inp.decode(audio=0):
+ for p in ostream.encode(frame):
+ out.mux(p)
+
+ for p in ostream.encode(None):
+ out.mux(p)
+
+ out.close()
+ inp.close()
+
+
+def load_audio(file, sr):
+ try:
+ # https://github.com/openai/whisper/blob/main/whisper/audio.py#L26
+ # This launches a subprocess to decode audio while down-mixing and resampling as necessary.
+ # Requires the ffmpeg CLI and `ffmpeg-python` package to be installed.
+ file = clean_path(file) # 防止小白拷路径头尾带了空格和"和回车
+ out, _ = (
+ ffmpeg.input(file, threads=0)
+ .output("-", format="f32le", acodec="pcm_f32le", ac=1, ar=sr)
+ .run(cmd=["ffmpeg", "-nostdin"], capture_stdout=True, capture_stderr=True)
+ )
+ except Exception as e:
+ raise RuntimeError(f"Failed to load audio: {e}")
+
+ return np.frombuffer(out, np.float32).flatten()
+
+
+def clean_path(path_str):
+ if platform.system() == "Windows":
+ path_str = path_str.replace("/", "\\")
+ return path_str.strip(" ").strip('"').strip("\n").strip('"').strip(" ")
diff --git a/infer/fcpe.py b/infer/fcpe.py
new file mode 100644
index 0000000..c99d7f9
--- /dev/null
+++ b/infer/fcpe.py
@@ -0,0 +1,97 @@
+import torch
+
+
+def _is_directml_device(device):
+ """Return whether *device* is the PrivateUse1 device registered by DirectML."""
+ return getattr(device, "type", None) == "privateuseone" or "privateuseone" in str(
+ device
+ ).lower()
+
+
+class FCPEInfer:
+ """Project-local FCPE inference adapter with a DirectML execution path.
+
+ DirectML does not support the complex tensor produced by ``torch.stft`` in
+ torchfcpe's wav2mel stage. Keep preprocessing and the small indexed
+ decoder on CPU while the FCPE neural network runs on DirectML. Other
+ devices retain torchfcpe's original end-to-end inference path.
+ """
+
+ def __init__(self, device):
+ from torchfcpe import spawn_bundled_infer_model
+
+ self.device = device
+ self.is_directml = _is_directml_device(device)
+ if self.is_directml:
+ # Loading a checkpoint directly with map_location=privateuseone is
+ # not supported consistently. Load on CPU, leave wav2mel there,
+ # and move only the real-valued FCPE network to DirectML.
+ self.infer_model = spawn_bundled_infer_model("cpu")
+ self.infer_model.wav2mel.eval()
+ self.cent_table_cpu = (
+ self.infer_model.model.cent_table.detach().float().cpu().clone()
+ )
+ self.out_dims = int(self.infer_model.model.out_dims)
+ self.infer_model.model.to(device).eval()
+ else:
+ self.infer_model = spawn_bundled_infer_model(device)
+
+ def _decode_on_cpu(self, latent, decoder_mode, threshold):
+ """Decode DML network logits on CPU with torchfcpe's exact formulas.
+
+ The current DirectML backend's ``aten::gather`` returns incorrect FCPE
+ bin values even though its indices and the neural-network logits match
+ CPU. Decoding is tiny compared with the model, so keep this
+ compatibility boundary on CPU as well as the complex STFT.
+ """
+ latent = latent.detach().float().cpu()
+ batch, frames, _ = latent.shape
+ cents = self.cent_table_cpu[None, None, :].expand(batch, frames, -1)
+
+ if decoder_mode == "argmax":
+ confidence = torch.max(latent, dim=-1, keepdim=True).values
+ decoded = torch.sum(cents * latent, dim=-1, keepdim=True) / torch.sum(
+ latent, dim=-1, keepdim=True
+ )
+ elif decoder_mode == "local_argmax":
+ confidence, max_index = torch.max(latent, dim=-1, keepdim=True)
+ local_index = torch.arange(9, dtype=torch.long) + (max_index - 4)
+ local_index.clamp_(0, self.out_dims - 1)
+ local_cents = torch.gather(cents, -1, local_index)
+ local_latent = torch.gather(latent, -1, local_index)
+ decoded = torch.sum(
+ local_cents * local_latent, dim=-1, keepdim=True
+ ) / torch.sum(local_latent, dim=-1, keepdim=True)
+ else:
+ raise ValueError(f"Unknown FCPE decoder mode: {decoder_mode}")
+
+ decoded = decoded.masked_fill(confidence <= threshold, float("-inf"))
+ return 10.0 * torch.pow(2.0, decoded / 1200.0)
+
+ # torch.no_grad is used instead of inference_mode because DirectML's
+ # PrivateUse1 backend still updates version counters in a few operators.
+ @torch.no_grad()
+ def infer(
+ self,
+ wav,
+ sr,
+ decoder_mode="local_argmax",
+ threshold=0.006,
+ ):
+ if not self.is_directml:
+ return self.infer_model.infer(
+ wav,
+ sr=sr,
+ decoder_mode=decoder_mode,
+ threshold=threshold,
+ )
+
+ wav_cpu = wav.detach().to(device="cpu", dtype=torch.float32)
+ mel_cpu = self.infer_model.wav2mel(wav_cpu, sr)
+ mel_dml = mel_cpu.to(device=self.device, dtype=torch.float32)
+ latent_dml = self.infer_model.model(mel_dml)
+ return self._decode_on_cpu(
+ latent_dml,
+ decoder_mode=decoder_mode,
+ threshold=threshold,
+ )
diff --git a/infer/hubert.py b/infer/hubert.py
new file mode 100644
index 0000000..128a628
--- /dev/null
+++ b/infer/hubert.py
@@ -0,0 +1,96 @@
+import logging
+from functools import lru_cache
+from pathlib import Path
+
+import torch
+from torch import nn
+from transformers import AutoFeatureExtractor, HubertModel
+
+
+logger = logging.getLogger(__name__)
+
+PROJECT_ROOT = Path(__file__).resolve().parent.parent
+
+
+class HubertModelWithFinalProj(HubertModel):
+ def __init__(self, config):
+ super().__init__(config)
+ self.final_proj = nn.Linear(config.hidden_size, config.classifier_proj_size)
+
+HUBERT_MODEL_PATH = (PROJECT_ROOT / "assets" / "hubert_base").resolve()
+
+
+def _device_type(device):
+ if isinstance(device, torch.device):
+ return device.type
+ return str(device).split(":", 1)[0]
+
+
+def load_hubert_model(device, is_half=False):
+ """Load the local Transformers HuBERT/ContentVec model for RVC."""
+ if not (HUBERT_MODEL_PATH / "config.json").is_file():
+ raise FileNotFoundError(
+ f"Transformers HuBERT model not found: {HUBERT_MODEL_PATH}"
+ )
+
+ dtype = torch.float16 if is_half else torch.float32
+ load_options = {
+ "local_files_only": True,
+ "torch_dtype": dtype,
+ }
+ # DirectML does not implement every SDPA kernel used by Transformers.
+ if _device_type(device) == "privateuseone":
+ load_options["attn_implementation"] = "eager"
+
+ logger.info(
+ "Loading Transformers HuBERT from %s (%s on %s)",
+ HUBERT_MODEL_PATH,
+ dtype,
+ device,
+ )
+ model = HubertModelWithFinalProj.from_pretrained(
+ str(HUBERT_MODEL_PATH), **load_options
+ )
+ model = model.to(device)
+ return model.eval()
+
+
+@lru_cache(maxsize=1)
+def hubert_audio_requires_normalization():
+ feature_extractor = AutoFeatureExtractor.from_pretrained(
+ str(HUBERT_MODEL_PATH), local_files_only=True
+ )
+ return bool(feature_extractor.do_normalize)
+
+
+def extract_hubert_features(model, source, version, padding_mask=None):
+ """Return the RVC v1 (256-D) or v2 (768-D) HuBERT representation.
+
+ Transformers hidden_states[N] is numerically equivalent to the source checkpoint's
+ output_layer=N for this converted checkpoint. RVC v1 uses layer 9 followed
+ by final_proj; RVC v2 uses the final (12th) encoder layer directly.
+ """
+ if version not in {"v1", "v2"}:
+ raise ValueError(f"Unsupported RVC feature version: {version!r}")
+
+ attention_mask = None
+ if padding_mask is not None and bool(torch.any(padding_mask).item()):
+ attention_mask = (~padding_mask.bool()).long()
+
+ if version == "v1":
+ outputs = model(
+ input_values=source,
+ attention_mask=attention_mask,
+ output_hidden_states=True,
+ return_dict=True,
+ )
+ features = outputs.hidden_states[9]
+ return model.final_proj(features)
+
+ outputs = model(
+ input_values=source,
+ attention_mask=attention_mask,
+ output_hidden_states=False,
+ return_dict=True,
+ )
+ return outputs.last_hidden_state
diff --git a/infer/module/attentions.py b/infer/module/attentions.py
new file mode 100644
index 0000000..377d7f0
--- /dev/null
+++ b/infer/module/attentions.py
@@ -0,0 +1,459 @@
+import copy
+import math
+from typing import Optional
+
+import numpy as np
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+from infer.module import commons, modules
+from infer.module.modules import LayerNorm
+
+
+class Encoder(nn.Module):
+ def __init__(
+ self,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size=1,
+ p_dropout=0.0,
+ window_size=10,
+ **kwargs
+ ):
+ super(Encoder, self).__init__()
+ self.hidden_channels = hidden_channels
+ self.filter_channels = filter_channels
+ self.n_heads = n_heads
+ self.n_layers = int(n_layers)
+ self.kernel_size = kernel_size
+ self.p_dropout = p_dropout
+ self.window_size = window_size
+
+ self.drop = nn.Dropout(p_dropout)
+ self.attn_layers = nn.ModuleList()
+ self.norm_layers_1 = nn.ModuleList()
+ self.ffn_layers = nn.ModuleList()
+ self.norm_layers_2 = nn.ModuleList()
+ for i in range(self.n_layers):
+ self.attn_layers.append(
+ MultiHeadAttention(
+ hidden_channels,
+ hidden_channels,
+ n_heads,
+ p_dropout=p_dropout,
+ window_size=window_size,
+ )
+ )
+ self.norm_layers_1.append(LayerNorm(hidden_channels))
+ self.ffn_layers.append(
+ FFN(
+ hidden_channels,
+ hidden_channels,
+ filter_channels,
+ kernel_size,
+ p_dropout=p_dropout,
+ )
+ )
+ self.norm_layers_2.append(LayerNorm(hidden_channels))
+
+ def forward(self, x, x_mask):
+ attn_mask = x_mask.unsqueeze(2) * x_mask.unsqueeze(-1)
+ x = x * x_mask
+ zippep = zip(
+ self.attn_layers, self.norm_layers_1, self.ffn_layers, self.norm_layers_2
+ )
+ for attn_layers, norm_layers_1, ffn_layers, norm_layers_2 in zippep:
+ y = attn_layers(x, x, attn_mask)
+ y = self.drop(y)
+ x = norm_layers_1(x + y)
+
+ y = ffn_layers(x, x_mask)
+ y = self.drop(y)
+ x = norm_layers_2(x + y)
+ x = x * x_mask
+ return x
+
+
+class Decoder(nn.Module):
+ def __init__(
+ self,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size=1,
+ p_dropout=0.0,
+ proximal_bias=False,
+ proximal_init=True,
+ **kwargs
+ ):
+ super(Decoder, self).__init__()
+ self.hidden_channels = hidden_channels
+ self.filter_channels = filter_channels
+ self.n_heads = n_heads
+ self.n_layers = n_layers
+ self.kernel_size = kernel_size
+ self.p_dropout = p_dropout
+ self.proximal_bias = proximal_bias
+ self.proximal_init = proximal_init
+
+ self.drop = nn.Dropout(p_dropout)
+ self.self_attn_layers = nn.ModuleList()
+ self.norm_layers_0 = nn.ModuleList()
+ self.encdec_attn_layers = nn.ModuleList()
+ self.norm_layers_1 = nn.ModuleList()
+ self.ffn_layers = nn.ModuleList()
+ self.norm_layers_2 = nn.ModuleList()
+ for i in range(self.n_layers):
+ self.self_attn_layers.append(
+ MultiHeadAttention(
+ hidden_channels,
+ hidden_channels,
+ n_heads,
+ p_dropout=p_dropout,
+ proximal_bias=proximal_bias,
+ proximal_init=proximal_init,
+ )
+ )
+ self.norm_layers_0.append(LayerNorm(hidden_channels))
+ self.encdec_attn_layers.append(
+ MultiHeadAttention(
+ hidden_channels, hidden_channels, n_heads, p_dropout=p_dropout
+ )
+ )
+ self.norm_layers_1.append(LayerNorm(hidden_channels))
+ self.ffn_layers.append(
+ FFN(
+ hidden_channels,
+ hidden_channels,
+ filter_channels,
+ kernel_size,
+ p_dropout=p_dropout,
+ causal=True,
+ )
+ )
+ self.norm_layers_2.append(LayerNorm(hidden_channels))
+
+ def forward(self, x, x_mask, h, h_mask):
+ """
+ x: decoder input
+ h: encoder output
+ """
+ self_attn_mask = commons.subsequent_mask(x_mask.size(2)).to(
+ device=x.device, dtype=x.dtype
+ )
+ encdec_attn_mask = h_mask.unsqueeze(2) * x_mask.unsqueeze(-1)
+ x = x * x_mask
+ for i in range(self.n_layers):
+ y = self.self_attn_layers[i](x, x, self_attn_mask)
+ y = self.drop(y)
+ x = self.norm_layers_0[i](x + y)
+
+ y = self.encdec_attn_layers[i](x, h, encdec_attn_mask)
+ y = self.drop(y)
+ x = self.norm_layers_1[i](x + y)
+
+ y = self.ffn_layers[i](x, x_mask)
+ y = self.drop(y)
+ x = self.norm_layers_2[i](x + y)
+ x = x * x_mask
+ return x
+
+
+class MultiHeadAttention(nn.Module):
+ def __init__(
+ self,
+ channels,
+ out_channels,
+ n_heads,
+ p_dropout=0.0,
+ window_size=None,
+ heads_share=True,
+ block_length=None,
+ proximal_bias=False,
+ proximal_init=False,
+ ):
+ super(MultiHeadAttention, self).__init__()
+ assert channels % n_heads == 0
+
+ self.channels = channels
+ self.out_channels = out_channels
+ self.n_heads = n_heads
+ self.p_dropout = p_dropout
+ self.window_size = window_size
+ self.heads_share = heads_share
+ self.block_length = block_length
+ self.proximal_bias = proximal_bias
+ self.proximal_init = proximal_init
+ self.attn = None
+
+ self.k_channels = channels // n_heads
+ self.conv_q = nn.Conv1d(channels, channels, 1)
+ self.conv_k = nn.Conv1d(channels, channels, 1)
+ self.conv_v = nn.Conv1d(channels, channels, 1)
+ self.conv_o = nn.Conv1d(channels, out_channels, 1)
+ self.drop = nn.Dropout(p_dropout)
+
+ if window_size is not None:
+ n_heads_rel = 1 if heads_share else n_heads
+ rel_stddev = self.k_channels**-0.5
+ self.emb_rel_k = nn.Parameter(
+ torch.randn(n_heads_rel, window_size * 2 + 1, self.k_channels)
+ * rel_stddev
+ )
+ self.emb_rel_v = nn.Parameter(
+ torch.randn(n_heads_rel, window_size * 2 + 1, self.k_channels)
+ * rel_stddev
+ )
+
+ nn.init.xavier_uniform_(self.conv_q.weight)
+ nn.init.xavier_uniform_(self.conv_k.weight)
+ nn.init.xavier_uniform_(self.conv_v.weight)
+ if proximal_init:
+ with torch.no_grad():
+ self.conv_k.weight.copy_(self.conv_q.weight)
+ self.conv_k.bias.copy_(self.conv_q.bias)
+
+ def forward(
+ self, x, c, attn_mask = None
+ ):
+ q = self.conv_q(x)
+ k = self.conv_k(c)
+ v = self.conv_v(c)
+
+ x, _ = self.attention(q, k, v, mask=attn_mask)
+
+ x = self.conv_o(x)
+ return x
+
+ def attention(
+ self,
+ query,
+ key,
+ value,
+ mask = None,
+ ):
+ # reshape [b, d, t] -> [b, n_h, t, d_k]
+ b, d, t_s = key.size()
+ t_t = query.size(2)
+ query = query.view(b, self.n_heads, self.k_channels, t_t).transpose(2, 3)
+ key = key.view(b, self.n_heads, self.k_channels, t_s).transpose(2, 3)
+ value = value.view(b, self.n_heads, self.k_channels, t_s).transpose(2, 3)
+
+ scores = torch.matmul(query / math.sqrt(self.k_channels), key.transpose(-2, -1))
+ if self.window_size is not None:
+ assert (
+ t_s == t_t
+ ), "Relative attention is only available for self-attention."
+ key_relative_embeddings = self._get_relative_embeddings(self.emb_rel_k, t_s)
+ rel_logits = self._matmul_with_relative_keys(
+ query / math.sqrt(self.k_channels), key_relative_embeddings
+ )
+ scores_local = self._relative_position_to_absolute_position(rel_logits)
+ scores = scores + scores_local
+ if self.proximal_bias:
+ assert t_s == t_t, "Proximal bias is only available for self-attention."
+ scores = scores + self._attention_bias_proximal(t_s).to(
+ device=scores.device, dtype=scores.dtype
+ )
+ if mask is not None:
+ scores = scores.masked_fill(mask == 0, -1e4)
+ if self.block_length is not None:
+ assert (
+ t_s == t_t
+ ), "Local attention is only available for self-attention."
+ block_mask = (
+ torch.ones_like(scores)
+ .triu(-self.block_length)
+ .tril(self.block_length)
+ )
+ scores = scores.masked_fill(block_mask == 0, -1e4)
+ p_attn = F.softmax(scores, dim=-1) # [b, n_h, t_t, t_s]
+ p_attn = self.drop(p_attn)
+ output = torch.matmul(p_attn, value)
+ if self.window_size is not None:
+ relative_weights = self._absolute_position_to_relative_position(p_attn)
+ value_relative_embeddings = self._get_relative_embeddings(
+ self.emb_rel_v, t_s
+ )
+ output = output + self._matmul_with_relative_values(
+ relative_weights, value_relative_embeddings
+ )
+ output = (
+ output.transpose(2, 3).contiguous().view(b, d, t_t)
+ ) # [b, n_h, t_t, d_k] -> [b, d, t_t]
+ return output, p_attn
+
+ def _matmul_with_relative_values(self, x, y):
+ """
+ x: [b, h, l, m]
+ y: [h or 1, m, d]
+ ret: [b, h, l, d]
+ """
+ ret = torch.matmul(x, y.unsqueeze(0))
+ return ret
+
+ def _matmul_with_relative_keys(self, x, y):
+ """
+ x: [b, h, l, d]
+ y: [h or 1, m, d]
+ ret: [b, h, l, m]
+ """
+ ret = torch.matmul(x, y.unsqueeze(0).transpose(-2, -1))
+ return ret
+
+ def _get_relative_embeddings(self, relative_embeddings, length):
+ max_relative_position = 2 * self.window_size + 1
+ # Pad first before slice to avoid using cond ops.
+ pad_length = max(length - (self.window_size + 1), 0)
+ slice_start_position = max((self.window_size + 1) - length, 0)
+ slice_end_position = slice_start_position + 2 * length - 1
+ if pad_length > 0:
+ padded_relative_embeddings = F.pad(
+ relative_embeddings,
+ # commons.convert_pad_shape([[0, 0], [pad_length, pad_length], [0, 0]]),
+ [0, 0, pad_length, pad_length, 0, 0],
+ )
+ else:
+ padded_relative_embeddings = relative_embeddings
+ used_relative_embeddings = padded_relative_embeddings[
+ :, slice_start_position:slice_end_position
+ ]
+ return used_relative_embeddings
+
+ def _relative_position_to_absolute_position(self, x):
+ """
+ x: [b, h, l, 2*l-1]
+ ret: [b, h, l, l]
+ """
+ batch, heads, length, _ = x.size()
+ # Concat columns of pad to shift from relative to absolute indexing.
+ x = F.pad(
+ x,
+ # commons.convert_pad_shape([[0, 0], [0, 0], [0, 0], [0, 1]])
+ [0, 1, 0, 0, 0, 0, 0, 0],
+ )
+
+ # Concat extra elements so to add up to shape (len+1, 2*len-1).
+ x_flat = x.view([batch, heads, length * 2 * length])
+ x_flat = F.pad(
+ x_flat,
+ # commons.convert_pad_shape([[0, 0], [0, 0], [0, int(length) - 1]])
+ [0, int(length) - 1, 0, 0, 0, 0],
+ )
+
+ # Reshape and slice out the padded elements.
+ x_final = x_flat.view([batch, heads, length + 1, 2 * length - 1])[
+ :, :, :length, length - 1 :
+ ]
+ return x_final
+
+ def _absolute_position_to_relative_position(self, x):
+ """
+ x: [b, h, l, l]
+ ret: [b, h, l, 2*l-1]
+ """
+ batch, heads, length, _ = x.size()
+ # padd along column
+ x = F.pad(
+ x,
+ # commons.convert_pad_shape([[0, 0], [0, 0], [0, 0], [0, int(length) - 1]])
+ [0, int(length) - 1, 0, 0, 0, 0, 0, 0],
+ )
+ x_flat = x.view([batch, heads, int(length**2) + int(length * (length - 1))])
+ # add 0's in the beginning that will skew the elements after reshape
+ x_flat = F.pad(
+ x_flat,
+ # commons.convert_pad_shape([[0, 0], [0, 0], [int(length), 0]])
+ [length, 0, 0, 0, 0, 0],
+ )
+ x_final = x_flat.view([batch, heads, length, 2 * length])[:, :, :, 1:]
+ return x_final
+
+ def _attention_bias_proximal(self, length):
+ """Bias for self-attention to encourage attention to close positions.
+ Args:
+ length: an integer scalar.
+ Returns:
+ a Tensor with shape [1, 1, length, length]
+ """
+ r = torch.arange(length, dtype=torch.float32)
+ diff = torch.unsqueeze(r, 0) - torch.unsqueeze(r, 1)
+ return torch.unsqueeze(torch.unsqueeze(-torch.log1p(torch.abs(diff)), 0), 0)
+
+
+class FFN(nn.Module):
+ def __init__(
+ self,
+ in_channels,
+ out_channels,
+ filter_channels,
+ kernel_size,
+ p_dropout=0.0,
+ activation = None,
+ causal=False,
+ ):
+ super(FFN, self).__init__()
+ self.in_channels = in_channels
+ self.out_channels = out_channels
+ self.filter_channels = filter_channels
+ self.kernel_size = kernel_size
+ self.p_dropout = p_dropout
+ self.activation = activation
+ self.causal = causal
+ self.is_activation = True if activation == "gelu" else False
+ # if causal:
+ # self.padding = self._causal_padding
+ # else:
+ # self.padding = self._same_padding
+
+ self.conv_1 = nn.Conv1d(in_channels, filter_channels, kernel_size)
+ self.conv_2 = nn.Conv1d(filter_channels, out_channels, kernel_size)
+ self.drop = nn.Dropout(p_dropout)
+
+ def padding(self, x, x_mask) :
+ if self.causal:
+ padding = self._causal_padding(x * x_mask)
+ else:
+ padding = self._same_padding(x * x_mask)
+ return padding
+
+ def forward(self, x, x_mask):
+ x = self.conv_1(self.padding(x, x_mask))
+ if self.is_activation:
+ x = x * torch.sigmoid(1.702 * x)
+ else:
+ x = torch.relu(x)
+ x = self.drop(x)
+
+ x = self.conv_2(self.padding(x, x_mask))
+ return x * x_mask
+
+ def _causal_padding(self, x):
+ if self.kernel_size == 1:
+ return x
+ pad_l = self.kernel_size - 1
+ pad_r = 0
+ # padding = [[0, 0], [0, 0], [pad_l, pad_r]]
+ x = F.pad(
+ x,
+ # commons.convert_pad_shape(padding)
+ [pad_l, pad_r, 0, 0, 0, 0],
+ )
+ return x
+
+ def _same_padding(self, x):
+ if self.kernel_size == 1:
+ return x
+ pad_l = (self.kernel_size - 1) // 2
+ pad_r = self.kernel_size // 2
+ # padding = [[0, 0], [0, 0], [pad_l, pad_r]]
+ x = F.pad(
+ x,
+ # commons.convert_pad_shape(padding)
+ [pad_l, pad_r, 0, 0, 0, 0],
+ )
+ return x
diff --git a/infer/module/commons.py b/infer/module/commons.py
new file mode 100644
index 0000000..57ab790
--- /dev/null
+++ b/infer/module/commons.py
@@ -0,0 +1,171 @@
+from typing import List, Optional
+import math
+
+import numpy as np
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+
+def init_weights(m, mean=0.0, std=0.01):
+ classname = m.__class__.__name__
+ if classname.find("Conv") != -1:
+ m.weight.data.normal_(mean, std)
+
+
+def get_padding(kernel_size, dilation=1):
+ return int((kernel_size * dilation - dilation) / 2)
+
+
+# def convert_pad_shape(pad_shape):
+# l = pad_shape[::-1]
+# pad_shape = [item for sublist in l for item in sublist]
+# return pad_shape
+
+
+def kl_divergence(m_p, logs_p, m_q, logs_q):
+ """KL(P||Q)"""
+ kl = (logs_q - logs_p) - 0.5
+ kl += (
+ 0.5 * (torch.exp(2.0 * logs_p) + ((m_p - m_q) ** 2)) * torch.exp(-2.0 * logs_q)
+ )
+ return kl
+
+
+def rand_gumbel(shape):
+ """Sample from the Gumbel distribution, protect from overflows."""
+ uniform_samples = torch.rand(shape) * 0.99998 + 0.00001
+ return -torch.log(-torch.log(uniform_samples))
+
+
+def rand_gumbel_like(x):
+ g = rand_gumbel(x.size()).to(dtype=x.dtype, device=x.device)
+ return g
+
+
+def slice_segments(x, ids_str, segment_size=4):
+ ret = torch.zeros_like(x[:, :, :segment_size])
+ for i in range(x.size(0)):
+ idx_str = ids_str[i]
+ idx_end = idx_str + segment_size
+ ret[i] = x[i, :, idx_str:idx_end]
+ return ret
+
+
+def slice_segments2(x, ids_str, segment_size=4):
+ ret = torch.zeros_like(x[:, :segment_size])
+ for i in range(x.size(0)):
+ idx_str = ids_str[i]
+ idx_end = idx_str + segment_size
+ ret[i] = x[i, idx_str:idx_end]
+ return ret
+
+
+def rand_slice_segments(x, x_lengths=None, segment_size=4):
+ b, d, t = x.size()
+ if x_lengths is None:
+ x_lengths = t
+ ids_str_max = x_lengths - segment_size + 1
+ ids_str = (torch.rand([b]).to(device=x.device) * ids_str_max).to(dtype=torch.long)
+ ret = slice_segments(x, ids_str, segment_size)
+ return ret, ids_str
+
+
+def get_timing_signal_1d(length, channels, min_timescale=1.0, max_timescale=1.0e4):
+ position = torch.arange(length, dtype=torch.float)
+ num_timescales = channels // 2
+ log_timescale_increment = math.log(float(max_timescale) / float(min_timescale)) / (
+ num_timescales - 1
+ )
+ inv_timescales = min_timescale * torch.exp(
+ torch.arange(num_timescales, dtype=torch.float) * -log_timescale_increment
+ )
+ scaled_time = position.unsqueeze(0) * inv_timescales.unsqueeze(1)
+ signal = torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], 0)
+ signal = F.pad(signal, [0, 0, 0, channels % 2])
+ signal = signal.view(1, channels, length)
+ return signal
+
+
+def add_timing_signal_1d(x, min_timescale=1.0, max_timescale=1.0e4):
+ b, channels, length = x.size()
+ signal = get_timing_signal_1d(length, channels, min_timescale, max_timescale)
+ return x + signal.to(dtype=x.dtype, device=x.device)
+
+
+def cat_timing_signal_1d(x, min_timescale=1.0, max_timescale=1.0e4, axis=1):
+ b, channels, length = x.size()
+ signal = get_timing_signal_1d(length, channels, min_timescale, max_timescale)
+ return torch.cat([x, signal.to(dtype=x.dtype, device=x.device)], axis)
+
+
+def subsequent_mask(length):
+ mask = torch.tril(torch.ones(length, length)).unsqueeze(0).unsqueeze(0)
+ return mask
+
+
+def fused_add_tanh_sigmoid_multiply(input_a, input_b, n_channels):
+ n_channels_int = n_channels[0]
+ in_act = input_a + input_b
+ t_act = torch.tanh(in_act[:, :n_channels_int, :])
+ s_act = torch.sigmoid(in_act[:, n_channels_int:, :])
+ acts = t_act * s_act
+ return acts
+
+
+# def convert_pad_shape(pad_shape):
+# l = pad_shape[::-1]
+# pad_shape = [item for sublist in l for item in sublist]
+# return pad_shape
+
+
+def convert_pad_shape(pad_shape) :
+ return torch.tensor(pad_shape).flip(0).reshape(-1).int().tolist()
+
+
+def shift_1d(x):
+ x = F.pad(x, convert_pad_shape([[0, 0], [0, 0], [1, 0]]))[:, :, :-1]
+ return x
+
+
+def sequence_mask(length, max_length = None):
+ if max_length is None:
+ max_length = length.max()
+ x = torch.arange(max_length, dtype=length.dtype, device=length.device)
+ return x.unsqueeze(0) < length.unsqueeze(1)
+
+
+def generate_path(duration, mask):
+ """
+ duration: [b, 1, t_x]
+ mask: [b, 1, t_y, t_x]
+ """
+ device = duration.device
+
+ b, _, t_y, t_x = mask.shape
+ cum_duration = torch.cumsum(duration, -1)
+
+ cum_duration_flat = cum_duration.view(b * t_x)
+ path = sequence_mask(cum_duration_flat, t_y).to(mask.dtype)
+ path = path.view(b, t_x, t_y)
+ path = path - F.pad(path, convert_pad_shape([[0, 0], [1, 0], [0, 0]]))[:, :-1]
+ path = path.unsqueeze(1).transpose(2, 3) * mask
+ return path
+
+
+def clip_grad_value_(parameters, clip_value, norm_type=2):
+ if isinstance(parameters, torch.Tensor):
+ parameters = [parameters]
+ parameters = list(filter(lambda p: p.grad is not None, parameters))
+ norm_type = float(norm_type)
+ if clip_value is not None:
+ clip_value = float(clip_value)
+
+ total_norm = 0
+ for p in parameters:
+ param_norm = p.grad.data.norm(norm_type)
+ total_norm += param_norm.item() ** norm_type
+ if clip_value is not None:
+ p.grad.data.clamp_(min=-clip_value, max=clip_value)
+ total_norm = total_norm ** (1.0 / norm_type)
+ return total_norm
diff --git a/infer/module/models.py b/infer/module/models.py
new file mode 100644
index 0000000..bb668fe
--- /dev/null
+++ b/infer/module/models.py
@@ -0,0 +1,1108 @@
+import math
+import logging
+from typing import Optional
+
+logger = logging.getLogger(__name__)
+
+import numpy as np
+import torch
+from torch import nn
+from torch.nn import AvgPool1d, Conv1d, Conv2d, ConvTranspose1d
+from torch.nn import functional as F
+from torch.nn.utils import remove_weight_norm, spectral_norm, weight_norm
+from infer.module import attentions, commons, modules
+from infer.module.commons import get_padding, init_weights
+
+class TextEncoder(nn.Module):
+ def __init__(
+ self,
+ in_channels,
+ out_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ p_dropout,
+ f0=True,
+ ):
+ super(TextEncoder, self).__init__()
+ self.out_channels = out_channels
+ self.hidden_channels = hidden_channels
+ self.filter_channels = filter_channels
+ self.n_heads = n_heads
+ self.n_layers = n_layers
+ self.kernel_size = kernel_size
+ self.p_dropout = float(p_dropout)
+ self.emb_phone = nn.Linear(in_channels, hidden_channels)
+ self.lrelu = nn.LeakyReLU(0.1, inplace=True)
+ if f0 == True:
+ self.emb_pitch = nn.Embedding(256, hidden_channels) # pitch 256
+ self.encoder = attentions.Encoder(
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ float(p_dropout),
+ )
+ self.proj = nn.Conv1d(hidden_channels, out_channels * 2, 1)
+
+ def forward(
+ self,
+ phone,
+ pitch,
+ lengths,
+ skip_head = None,
+ ):
+ if pitch is None:
+ x = self.emb_phone(phone)
+ else:
+ x = self.emb_phone(phone) + self.emb_pitch(pitch)
+ x = x * math.sqrt(self.hidden_channels) # [b, t, h]
+ x = self.lrelu(x)
+ x = torch.transpose(x, 1, -1) # [b, h, t]
+ x_mask = torch.unsqueeze(commons.sequence_mask(lengths, x.size(2)), 1).to(
+ x.dtype
+ )
+ x = self.encoder(x * x_mask, x_mask)
+ if skip_head is not None:
+ assert isinstance(skip_head, torch.Tensor)
+ head = int(skip_head.item())
+ x = x[:, :, head:]
+ x_mask = x_mask[:, :, head:]
+ stats = self.proj(x) * x_mask
+ m, logs = torch.split(stats, self.out_channels, dim=1)
+ return m, logs, x_mask
+
+
+class ResidualCouplingBlock(nn.Module):
+ def __init__(
+ self,
+ channels,
+ hidden_channels,
+ kernel_size,
+ dilation_rate,
+ n_layers,
+ n_flows=4,
+ gin_channels=0,
+ ):
+ super(ResidualCouplingBlock, self).__init__()
+ self.channels = channels
+ self.hidden_channels = hidden_channels
+ self.kernel_size = kernel_size
+ self.dilation_rate = dilation_rate
+ self.n_layers = n_layers
+ self.n_flows = n_flows
+ self.gin_channels = gin_channels
+
+ self.flows = nn.ModuleList()
+ for i in range(n_flows):
+ self.flows.append(
+ modules.ResidualCouplingLayer(
+ channels,
+ hidden_channels,
+ kernel_size,
+ dilation_rate,
+ n_layers,
+ gin_channels=gin_channels,
+ mean_only=True,
+ )
+ )
+ self.flows.append(modules.Flip())
+
+ def forward(
+ self,
+ x,
+ x_mask,
+ g = None,
+ reverse = False,
+ ):
+ if not reverse:
+ for flow in self.flows:
+ x, _ = flow(x, x_mask, g=g, reverse=reverse)
+ else:
+ for flow in self.flows[::-1]:
+ x, _ = flow.forward(x, x_mask, g=g, reverse=reverse)
+ return x
+
+ def remove_weight_norm(self):
+ for i in range(self.n_flows):
+ self.flows[i * 2].remove_weight_norm()
+
+class PosteriorEncoder(nn.Module):
+ def __init__(
+ self,
+ in_channels,
+ out_channels,
+ hidden_channels,
+ kernel_size,
+ dilation_rate,
+ n_layers,
+ gin_channels=0,
+ ):
+ super(PosteriorEncoder, self).__init__()
+ self.in_channels = in_channels
+ self.out_channels = out_channels
+ self.hidden_channels = hidden_channels
+ self.kernel_size = kernel_size
+ self.dilation_rate = dilation_rate
+ self.n_layers = n_layers
+ self.gin_channels = gin_channels
+
+ self.pre = nn.Conv1d(in_channels, hidden_channels, 1)
+ self.enc = modules.WN(
+ hidden_channels,
+ kernel_size,
+ dilation_rate,
+ n_layers,
+ gin_channels=gin_channels,
+ )
+ self.proj = nn.Conv1d(hidden_channels, out_channels * 2, 1)
+
+ def forward(
+ self, x, x_lengths, g = None
+ ):
+ x_mask = torch.unsqueeze(commons.sequence_mask(x_lengths, x.size(2)), 1).to(
+ x.dtype
+ )
+ x = self.pre(x) * x_mask
+ x = self.enc(x, x_mask, g=g)
+ stats = self.proj(x) * x_mask
+ m, logs = torch.split(stats, self.out_channels, dim=1)
+ z = (m + torch.randn_like(m) * torch.exp(logs)) * x_mask
+ return z, m, logs, x_mask
+
+ def remove_weight_norm(self):
+ self.enc.remove_weight_norm()
+
+class Generator(torch.nn.Module):
+ def __init__(
+ self,
+ initial_channel,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ gin_channels=0,
+ ):
+ super(Generator, self).__init__()
+ self.num_kernels = len(resblock_kernel_sizes)
+ self.num_upsamples = len(upsample_rates)
+ self.conv_pre = Conv1d(
+ initial_channel, upsample_initial_channel, 7, 1, padding=3
+ )
+ resblock = modules.ResBlock1 if resblock == "1" else modules.ResBlock2
+
+ self.ups = nn.ModuleList()
+ for i, (u, k) in enumerate(zip(upsample_rates, upsample_kernel_sizes)):
+ self.ups.append(
+ weight_norm(
+ ConvTranspose1d(
+ upsample_initial_channel // (2**i),
+ upsample_initial_channel // (2 ** (i + 1)),
+ k,
+ u,
+ padding=(k - u) // 2,
+ )
+ )
+ )
+
+ self.resblocks = nn.ModuleList()
+ for i in range(len(self.ups)):
+ ch = upsample_initial_channel // (2 ** (i + 1))
+ for j, (k, d) in enumerate(
+ zip(resblock_kernel_sizes, resblock_dilation_sizes)
+ ):
+ self.resblocks.append(resblock(ch, k, d))
+
+ self.conv_post = Conv1d(ch, 1, 7, 1, padding=3, bias=False)
+ self.ups.apply(init_weights)
+
+ if gin_channels != 0:
+ self.cond = nn.Conv1d(gin_channels, upsample_initial_channel, 1)
+
+ def forward(
+ self,
+ x,
+ g = None,
+ n_res = None,
+ ):
+ if n_res is not None:
+ assert isinstance(n_res, torch.Tensor)
+ n = int(n_res.item())
+ if n != x.shape[-1]:
+ x = F.interpolate(x, size=n, mode="linear")
+ x = self.conv_pre(x)
+ if g is not None:
+ x = x + self.cond(g)
+
+ for i in range(self.num_upsamples):
+ x = F.leaky_relu(x, modules.LRELU_SLOPE)
+ x = self.ups[i](x)
+ xs = None
+ for j in range(self.num_kernels):
+ if xs is None:
+ xs = self.resblocks[i * self.num_kernels + j](x)
+ else:
+ xs += self.resblocks[i * self.num_kernels + j](x)
+ x = xs / self.num_kernels
+ x = F.leaky_relu(x)
+ x = self.conv_post(x)
+ x = torch.tanh(x)
+
+ return x
+
+ def remove_weight_norm(self):
+ for l in self.ups:
+ remove_weight_norm(l)
+ for l in self.resblocks:
+ l.remove_weight_norm()
+
+
+class SineGen(torch.nn.Module):
+ """Definition of sine generator
+ SineGen(samp_rate, harmonic_num = 0,
+ sine_amp = 0.1, noise_std = 0.003,
+ voiced_threshold = 0,
+ flag_for_pulse=False)
+ samp_rate: sampling rate in Hz
+ harmonic_num: number of harmonic overtones (default 0)
+ sine_amp: amplitude of sine-wavefrom (default 0.1)
+ noise_std: std of Gaussian noise (default 0.003)
+ voiced_thoreshold: F0 threshold for U/V classification (default 0)
+ flag_for_pulse: this SinGen is used inside PulseGen (default False)
+ Note: when flag_for_pulse is True, the first time step of a voiced
+ segment is always sin(torch.pi) or cos(0)
+ """
+
+ def __init__(
+ self,
+ samp_rate,
+ harmonic_num=0,
+ sine_amp=0.1,
+ noise_std=0.003,
+ voiced_threshold=0,
+ flag_for_pulse=False,
+ ):
+ super(SineGen, self).__init__()
+ self.sine_amp = sine_amp
+ self.noise_std = noise_std
+ self.harmonic_num = harmonic_num
+ self.dim = self.harmonic_num + 1
+ self.sampling_rate = samp_rate
+ self.voiced_threshold = voiced_threshold
+
+ def _f02uv(self, f0):
+ # generate uv signal
+ uv = torch.ones_like(f0)
+ uv = uv * (f0 > self.voiced_threshold)
+ if uv.device.type == "privateuseone": # for DirectML
+ uv = uv.float()
+ return uv
+
+ def forward(self, f0, upp):
+ """sine_tensor, uv = forward(f0)
+ input F0: tensor(batchsize=1, length, dim=1)
+ f0 for unvoiced steps should be 0
+ output sine_tensor: tensor(batchsize=1, length, dim)
+ output uv: tensor(batchsize=1, length, 1)
+ """
+ with torch.no_grad():
+ f0 = f0[:, None].transpose(1, 2)
+ f0_buf = torch.zeros(f0.shape[0], f0.shape[1], self.dim, device=f0.device)
+ # fundamental component
+ f0_buf[:, :, 0] = f0[:, :, 0]
+ for idx in range(self.harmonic_num):
+ f0_buf[:, :, idx + 1] = f0_buf[:, :, 0] * (
+ idx + 2
+ ) # idx + 2: the (idx+1)-th overtone, (idx+2)-th harmonic
+ rad_values = (
+ f0_buf / self.sampling_rate
+ ) % 1 ###%1意味着n_har的乘积无法后处理优化
+ rand_ini = torch.rand(
+ f0_buf.shape[0], f0_buf.shape[2], device=f0_buf.device
+ )
+ rand_ini[:, 0] = 0
+ rad_values[:, 0, :] = rad_values[:, 0, :] + rand_ini
+ tmp_over_one = torch.cumsum(
+ rad_values, 1
+ ) # % 1 #####%1意味着后面的cumsum无法再优化
+ tmp_over_one *= upp
+ tmp_over_one = F.interpolate(
+ tmp_over_one.transpose(2, 1),
+ scale_factor=float(upp),
+ mode="linear",
+ align_corners=True,
+ ).transpose(2, 1)
+ rad_values = F.interpolate(
+ rad_values.transpose(2, 1), scale_factor=float(upp), mode="nearest"
+ ).transpose(
+ 2, 1
+ ) #######
+ tmp_over_one %= 1
+ tmp_over_one_idx = (tmp_over_one[:, 1:, :] - tmp_over_one[:, :-1, :]) < 0
+ cumsum_shift = torch.zeros_like(rad_values)
+ cumsum_shift[:, 1:, :] = tmp_over_one_idx * -1.0
+ sine_waves = torch.sin(
+ torch.cumsum(rad_values + cumsum_shift, dim=1) * 2 * torch.pi
+ )
+ sine_waves = sine_waves * self.sine_amp
+ uv = self._f02uv(f0)
+ uv = F.interpolate(
+ uv.transpose(2, 1), scale_factor=float(upp), mode="nearest"
+ ).transpose(2, 1)
+ noise_amp = uv * self.noise_std + (1 - uv) * self.sine_amp / 3
+ noise = noise_amp * torch.randn_like(sine_waves)
+ sine_waves = sine_waves * uv + noise
+ return sine_waves, uv, noise
+
+
+class SourceModuleHnNSF(torch.nn.Module):
+ """SourceModule for hn-nsf
+ SourceModule(sampling_rate, harmonic_num=0, sine_amp=0.1,
+ add_noise_std=0.003, voiced_threshod=0)
+ sampling_rate: sampling_rate in Hz
+ harmonic_num: number of harmonic above F0 (default: 0)
+ sine_amp: amplitude of sine source signal (default: 0.1)
+ add_noise_std: std of additive Gaussian noise (default: 0.003)
+ note that amplitude of noise in unvoiced is decided
+ by sine_amp
+ voiced_threshold: threhold to set U/V given F0 (default: 0)
+ Sine_source, noise_source = SourceModuleHnNSF(F0_sampled)
+ F0_sampled (batchsize, length, 1)
+ Sine_source (batchsize, length, 1)
+ noise_source (batchsize, length 1)
+ uv (batchsize, length, 1)
+ """
+
+ def __init__(
+ self,
+ sampling_rate,
+ harmonic_num=0,
+ sine_amp=0.1,
+ add_noise_std=0.003,
+ voiced_threshod=0,
+ is_half=True,
+ ):
+ super(SourceModuleHnNSF, self).__init__()
+
+ self.sine_amp = sine_amp
+ self.noise_std = add_noise_std
+ self.is_half = is_half
+ # to produce sine waveforms
+ self.l_sin_gen = SineGen(
+ sampling_rate, harmonic_num, sine_amp, add_noise_std, voiced_threshod
+ )
+
+ # to merge source harmonics into a single excitation
+ self.l_linear = torch.nn.Linear(harmonic_num + 1, 1)
+ self.l_tanh = torch.nn.Tanh()
+ # self.ddtype:int = -1
+
+ def forward(self, x, upp = 1):
+ # if self.ddtype ==-1:
+ # self.ddtype = self.l_linear.weight.dtype
+ sine_wavs, uv, _ = self.l_sin_gen(x, upp)
+ # print(x.dtype,sine_wavs.dtype,self.l_linear.weight.dtype)
+ # if self.is_half:
+ # sine_wavs = sine_wavs.half()
+ # sine_merge = self.l_tanh(self.l_linear(sine_wavs.to(x)))
+ # print(sine_wavs.dtype,self.ddtype)
+ # if sine_wavs.dtype != self.l_linear.weight.dtype:
+ sine_wavs = sine_wavs.to(dtype=self.l_linear.weight.dtype)
+ sine_merge = self.l_tanh(self.l_linear(sine_wavs))
+ return sine_merge, None, None # noise, uv
+
+
+class GeneratorNSF(torch.nn.Module):
+ def __init__(
+ self,
+ initial_channel,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ gin_channels,
+ sr,
+ is_half=False,
+ ):
+ super(GeneratorNSF, self).__init__()
+ self.num_kernels = len(resblock_kernel_sizes)
+ self.num_upsamples = len(upsample_rates)
+
+ self.f0_upsamp = torch.nn.Upsample(scale_factor=math.prod(upsample_rates))
+ self.m_source = SourceModuleHnNSF(
+ sampling_rate=sr, harmonic_num=0, is_half=is_half
+ )
+ self.noise_convs = nn.ModuleList()
+ self.conv_pre = Conv1d(
+ initial_channel, upsample_initial_channel, 7, 1, padding=3
+ )
+ resblock = modules.ResBlock1 if resblock == "1" else modules.ResBlock2
+
+ self.ups = nn.ModuleList()
+ for i, (u, k) in enumerate(zip(upsample_rates, upsample_kernel_sizes)):
+ c_cur = upsample_initial_channel // (2 ** (i + 1))
+ self.ups.append(
+ weight_norm(
+ ConvTranspose1d(
+ upsample_initial_channel // (2**i),
+ upsample_initial_channel // (2 ** (i + 1)),
+ k,
+ u,
+ padding=(k - u) // 2,
+ )
+ )
+ )
+ if i + 1 < len(upsample_rates):
+ stride_f0 = math.prod(upsample_rates[i + 1 :])
+ self.noise_convs.append(
+ Conv1d(
+ 1,
+ c_cur,
+ kernel_size=stride_f0 * 2,
+ stride=stride_f0,
+ padding=stride_f0 // 2,
+ )
+ )
+ else:
+ self.noise_convs.append(Conv1d(1, c_cur, kernel_size=1))
+
+ self.resblocks = nn.ModuleList()
+ for i in range(len(self.ups)):
+ ch = upsample_initial_channel // (2 ** (i + 1))
+ for j, (k, d) in enumerate(
+ zip(resblock_kernel_sizes, resblock_dilation_sizes)
+ ):
+ self.resblocks.append(resblock(ch, k, d))
+
+ self.conv_post = Conv1d(ch, 1, 7, 1, padding=3, bias=False)
+ self.ups.apply(init_weights)
+
+ if gin_channels != 0:
+ self.cond = nn.Conv1d(gin_channels, upsample_initial_channel, 1)
+
+ self.upp = math.prod(upsample_rates)
+
+ self.lrelu_slope = modules.LRELU_SLOPE
+
+ def forward(
+ self,
+ x,
+ f0,
+ g = None,
+ n_res = None,
+ ):
+ har_source, noi_source, uv = self.m_source(f0, self.upp)
+ har_source = har_source.transpose(1, 2)
+ if n_res is not None:
+ assert isinstance(n_res, torch.Tensor)
+ n = int(n_res.item())
+ if n * self.upp != har_source.shape[-1]:
+ har_source = F.interpolate(har_source, size=n * self.upp, mode="linear")
+ if n != x.shape[-1]:
+ x = F.interpolate(x, size=n, mode="linear")
+ x = self.conv_pre(x)
+ if g is not None:
+ x = x + self.cond(g)
+ for i, (ups, noise_convs) in enumerate(zip(self.ups, self.noise_convs)):
+ if i < self.num_upsamples:
+ x = F.leaky_relu(x, self.lrelu_slope)
+ x = ups(x)
+ x_source = noise_convs(har_source)
+ x = x + x_source
+ xs = None
+ l = [i * self.num_kernels + j for j in range(self.num_kernels)]
+ for j, resblock in enumerate(self.resblocks):
+ if j in l:
+ if xs is None:
+ xs = resblock(x)
+ else:
+ xs += resblock(x)
+ assert isinstance(xs, torch.Tensor)
+ x = xs / self.num_kernels
+ x = F.leaky_relu(x)
+ x = self.conv_post(x)
+ x = torch.tanh(x)
+
+ return x
+
+ def remove_weight_norm(self):
+ for l in self.ups:
+ remove_weight_norm(l)
+ for l in self.resblocks:
+ l.remove_weight_norm()
+
+sr2sr = {
+ "32k": 32000,
+ "40k": 40000,
+ "48k": 48000,
+}
+
+
+class SynthesizerTrnMs256NSFsid(nn.Module):
+ def __init__(
+ self,
+ spec_channels,
+ segment_size,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ p_dropout,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ spk_embed_dim,
+ gin_channels,
+ sr,
+ **kwargs
+ ):
+ super(SynthesizerTrnMs256NSFsid, self).__init__()
+ if isinstance(sr, str):
+ sr = sr2sr[sr]
+ self.spec_channels = spec_channels
+ self.inter_channels = inter_channels
+ self.hidden_channels = hidden_channels
+ self.filter_channels = filter_channels
+ self.n_heads = n_heads
+ self.n_layers = n_layers
+ self.kernel_size = kernel_size
+ self.p_dropout = float(p_dropout)
+ self.resblock = resblock
+ self.resblock_kernel_sizes = resblock_kernel_sizes
+ self.resblock_dilation_sizes = resblock_dilation_sizes
+ self.upsample_rates = upsample_rates
+ self.upsample_initial_channel = upsample_initial_channel
+ self.upsample_kernel_sizes = upsample_kernel_sizes
+ self.segment_size = segment_size
+ self.gin_channels = gin_channels
+ # self.hop_length = hop_length#
+ self.spk_embed_dim = spk_embed_dim
+ self.enc_p = TextEncoder(
+ 256,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ float(p_dropout),
+ )
+ self.dec = GeneratorNSF(
+ inter_channels,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ gin_channels=gin_channels,
+ sr=sr,
+ is_half=kwargs["is_half"],
+ )
+ self.enc_q = PosteriorEncoder(
+ spec_channels,
+ inter_channels,
+ hidden_channels,
+ 5,
+ 1,
+ 16,
+ gin_channels=gin_channels,
+ )
+ self.flow = ResidualCouplingBlock(
+ inter_channels, hidden_channels, 5, 1, 3, gin_channels=gin_channels
+ )
+ self.emb_g = nn.Embedding(self.spk_embed_dim, gin_channels)
+ logger.debug(
+ "gin_channels: "
+ + str(gin_channels)
+ + ", self.spk_embed_dim: "
+ + str(self.spk_embed_dim)
+ )
+
+ def remove_weight_norm(self):
+ self.dec.remove_weight_norm()
+ self.flow.remove_weight_norm()
+ if hasattr(self, "enc_q"):
+ self.enc_q.remove_weight_norm()
+
+ def forward(
+ self,
+ phone,
+ phone_lengths,
+ pitch,
+ pitchf,
+ y,
+ y_lengths,
+ ds = None,
+ ): # 这里ds是id,[bs,1]
+ # print(1,pitch.shape)#[bs,t]
+ g = self.emb_g(ds).unsqueeze(-1) # [b, 256, 1]##1是t,广播的
+ m_p, logs_p, x_mask = self.enc_p(phone, pitch, phone_lengths)
+ z, m_q, logs_q, y_mask = self.enc_q(y, y_lengths, g=g)
+ z_p = self.flow(z, y_mask, g=g)
+ z_slice, ids_slice = commons.rand_slice_segments(
+ z, y_lengths, self.segment_size
+ )
+ # print(-1,pitchf.shape,ids_slice,self.segment_size,self.hop_length,self.segment_size//self.hop_length)
+ pitchf = commons.slice_segments2(pitchf, ids_slice, self.segment_size)
+ # print(-2,pitchf.shape,z_slice.shape)
+ o = self.dec(z_slice, pitchf, g=g)
+ return o, ids_slice, x_mask, y_mask, (z, z_p, m_p, logs_p, m_q, logs_q)
+
+ def infer(
+ self,
+ phone,
+ phone_lengths,
+ pitch,
+ nsff0,
+ sid,
+ skip_head = None,
+ return_length = None,
+ return_length2 = None,
+ ):
+ g = self.emb_g(sid).unsqueeze(-1)
+ if skip_head is not None and return_length is not None:
+ assert isinstance(skip_head, torch.Tensor)
+ assert isinstance(return_length, torch.Tensor)
+ head = int(skip_head.item())
+ length = int(return_length.item())
+ flow_head = torch.clamp(skip_head - 24, min=0)
+ dec_head = head - int(flow_head.item())
+ m_p, logs_p, x_mask = self.enc_p(phone, pitch, phone_lengths, flow_head)
+ z_p = (m_p + torch.exp(logs_p) * torch.randn_like(m_p) * 0.66666) * x_mask
+ z = self.flow(z_p, x_mask, g=g, reverse=True)
+ z = z[:, :, dec_head : dec_head + length]
+ x_mask = x_mask[:, :, dec_head : dec_head + length]
+ nsff0 = nsff0[:, head : head + length]
+ else:
+ m_p, logs_p, x_mask = self.enc_p(phone, pitch, phone_lengths)
+ z_p = (m_p + torch.exp(logs_p) * torch.randn_like(m_p) * 0.66666) * x_mask
+ z = self.flow(z_p, x_mask, g=g, reverse=True)
+ o = self.dec(z * x_mask, nsff0, g=g, n_res=return_length2)
+ return o, x_mask, (z, z_p, m_p, logs_p)
+
+
+class SynthesizerTrnMs768NSFsid(SynthesizerTrnMs256NSFsid):
+ def __init__(
+ self,
+ spec_channels,
+ segment_size,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ p_dropout,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ spk_embed_dim,
+ gin_channels,
+ sr,
+ **kwargs
+ ):
+ super(SynthesizerTrnMs768NSFsid, self).__init__(
+ spec_channels,
+ segment_size,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ p_dropout,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ spk_embed_dim,
+ gin_channels,
+ sr,
+ **kwargs
+ )
+ del self.enc_p
+ self.enc_p = TextEncoder(
+ 768,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ float(p_dropout),
+ )
+
+
+class SynthesizerTrnMs256NSFsid_nono(nn.Module):
+ def __init__(
+ self,
+ spec_channels,
+ segment_size,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ p_dropout,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ spk_embed_dim,
+ gin_channels,
+ sr=None,
+ **kwargs
+ ):
+ super(SynthesizerTrnMs256NSFsid_nono, self).__init__()
+ self.spec_channels = spec_channels
+ self.inter_channels = inter_channels
+ self.hidden_channels = hidden_channels
+ self.filter_channels = filter_channels
+ self.n_heads = n_heads
+ self.n_layers = n_layers
+ self.kernel_size = kernel_size
+ self.p_dropout = float(p_dropout)
+ self.resblock = resblock
+ self.resblock_kernel_sizes = resblock_kernel_sizes
+ self.resblock_dilation_sizes = resblock_dilation_sizes
+ self.upsample_rates = upsample_rates
+ self.upsample_initial_channel = upsample_initial_channel
+ self.upsample_kernel_sizes = upsample_kernel_sizes
+ self.segment_size = segment_size
+ self.gin_channels = gin_channels
+ # self.hop_length = hop_length#
+ self.spk_embed_dim = spk_embed_dim
+ self.enc_p = TextEncoder(
+ 256,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ float(p_dropout),
+ f0=False,
+ )
+ self.dec = Generator(
+ inter_channels,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ gin_channels=gin_channels,
+ )
+ self.enc_q = PosteriorEncoder(
+ spec_channels,
+ inter_channels,
+ hidden_channels,
+ 5,
+ 1,
+ 16,
+ gin_channels=gin_channels,
+ )
+ self.flow = ResidualCouplingBlock(
+ inter_channels, hidden_channels, 5, 1, 3, gin_channels=gin_channels
+ )
+ self.emb_g = nn.Embedding(self.spk_embed_dim, gin_channels)
+ logger.debug(
+ "gin_channels: "
+ + str(gin_channels)
+ + ", self.spk_embed_dim: "
+ + str(self.spk_embed_dim)
+ )
+
+ def remove_weight_norm(self):
+ self.dec.remove_weight_norm()
+ self.flow.remove_weight_norm()
+ if hasattr(self, "enc_q"):
+ self.enc_q.remove_weight_norm()
+
+ def forward(self, phone, phone_lengths, y, y_lengths, ds): # 这里ds是id,[bs,1]
+ g = self.emb_g(ds).unsqueeze(-1) # [b, 256, 1]##1是t,广播的
+ m_p, logs_p, x_mask = self.enc_p(phone, None, phone_lengths)
+ z, m_q, logs_q, y_mask = self.enc_q(y, y_lengths, g=g)
+ z_p = self.flow(z, y_mask, g=g)
+ z_slice, ids_slice = commons.rand_slice_segments(
+ z, y_lengths, self.segment_size
+ )
+ o = self.dec(z_slice, g=g)
+ return o, ids_slice, x_mask, y_mask, (z, z_p, m_p, logs_p, m_q, logs_q)
+
+ def infer(
+ self,
+ phone,
+ phone_lengths,
+ sid,
+ skip_head = None,
+ return_length = None,
+ return_length2 = None,
+ ):
+ g = self.emb_g(sid).unsqueeze(-1)
+ if skip_head is not None and return_length is not None:
+ assert isinstance(skip_head, torch.Tensor)
+ assert isinstance(return_length, torch.Tensor)
+ head = int(skip_head.item())
+ length = int(return_length.item())
+ flow_head = torch.clamp(skip_head - 24, min=0)
+ dec_head = head - int(flow_head.item())
+ m_p, logs_p, x_mask = self.enc_p(phone, None, phone_lengths, flow_head)
+ z_p = (m_p + torch.exp(logs_p) * torch.randn_like(m_p) * 0.66666) * x_mask
+ z = self.flow(z_p, x_mask, g=g, reverse=True)
+ z = z[:, :, dec_head : dec_head + length]
+ x_mask = x_mask[:, :, dec_head : dec_head + length]
+ else:
+ m_p, logs_p, x_mask = self.enc_p(phone, None, phone_lengths)
+ z_p = (m_p + torch.exp(logs_p) * torch.randn_like(m_p) * 0.66666) * x_mask
+ z = self.flow(z_p, x_mask, g=g, reverse=True)
+ o = self.dec(z * x_mask, g=g, n_res=return_length2)
+ return o, x_mask, (z, z_p, m_p, logs_p)
+
+
+class SynthesizerTrnMs768NSFsid_nono(SynthesizerTrnMs256NSFsid_nono):
+ def __init__(
+ self,
+ spec_channels,
+ segment_size,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ p_dropout,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ spk_embed_dim,
+ gin_channels,
+ sr=None,
+ **kwargs
+ ):
+ super(SynthesizerTrnMs768NSFsid_nono, self).__init__(
+ spec_channels,
+ segment_size,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ p_dropout,
+ resblock,
+ resblock_kernel_sizes,
+ resblock_dilation_sizes,
+ upsample_rates,
+ upsample_initial_channel,
+ upsample_kernel_sizes,
+ spk_embed_dim,
+ gin_channels,
+ sr,
+ **kwargs
+ )
+ del self.enc_p
+ self.enc_p = TextEncoder(
+ 768,
+ inter_channels,
+ hidden_channels,
+ filter_channels,
+ n_heads,
+ n_layers,
+ kernel_size,
+ float(p_dropout),
+ f0=False,
+ )
+
+
+class MultiPeriodDiscriminator(torch.nn.Module):
+ def __init__(self, use_spectral_norm=False):
+ super(MultiPeriodDiscriminator, self).__init__()
+ periods = [2, 3, 5, 7, 11, 17]
+ # periods = [3, 5, 7, 11, 17, 23, 37]
+
+ discs = [DiscriminatorS(use_spectral_norm=use_spectral_norm)]
+ discs = discs + [
+ DiscriminatorP(i, use_spectral_norm=use_spectral_norm) for i in periods
+ ]
+ self.discriminators = nn.ModuleList(discs)
+
+ def forward(self, y, y_hat):
+ y_d_rs = [] #
+ y_d_gs = []
+ fmap_rs = []
+ fmap_gs = []
+ for i, d in enumerate(self.discriminators):
+ y_d_r, fmap_r = d(y)
+ y_d_g, fmap_g = d(y_hat)
+ # for j in range(len(fmap_r)):
+ # print(i,j,y.shape,y_hat.shape,fmap_r[j].shape,fmap_g[j].shape)
+ y_d_rs.append(y_d_r)
+ y_d_gs.append(y_d_g)
+ fmap_rs.append(fmap_r)
+ fmap_gs.append(fmap_g)
+
+ return y_d_rs, y_d_gs, fmap_rs, fmap_gs
+
+
+class MultiPeriodDiscriminatorV2(torch.nn.Module):
+ def __init__(self, use_spectral_norm=False):
+ super(MultiPeriodDiscriminatorV2, self).__init__()
+ # periods = [2, 3, 5, 7, 11, 17]
+ periods = [2, 3, 5, 7, 11, 17, 23, 37]
+
+ discs = [DiscriminatorS(use_spectral_norm=use_spectral_norm)]
+ discs = discs + [
+ DiscriminatorP(i, use_spectral_norm=use_spectral_norm) for i in periods
+ ]
+ self.discriminators = nn.ModuleList(discs)
+
+ def forward(self, y, y_hat):
+ y_d_rs = [] #
+ y_d_gs = []
+ fmap_rs = []
+ fmap_gs = []
+ for i, d in enumerate(self.discriminators):
+ y_d_r, fmap_r = d(y)
+ y_d_g, fmap_g = d(y_hat)
+ # for j in range(len(fmap_r)):
+ # print(i,j,y.shape,y_hat.shape,fmap_r[j].shape,fmap_g[j].shape)
+ y_d_rs.append(y_d_r)
+ y_d_gs.append(y_d_g)
+ fmap_rs.append(fmap_r)
+ fmap_gs.append(fmap_g)
+
+ return y_d_rs, y_d_gs, fmap_rs, fmap_gs
+
+
+class DiscriminatorS(torch.nn.Module):
+ def __init__(self, use_spectral_norm=False):
+ super(DiscriminatorS, self).__init__()
+ norm_f = weight_norm if use_spectral_norm == False else spectral_norm
+ self.convs = nn.ModuleList(
+ [
+ norm_f(Conv1d(1, 16, 15, 1, padding=7)),
+ norm_f(Conv1d(16, 64, 41, 4, groups=4, padding=20)),
+ norm_f(Conv1d(64, 256, 41, 4, groups=16, padding=20)),
+ norm_f(Conv1d(256, 1024, 41, 4, groups=64, padding=20)),
+ norm_f(Conv1d(1024, 1024, 41, 4, groups=256, padding=20)),
+ norm_f(Conv1d(1024, 1024, 5, 1, padding=2)),
+ ]
+ )
+ self.conv_post = norm_f(Conv1d(1024, 1, 3, 1, padding=1))
+
+ def forward(self, x):
+ fmap = []
+
+ for l in self.convs:
+ x = l(x)
+ x = F.leaky_relu(x, modules.LRELU_SLOPE)
+ fmap.append(x)
+ x = self.conv_post(x)
+ fmap.append(x)
+ x = torch.flatten(x, 1, -1)
+
+ return x, fmap
+
+
+class DiscriminatorP(torch.nn.Module):
+ def __init__(self, period, kernel_size=5, stride=3, use_spectral_norm=False):
+ super(DiscriminatorP, self).__init__()
+ self.period = period
+ self.use_spectral_norm = use_spectral_norm
+ norm_f = weight_norm if use_spectral_norm == False else spectral_norm
+ self.convs = nn.ModuleList(
+ [
+ norm_f(
+ Conv2d(
+ 1,
+ 32,
+ (kernel_size, 1),
+ (stride, 1),
+ padding=(get_padding(kernel_size, 1), 0),
+ )
+ ),
+ norm_f(
+ Conv2d(
+ 32,
+ 128,
+ (kernel_size, 1),
+ (stride, 1),
+ padding=(get_padding(kernel_size, 1), 0),
+ )
+ ),
+ norm_f(
+ Conv2d(
+ 128,
+ 512,
+ (kernel_size, 1),
+ (stride, 1),
+ padding=(get_padding(kernel_size, 1), 0),
+ )
+ ),
+ norm_f(
+ Conv2d(
+ 512,
+ 1024,
+ (kernel_size, 1),
+ (stride, 1),
+ padding=(get_padding(kernel_size, 1), 0),
+ )
+ ),
+ norm_f(
+ Conv2d(
+ 1024,
+ 1024,
+ (kernel_size, 1),
+ 1,
+ padding=(get_padding(kernel_size, 1), 0),
+ )
+ ),
+ ]
+ )
+ self.conv_post = norm_f(Conv2d(1024, 1, (3, 1), 1, padding=(1, 0)))
+
+ def forward(self, x):
+ fmap = []
+
+ # 1d to 2d
+ b, c, t = x.shape
+ if t % self.period != 0: # pad first
+ n_pad = self.period - (t % self.period)
+ x = F.pad(x, (0, n_pad), "reflect")
+ t = t + n_pad
+ x = x.view(b, c, t // self.period, self.period)
+
+ for l in self.convs:
+ x = l(x)
+ x = F.leaky_relu(x, modules.LRELU_SLOPE)
+ fmap.append(x)
+ x = self.conv_post(x)
+ fmap.append(x)
+ x = torch.flatten(x, 1, -1)
+
+ return x, fmap
diff --git a/infer/module/modules.py b/infer/module/modules.py
new file mode 100644
index 0000000..df73687
--- /dev/null
+++ b/infer/module/modules.py
@@ -0,0 +1,548 @@
+import copy
+import math
+from typing import Optional, Tuple
+
+import numpy as np
+import scipy
+import torch
+from torch import nn
+from torch.nn import AvgPool1d, Conv1d, Conv2d, ConvTranspose1d
+from torch.nn import functional as F
+from torch.nn.utils import remove_weight_norm, weight_norm
+
+from infer.module import commons
+from infer.module.commons import get_padding, init_weights
+from infer.module.transforms import piecewise_rational_quadratic_transform
+
+LRELU_SLOPE = 0.1
+
+
+class LayerNorm(nn.Module):
+ def __init__(self, channels, eps=1e-5):
+ super(LayerNorm, self).__init__()
+ self.channels = channels
+ self.eps = eps
+
+ self.gamma = nn.Parameter(torch.ones(channels))
+ self.beta = nn.Parameter(torch.zeros(channels))
+
+ def forward(self, x):
+ x = x.transpose(1, -1)
+ x = F.layer_norm(x, (self.channels,), self.gamma, self.beta, self.eps)
+ return x.transpose(1, -1)
+
+
+class ConvReluNorm(nn.Module):
+ def __init__(
+ self,
+ in_channels,
+ hidden_channels,
+ out_channels,
+ kernel_size,
+ n_layers,
+ p_dropout,
+ ):
+ super(ConvReluNorm, self).__init__()
+ self.in_channels = in_channels
+ self.hidden_channels = hidden_channels
+ self.out_channels = out_channels
+ self.kernel_size = kernel_size
+ self.n_layers = n_layers
+ self.p_dropout = float(p_dropout)
+ assert n_layers > 1, "Number of layers should be larger than 0."
+
+ self.conv_layers = nn.ModuleList()
+ self.norm_layers = nn.ModuleList()
+ self.conv_layers.append(
+ nn.Conv1d(
+ in_channels, hidden_channels, kernel_size, padding=kernel_size // 2
+ )
+ )
+ self.norm_layers.append(LayerNorm(hidden_channels))
+ self.relu_drop = nn.Sequential(nn.ReLU(), nn.Dropout(float(p_dropout)))
+ for _ in range(n_layers - 1):
+ self.conv_layers.append(
+ nn.Conv1d(
+ hidden_channels,
+ hidden_channels,
+ kernel_size,
+ padding=kernel_size // 2,
+ )
+ )
+ self.norm_layers.append(LayerNorm(hidden_channels))
+ self.proj = nn.Conv1d(hidden_channels, out_channels, 1)
+ self.proj.weight.data.zero_()
+ self.proj.bias.data.zero_()
+
+ def forward(self, x, x_mask):
+ x_org = x
+ for i in range(self.n_layers):
+ x = self.conv_layers[i](x * x_mask)
+ x = self.norm_layers[i](x)
+ x = self.relu_drop(x)
+ x = x_org + self.proj(x)
+ return x * x_mask
+
+
+class DDSConv(nn.Module):
+ """
+ Dialted and Depth-Separable Convolution
+ """
+
+ def __init__(self, channels, kernel_size, n_layers, p_dropout=0.0):
+ super(DDSConv, self).__init__()
+ self.channels = channels
+ self.kernel_size = kernel_size
+ self.n_layers = n_layers
+ self.p_dropout = float(p_dropout)
+
+ self.drop = nn.Dropout(float(p_dropout))
+ self.convs_sep = nn.ModuleList()
+ self.convs_1x1 = nn.ModuleList()
+ self.norms_1 = nn.ModuleList()
+ self.norms_2 = nn.ModuleList()
+ for i in range(n_layers):
+ dilation = kernel_size**i
+ padding = (kernel_size * dilation - dilation) // 2
+ self.convs_sep.append(
+ nn.Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ groups=channels,
+ dilation=dilation,
+ padding=padding,
+ )
+ )
+ self.convs_1x1.append(nn.Conv1d(channels, channels, 1))
+ self.norms_1.append(LayerNorm(channels))
+ self.norms_2.append(LayerNorm(channels))
+
+ def forward(self, x, x_mask, g = None):
+ if g is not None:
+ x = x + g
+ for i in range(self.n_layers):
+ y = self.convs_sep[i](x * x_mask)
+ y = self.norms_1[i](y)
+ y = F.gelu(y)
+ y = self.convs_1x1[i](y)
+ y = self.norms_2[i](y)
+ y = F.gelu(y)
+ y = self.drop(y)
+ x = x + y
+ return x * x_mask
+
+
+class WN(torch.nn.Module):
+ def __init__(
+ self,
+ hidden_channels,
+ kernel_size,
+ dilation_rate,
+ n_layers,
+ gin_channels=0,
+ p_dropout=0,
+ ):
+ super(WN, self).__init__()
+ assert kernel_size % 2 == 1
+ self.hidden_channels = hidden_channels
+ self.kernel_size = (kernel_size,)
+ self.dilation_rate = dilation_rate
+ self.n_layers = n_layers
+ self.gin_channels = gin_channels
+ self.p_dropout = float(p_dropout)
+
+ self.in_layers = torch.nn.ModuleList()
+ self.res_skip_layers = torch.nn.ModuleList()
+ self.drop = nn.Dropout(float(p_dropout))
+
+ if gin_channels != 0:
+ cond_layer = torch.nn.Conv1d(
+ gin_channels, 2 * hidden_channels * n_layers, 1
+ )
+ self.cond_layer = torch.nn.utils.weight_norm(cond_layer, name="weight")
+
+ for i in range(n_layers):
+ dilation = dilation_rate**i
+ padding = int((kernel_size * dilation - dilation) / 2)
+ in_layer = torch.nn.Conv1d(
+ hidden_channels,
+ 2 * hidden_channels,
+ kernel_size,
+ dilation=dilation,
+ padding=padding,
+ )
+ in_layer = torch.nn.utils.weight_norm(in_layer, name="weight")
+ self.in_layers.append(in_layer)
+
+ # last one is not necessary
+ if i < n_layers - 1:
+ res_skip_channels = 2 * hidden_channels
+ else:
+ res_skip_channels = hidden_channels
+
+ res_skip_layer = torch.nn.Conv1d(hidden_channels, res_skip_channels, 1)
+ res_skip_layer = torch.nn.utils.weight_norm(res_skip_layer, name="weight")
+ self.res_skip_layers.append(res_skip_layer)
+
+ def forward(
+ self, x, x_mask, g = None
+ ):
+ output = torch.zeros_like(x)
+ n_channels_tensor = torch.IntTensor([self.hidden_channels])
+
+ if g is not None:
+ g = self.cond_layer(g)
+
+ for i, (in_layer, res_skip_layer) in enumerate(
+ zip(self.in_layers, self.res_skip_layers)
+ ):
+ x_in = in_layer(x)
+ if g is not None:
+ cond_offset = i * 2 * self.hidden_channels
+ g_l = g[:, cond_offset : cond_offset + 2 * self.hidden_channels, :]
+ else:
+ g_l = torch.zeros_like(x_in)
+
+ acts = commons.fused_add_tanh_sigmoid_multiply(x_in, g_l, n_channels_tensor)
+ acts = self.drop(acts)
+
+ res_skip_acts = res_skip_layer(acts)
+ if i < self.n_layers - 1:
+ res_acts = res_skip_acts[:, : self.hidden_channels, :]
+ x = (x + res_acts) * x_mask
+ output = output + res_skip_acts[:, self.hidden_channels :, :]
+ else:
+ output = output + res_skip_acts
+ return output * x_mask
+
+ def remove_weight_norm(self):
+ if self.gin_channels != 0:
+ torch.nn.utils.remove_weight_norm(self.cond_layer)
+ for l in self.in_layers:
+ torch.nn.utils.remove_weight_norm(l)
+ for l in self.res_skip_layers:
+ torch.nn.utils.remove_weight_norm(l)
+
+class ResBlock1(torch.nn.Module):
+ def __init__(self, channels, kernel_size=3, dilation=(1, 3, 5)):
+ super(ResBlock1, self).__init__()
+ self.convs1 = nn.ModuleList(
+ [
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=dilation[0],
+ padding=get_padding(kernel_size, dilation[0]),
+ )
+ ),
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=dilation[1],
+ padding=get_padding(kernel_size, dilation[1]),
+ )
+ ),
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=dilation[2],
+ padding=get_padding(kernel_size, dilation[2]),
+ )
+ ),
+ ]
+ )
+ self.convs1.apply(init_weights)
+
+ self.convs2 = nn.ModuleList(
+ [
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=1,
+ padding=get_padding(kernel_size, 1),
+ )
+ ),
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=1,
+ padding=get_padding(kernel_size, 1),
+ )
+ ),
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=1,
+ padding=get_padding(kernel_size, 1),
+ )
+ ),
+ ]
+ )
+ self.convs2.apply(init_weights)
+ self.lrelu_slope = LRELU_SLOPE
+
+ def forward(self, x, x_mask = None):
+ for c1, c2 in zip(self.convs1, self.convs2):
+ xt = F.leaky_relu(x, self.lrelu_slope)
+ if x_mask is not None:
+ xt = xt * x_mask
+ xt = c1(xt)
+ xt = F.leaky_relu(xt, self.lrelu_slope)
+ if x_mask is not None:
+ xt = xt * x_mask
+ xt = c2(xt)
+ x = xt + x
+ if x_mask is not None:
+ x = x * x_mask
+ return x
+
+ def remove_weight_norm(self):
+ for l in self.convs1:
+ remove_weight_norm(l)
+ for l in self.convs2:
+ remove_weight_norm(l)
+
+class ResBlock2(torch.nn.Module):
+ def __init__(self, channels, kernel_size=3, dilation=(1, 3)):
+ super(ResBlock2, self).__init__()
+ self.convs = nn.ModuleList(
+ [
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=dilation[0],
+ padding=get_padding(kernel_size, dilation[0]),
+ )
+ ),
+ weight_norm(
+ Conv1d(
+ channels,
+ channels,
+ kernel_size,
+ 1,
+ dilation=dilation[1],
+ padding=get_padding(kernel_size, dilation[1]),
+ )
+ ),
+ ]
+ )
+ self.convs.apply(init_weights)
+ self.lrelu_slope = LRELU_SLOPE
+
+ def forward(self, x, x_mask = None):
+ for c in self.convs:
+ xt = F.leaky_relu(x, self.lrelu_slope)
+ if x_mask is not None:
+ xt = xt * x_mask
+ xt = c(xt)
+ x = xt + x
+ if x_mask is not None:
+ x = x * x_mask
+ return x
+
+ def remove_weight_norm(self):
+ for l in self.convs:
+ remove_weight_norm(l)
+
+class Log(nn.Module):
+ def forward(
+ self,
+ x,
+ x_mask,
+ g = None,
+ reverse = False,
+ ) :
+ if not reverse:
+ y = torch.log(torch.clamp_min(x, 1e-5)) * x_mask
+ logdet = torch.sum(-y, [1, 2])
+ return y, logdet
+ else:
+ x = torch.exp(x) * x_mask
+ return x
+
+
+class Flip(nn.Module):
+ def forward(
+ self,
+ x,
+ x_mask,
+ g = None,
+ reverse = False,
+ ) :
+ x = torch.flip(x, [1])
+ if not reverse:
+ logdet = torch.zeros(x.size(0)).to(dtype=x.dtype, device=x.device)
+ return x, logdet
+ else:
+ return x, torch.zeros([1], device=x.device)
+
+
+class ElementwiseAffine(nn.Module):
+ def __init__(self, channels):
+ super(ElementwiseAffine, self).__init__()
+ self.channels = channels
+ self.m = nn.Parameter(torch.zeros(channels, 1))
+ self.logs = nn.Parameter(torch.zeros(channels, 1))
+
+ def forward(self, x, x_mask, reverse=False, **kwargs):
+ if not reverse:
+ y = self.m + torch.exp(self.logs) * x
+ y = y * x_mask
+ logdet = torch.sum(self.logs * x_mask, [1, 2])
+ return y, logdet
+ else:
+ x = (x - self.m) * torch.exp(-self.logs) * x_mask
+ return x
+
+
+class ResidualCouplingLayer(nn.Module):
+ def __init__(
+ self,
+ channels,
+ hidden_channels,
+ kernel_size,
+ dilation_rate,
+ n_layers,
+ p_dropout=0,
+ gin_channels=0,
+ mean_only=False,
+ ):
+ assert channels % 2 == 0, "channels should be divisible by 2"
+ super(ResidualCouplingLayer, self).__init__()
+ self.channels = channels
+ self.hidden_channels = hidden_channels
+ self.kernel_size = kernel_size
+ self.dilation_rate = dilation_rate
+ self.n_layers = n_layers
+ self.half_channels = channels // 2
+ self.mean_only = mean_only
+
+ self.pre = nn.Conv1d(self.half_channels, hidden_channels, 1)
+ self.enc = WN(
+ hidden_channels,
+ kernel_size,
+ dilation_rate,
+ n_layers,
+ p_dropout=float(p_dropout),
+ gin_channels=gin_channels,
+ )
+ self.post = nn.Conv1d(hidden_channels, self.half_channels * (2 - mean_only), 1)
+ self.post.weight.data.zero_()
+ self.post.bias.data.zero_()
+
+ def forward(
+ self,
+ x,
+ x_mask,
+ g = None,
+ reverse = False,
+ ):
+ x0, x1 = torch.split(x, [self.half_channels] * 2, 1)
+ h = self.pre(x0) * x_mask
+ h = self.enc(h, x_mask, g=g)
+ stats = self.post(h) * x_mask
+ if not self.mean_only:
+ m, logs = torch.split(stats, [self.half_channels] * 2, 1)
+ else:
+ m = stats
+ logs = torch.zeros_like(m)
+
+ if not reverse:
+ x1 = m + x1 * torch.exp(logs) * x_mask
+ x = torch.cat([x0, x1], 1)
+ logdet = torch.sum(logs, [1, 2])
+ return x, logdet
+ else:
+ x1 = (x1 - m) * torch.exp(-logs) * x_mask
+ x = torch.cat([x0, x1], 1)
+ return x, torch.zeros([1])
+
+ def remove_weight_norm(self):
+ self.enc.remove_weight_norm()
+
+class ConvFlow(nn.Module):
+ def __init__(
+ self,
+ in_channels,
+ filter_channels,
+ kernel_size,
+ n_layers,
+ num_bins=10,
+ tail_bound=5.0,
+ ):
+ super(ConvFlow, self).__init__()
+ self.in_channels = in_channels
+ self.filter_channels = filter_channels
+ self.kernel_size = kernel_size
+ self.n_layers = n_layers
+ self.num_bins = num_bins
+ self.tail_bound = tail_bound
+ self.half_channels = in_channels // 2
+
+ self.pre = nn.Conv1d(self.half_channels, filter_channels, 1)
+ self.convs = DDSConv(filter_channels, kernel_size, n_layers, p_dropout=0.0)
+ self.proj = nn.Conv1d(
+ filter_channels, self.half_channels * (num_bins * 3 - 1), 1
+ )
+ self.proj.weight.data.zero_()
+ self.proj.bias.data.zero_()
+
+ def forward(
+ self,
+ x,
+ x_mask,
+ g = None,
+ reverse=False,
+ ):
+ x0, x1 = torch.split(x, [self.half_channels] * 2, 1)
+ h = self.pre(x0)
+ h = self.convs(h, x_mask, g=g)
+ h = self.proj(h) * x_mask
+
+ b, c, t = x0.shape
+ h = h.reshape(b, c, -1, t).permute(0, 1, 3, 2) # [b, cx?, t] -> [b, c, t, ?]
+
+ unnormalized_widths = h[..., : self.num_bins] / math.sqrt(self.filter_channels)
+ unnormalized_heights = h[..., self.num_bins : 2 * self.num_bins] / math.sqrt(
+ self.filter_channels
+ )
+ unnormalized_derivatives = h[..., 2 * self.num_bins :]
+
+ x1, logabsdet = piecewise_rational_quadratic_transform(
+ x1,
+ unnormalized_widths,
+ unnormalized_heights,
+ unnormalized_derivatives,
+ inverse=reverse,
+ tails="linear",
+ tail_bound=self.tail_bound,
+ )
+
+ x = torch.cat([x0, x1], 1) * x_mask
+ logdet = torch.sum(logabsdet * x_mask, [1, 2])
+ if not reverse:
+ return x, logdet
+ else:
+ return x
diff --git a/infer/module/transforms.py b/infer/module/transforms.py
new file mode 100644
index 0000000..6d07b3b
--- /dev/null
+++ b/infer/module/transforms.py
@@ -0,0 +1,207 @@
+import numpy as np
+import torch
+from torch.nn import functional as F
+
+DEFAULT_MIN_BIN_WIDTH = 1e-3
+DEFAULT_MIN_BIN_HEIGHT = 1e-3
+DEFAULT_MIN_DERIVATIVE = 1e-3
+
+
+def piecewise_rational_quadratic_transform(
+ inputs,
+ unnormalized_widths,
+ unnormalized_heights,
+ unnormalized_derivatives,
+ inverse=False,
+ tails=None,
+ tail_bound=1.0,
+ min_bin_width=DEFAULT_MIN_BIN_WIDTH,
+ min_bin_height=DEFAULT_MIN_BIN_HEIGHT,
+ min_derivative=DEFAULT_MIN_DERIVATIVE,
+):
+ if tails is None:
+ spline_fn = rational_quadratic_spline
+ spline_kwargs = {}
+ else:
+ spline_fn = unconstrained_rational_quadratic_spline
+ spline_kwargs = {"tails": tails, "tail_bound": tail_bound}
+
+ outputs, logabsdet = spline_fn(
+ inputs=inputs,
+ unnormalized_widths=unnormalized_widths,
+ unnormalized_heights=unnormalized_heights,
+ unnormalized_derivatives=unnormalized_derivatives,
+ inverse=inverse,
+ min_bin_width=min_bin_width,
+ min_bin_height=min_bin_height,
+ min_derivative=min_derivative,
+ **spline_kwargs
+ )
+ return outputs, logabsdet
+
+
+def searchsorted(bin_locations, inputs, eps=1e-6):
+ bin_locations[..., -1] += eps
+ return torch.sum(inputs[..., None] >= bin_locations, dim=-1) - 1
+
+
+def unconstrained_rational_quadratic_spline(
+ inputs,
+ unnormalized_widths,
+ unnormalized_heights,
+ unnormalized_derivatives,
+ inverse=False,
+ tails="linear",
+ tail_bound=1.0,
+ min_bin_width=DEFAULT_MIN_BIN_WIDTH,
+ min_bin_height=DEFAULT_MIN_BIN_HEIGHT,
+ min_derivative=DEFAULT_MIN_DERIVATIVE,
+):
+ inside_interval_mask = (inputs >= -tail_bound) & (inputs <= tail_bound)
+ outside_interval_mask = ~inside_interval_mask
+
+ outputs = torch.zeros_like(inputs)
+ logabsdet = torch.zeros_like(inputs)
+
+ if tails == "linear":
+ unnormalized_derivatives = F.pad(unnormalized_derivatives, pad=(1, 1))
+ constant = np.log(np.exp(1 - min_derivative) - 1)
+ unnormalized_derivatives[..., 0] = constant
+ unnormalized_derivatives[..., -1] = constant
+
+ outputs[outside_interval_mask] = inputs[outside_interval_mask]
+ logabsdet[outside_interval_mask] = 0
+ else:
+ raise RuntimeError("{} tails are not implemented.".format(tails))
+
+ (
+ outputs[inside_interval_mask],
+ logabsdet[inside_interval_mask],
+ ) = rational_quadratic_spline(
+ inputs=inputs[inside_interval_mask],
+ unnormalized_widths=unnormalized_widths[inside_interval_mask, :],
+ unnormalized_heights=unnormalized_heights[inside_interval_mask, :],
+ unnormalized_derivatives=unnormalized_derivatives[inside_interval_mask, :],
+ inverse=inverse,
+ left=-tail_bound,
+ right=tail_bound,
+ bottom=-tail_bound,
+ top=tail_bound,
+ min_bin_width=min_bin_width,
+ min_bin_height=min_bin_height,
+ min_derivative=min_derivative,
+ )
+
+ return outputs, logabsdet
+
+
+def rational_quadratic_spline(
+ inputs,
+ unnormalized_widths,
+ unnormalized_heights,
+ unnormalized_derivatives,
+ inverse=False,
+ left=0.0,
+ right=1.0,
+ bottom=0.0,
+ top=1.0,
+ min_bin_width=DEFAULT_MIN_BIN_WIDTH,
+ min_bin_height=DEFAULT_MIN_BIN_HEIGHT,
+ min_derivative=DEFAULT_MIN_DERIVATIVE,
+):
+ if torch.min(inputs) < left or torch.max(inputs) > right:
+ raise ValueError("Input to a transform is not within its domain")
+
+ num_bins = unnormalized_widths.shape[-1]
+
+ if min_bin_width * num_bins > 1.0:
+ raise ValueError("Minimal bin width too large for the number of bins")
+ if min_bin_height * num_bins > 1.0:
+ raise ValueError("Minimal bin height too large for the number of bins")
+
+ widths = F.softmax(unnormalized_widths, dim=-1)
+ widths = min_bin_width + (1 - min_bin_width * num_bins) * widths
+ cumwidths = torch.cumsum(widths, dim=-1)
+ cumwidths = F.pad(cumwidths, pad=(1, 0), mode="constant", value=0.0)
+ cumwidths = (right - left) * cumwidths + left
+ cumwidths[..., 0] = left
+ cumwidths[..., -1] = right
+ widths = cumwidths[..., 1:] - cumwidths[..., :-1]
+
+ derivatives = min_derivative + F.softplus(unnormalized_derivatives)
+
+ heights = F.softmax(unnormalized_heights, dim=-1)
+ heights = min_bin_height + (1 - min_bin_height * num_bins) * heights
+ cumheights = torch.cumsum(heights, dim=-1)
+ cumheights = F.pad(cumheights, pad=(1, 0), mode="constant", value=0.0)
+ cumheights = (top - bottom) * cumheights + bottom
+ cumheights[..., 0] = bottom
+ cumheights[..., -1] = top
+ heights = cumheights[..., 1:] - cumheights[..., :-1]
+
+ if inverse:
+ bin_idx = searchsorted(cumheights, inputs)[..., None]
+ else:
+ bin_idx = searchsorted(cumwidths, inputs)[..., None]
+
+ input_cumwidths = cumwidths.gather(-1, bin_idx)[..., 0]
+ input_bin_widths = widths.gather(-1, bin_idx)[..., 0]
+
+ input_cumheights = cumheights.gather(-1, bin_idx)[..., 0]
+ delta = heights / widths
+ input_delta = delta.gather(-1, bin_idx)[..., 0]
+
+ input_derivatives = derivatives.gather(-1, bin_idx)[..., 0]
+ input_derivatives_plus_one = derivatives[..., 1:].gather(-1, bin_idx)[..., 0]
+
+ input_heights = heights.gather(-1, bin_idx)[..., 0]
+
+ if inverse:
+ a = (inputs - input_cumheights) * (
+ input_derivatives + input_derivatives_plus_one - 2 * input_delta
+ ) + input_heights * (input_delta - input_derivatives)
+ b = input_heights * input_derivatives - (inputs - input_cumheights) * (
+ input_derivatives + input_derivatives_plus_one - 2 * input_delta
+ )
+ c = -input_delta * (inputs - input_cumheights)
+
+ discriminant = b.pow(2) - 4 * a * c
+ assert (discriminant >= 0).all()
+
+ root = (2 * c) / (-b - torch.sqrt(discriminant))
+ outputs = root * input_bin_widths + input_cumwidths
+
+ theta_one_minus_theta = root * (1 - root)
+ denominator = input_delta + (
+ (input_derivatives + input_derivatives_plus_one - 2 * input_delta)
+ * theta_one_minus_theta
+ )
+ derivative_numerator = input_delta.pow(2) * (
+ input_derivatives_plus_one * root.pow(2)
+ + 2 * input_delta * theta_one_minus_theta
+ + input_derivatives * (1 - root).pow(2)
+ )
+ logabsdet = torch.log(derivative_numerator) - 2 * torch.log(denominator)
+
+ return outputs, -logabsdet
+ else:
+ theta = (inputs - input_cumwidths) / input_bin_widths
+ theta_one_minus_theta = theta * (1 - theta)
+
+ numerator = input_heights * (
+ input_delta * theta.pow(2) + input_derivatives * theta_one_minus_theta
+ )
+ denominator = input_delta + (
+ (input_derivatives + input_derivatives_plus_one - 2 * input_delta)
+ * theta_one_minus_theta
+ )
+ outputs = input_cumheights + numerator / denominator
+
+ derivative_numerator = input_delta.pow(2) * (
+ input_derivatives_plus_one * theta.pow(2)
+ + 2 * input_delta * theta_one_minus_theta
+ + input_derivatives * (1 - theta).pow(2)
+ )
+ logabsdet = torch.log(derivative_numerator) - 2 * torch.log(denominator)
+
+ return outputs, logabsdet
diff --git a/infer/rmvpe.py b/infer/rmvpe.py
new file mode 100644
index 0000000..cccb8d0
--- /dev/null
+++ b/infer/rmvpe.py
@@ -0,0 +1,640 @@
+import os
+from typing import List, Optional, Tuple
+import numpy as np
+import torch
+
+import torch.nn as nn
+import torch.nn.functional as F
+from librosa.util import normalize, pad_center, tiny
+from scipy.signal import get_window
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class STFT(torch.nn.Module):
+ def __init__(
+ self, filter_length=1024, hop_length=512, win_length=None, window="hann"
+ ):
+ """
+ This module implements an STFT using 1D convolution and 1D transpose convolutions.
+ This is a bit tricky so there are some cases that probably won't work as working
+ out the same sizes before and after in all overlap add setups is tough. Right now,
+ this code should work with hop lengths that are half the filter length (50% overlap
+ between frames).
+
+ Keyword Arguments:
+ filter_length {int} -- Length of filters used (default: {1024})
+ hop_length {int} -- Hop length of STFT (restrict to 50% overlap between frames) (default: {512})
+ win_length {[type]} -- Length of the window function applied to each frame (if not specified, it
+ equals the filter length). (default: {None})
+ window {str} -- Type of window to use (options are bartlett, hann, hamming, blackman, blackmanharris)
+ (default: {'hann'})
+ """
+ super(STFT, self).__init__()
+ self.filter_length = filter_length
+ self.hop_length = hop_length
+ self.win_length = win_length if win_length else filter_length
+ self.window = window
+ self.forward_transform = None
+ self.pad_amount = int(self.filter_length / 2)
+ fourier_basis = np.fft.fft(np.eye(self.filter_length))
+
+ cutoff = int((self.filter_length / 2 + 1))
+ fourier_basis = np.vstack(
+ [np.real(fourier_basis[:cutoff, :]), np.imag(fourier_basis[:cutoff, :])]
+ )
+ forward_basis = torch.FloatTensor(fourier_basis)
+ inverse_basis = torch.FloatTensor(np.linalg.pinv(fourier_basis))
+
+ assert filter_length >= self.win_length
+ # get window and zero center pad it to filter_length
+ fft_window = get_window(window, self.win_length, fftbins=True)
+ fft_window = pad_center(fft_window, size=filter_length)
+ fft_window = torch.from_numpy(fft_window).float()
+
+ # window the bases
+ forward_basis *= fft_window
+ inverse_basis = (inverse_basis.T * fft_window).T
+
+ self.register_buffer("forward_basis", forward_basis.float())
+ self.register_buffer("inverse_basis", inverse_basis.float())
+ self.register_buffer("fft_window", fft_window.float())
+
+ def transform(self, input_data, return_phase=False):
+ """Take input data (audio) to STFT domain.
+
+ Arguments:
+ input_data {tensor} -- Tensor of floats, with shape (num_batch, num_samples)
+
+ Returns:
+ magnitude {tensor} -- Magnitude of STFT with shape (num_batch,
+ num_frequencies, num_frames)
+ phase {tensor} -- Phase of STFT with shape (num_batch,
+ num_frequencies, num_frames)
+ """
+ input_data = F.pad(
+ input_data,
+ (self.pad_amount, self.pad_amount),
+ mode="reflect",
+ )
+ forward_transform = input_data.unfold(
+ 1, self.filter_length, self.hop_length
+ ).permute(0, 2, 1)
+ forward_transform = torch.matmul(self.forward_basis, forward_transform)
+ cutoff = int((self.filter_length / 2) + 1)
+ real_part = forward_transform[:, :cutoff, :]
+ imag_part = forward_transform[:, cutoff:, :]
+ magnitude = torch.sqrt(real_part**2 + imag_part**2)
+ if return_phase:
+ phase = torch.atan2(imag_part.data, real_part.data)
+ return magnitude, phase
+ else:
+ return magnitude
+
+ def inverse(self, magnitude, phase):
+ """Call the inverse STFT (iSTFT), given magnitude and phase tensors produced
+ by the ```transform``` function.
+
+ Arguments:
+ magnitude {tensor} -- Magnitude of STFT with shape (num_batch,
+ num_frequencies, num_frames)
+ phase {tensor} -- Phase of STFT with shape (num_batch,
+ num_frequencies, num_frames)
+
+ Returns:
+ inverse_transform {tensor} -- Reconstructed audio given magnitude and phase. Of
+ shape (num_batch, num_samples)
+ """
+ cat = torch.cat(
+ [magnitude * torch.cos(phase), magnitude * torch.sin(phase)], dim=1
+ )
+ fold = torch.nn.Fold(
+ output_size=(1, (cat.size(-1) - 1) * self.hop_length + self.filter_length),
+ kernel_size=(1, self.filter_length),
+ stride=(1, self.hop_length),
+ )
+ inverse_transform = torch.matmul(self.inverse_basis, cat)
+ inverse_transform = fold(inverse_transform)[
+ :, 0, 0, self.pad_amount : -self.pad_amount
+ ]
+ window_square_sum = (
+ self.fft_window.pow(2).repeat(cat.size(-1), 1).T.unsqueeze(0)
+ )
+ window_square_sum = fold(window_square_sum)[
+ :, 0, 0, self.pad_amount : -self.pad_amount
+ ]
+ inverse_transform /= window_square_sum
+ return inverse_transform
+
+ def forward(self, input_data):
+ """Take input data (audio) to STFT domain and then back to audio.
+
+ Arguments:
+ input_data {tensor} -- Tensor of floats, with shape (num_batch, num_samples)
+
+ Returns:
+ reconstruction {tensor} -- Reconstructed audio given magnitude and phase. Of
+ shape (num_batch, num_samples)
+ """
+ self.magnitude, self.phase = self.transform(input_data, return_phase=True)
+ reconstruction = self.inverse(self.magnitude, self.phase)
+ return reconstruction
+
+
+from time import time as ttime
+
+
+class BiGRU(nn.Module):
+ def __init__(self, input_features, hidden_features, num_layers):
+ super(BiGRU, self).__init__()
+ self.gru = nn.GRU(
+ input_features,
+ hidden_features,
+ num_layers=num_layers,
+ batch_first=True,
+ bidirectional=True,
+ )
+
+ def forward(self, x):
+ return self.gru(x)[0]
+
+
+class ConvBlockRes(nn.Module):
+ def __init__(self, in_channels, out_channels, momentum=0.01):
+ super(ConvBlockRes, self).__init__()
+ self.conv = nn.Sequential(
+ nn.Conv2d(
+ in_channels=in_channels,
+ out_channels=out_channels,
+ kernel_size=(3, 3),
+ stride=(1, 1),
+ padding=(1, 1),
+ bias=False,
+ ),
+ nn.BatchNorm2d(out_channels, momentum=momentum),
+ nn.ReLU(),
+ nn.Conv2d(
+ in_channels=out_channels,
+ out_channels=out_channels,
+ kernel_size=(3, 3),
+ stride=(1, 1),
+ padding=(1, 1),
+ bias=False,
+ ),
+ nn.BatchNorm2d(out_channels, momentum=momentum),
+ nn.ReLU(),
+ )
+ # self.shortcut:Optional[nn.Module] = None
+ if in_channels != out_channels:
+ self.shortcut = nn.Conv2d(in_channels, out_channels, (1, 1))
+
+ def forward(self, x):
+ if not hasattr(self, "shortcut"):
+ return self.conv(x) + x
+ else:
+ return self.conv(x) + self.shortcut(x)
+
+
+class Encoder(nn.Module):
+ def __init__(
+ self,
+ in_channels,
+ in_size,
+ n_encoders,
+ kernel_size,
+ n_blocks,
+ out_channels=16,
+ momentum=0.01,
+ ):
+ super(Encoder, self).__init__()
+ self.n_encoders = n_encoders
+ self.bn = nn.BatchNorm2d(in_channels, momentum=momentum)
+ self.layers = nn.ModuleList()
+ self.latent_channels = []
+ for i in range(self.n_encoders):
+ self.layers.append(
+ ResEncoderBlock(
+ in_channels, out_channels, kernel_size, n_blocks, momentum=momentum
+ )
+ )
+ self.latent_channels.append([out_channels, in_size])
+ in_channels = out_channels
+ out_channels *= 2
+ in_size //= 2
+ self.out_size = in_size
+ self.out_channel = out_channels
+
+ def forward(self, x):
+ concat_tensors = []
+ x = self.bn(x)
+ for i, layer in enumerate(self.layers):
+ t, x = layer(x)
+ concat_tensors.append(t)
+ return x, concat_tensors
+
+
+class ResEncoderBlock(nn.Module):
+ def __init__(
+ self, in_channels, out_channels, kernel_size, n_blocks=1, momentum=0.01
+ ):
+ super(ResEncoderBlock, self).__init__()
+ self.n_blocks = n_blocks
+ self.conv = nn.ModuleList()
+ self.conv.append(ConvBlockRes(in_channels, out_channels, momentum))
+ for i in range(n_blocks - 1):
+ self.conv.append(ConvBlockRes(out_channels, out_channels, momentum))
+ self.kernel_size = kernel_size
+ if self.kernel_size is not None:
+ self.pool = nn.AvgPool2d(kernel_size=kernel_size)
+
+ def forward(self, x):
+ for i, conv in enumerate(self.conv):
+ x = conv(x)
+ if self.kernel_size is not None:
+ return x, self.pool(x)
+ else:
+ return x
+
+
+class Intermediate(nn.Module): #
+ def __init__(self, in_channels, out_channels, n_inters, n_blocks, momentum=0.01):
+ super(Intermediate, self).__init__()
+ self.n_inters = n_inters
+ self.layers = nn.ModuleList()
+ self.layers.append(
+ ResEncoderBlock(in_channels, out_channels, None, n_blocks, momentum)
+ )
+ for i in range(self.n_inters - 1):
+ self.layers.append(
+ ResEncoderBlock(out_channels, out_channels, None, n_blocks, momentum)
+ )
+
+ def forward(self, x):
+ for i, layer in enumerate(self.layers):
+ x = layer(x)
+ return x
+
+
+class ResDecoderBlock(nn.Module):
+ def __init__(self, in_channels, out_channels, stride, n_blocks=1, momentum=0.01):
+ super(ResDecoderBlock, self).__init__()
+ out_padding = (0, 1) if stride == (1, 2) else (1, 1)
+ self.n_blocks = n_blocks
+ self.conv1 = nn.Sequential(
+ nn.ConvTranspose2d(
+ in_channels=in_channels,
+ out_channels=out_channels,
+ kernel_size=(3, 3),
+ stride=stride,
+ padding=(1, 1),
+ output_padding=out_padding,
+ bias=False,
+ ),
+ nn.BatchNorm2d(out_channels, momentum=momentum),
+ nn.ReLU(),
+ )
+ self.conv2 = nn.ModuleList()
+ self.conv2.append(ConvBlockRes(out_channels * 2, out_channels, momentum))
+ for i in range(n_blocks - 1):
+ self.conv2.append(ConvBlockRes(out_channels, out_channels, momentum))
+
+ def forward(self, x, concat_tensor):
+ x = self.conv1(x)
+ x = torch.cat((x, concat_tensor), dim=1)
+ for i, conv2 in enumerate(self.conv2):
+ x = conv2(x)
+ return x
+
+
+class Decoder(nn.Module):
+ def __init__(self, in_channels, n_decoders, stride, n_blocks, momentum=0.01):
+ super(Decoder, self).__init__()
+ self.layers = nn.ModuleList()
+ self.n_decoders = n_decoders
+ for i in range(self.n_decoders):
+ out_channels = in_channels // 2
+ self.layers.append(
+ ResDecoderBlock(in_channels, out_channels, stride, n_blocks, momentum)
+ )
+ in_channels = out_channels
+
+ def forward(self, x, concat_tensors):
+ for i, layer in enumerate(self.layers):
+ x = layer(x, concat_tensors[-1 - i])
+ return x
+
+
+class DeepUnet(nn.Module):
+ def __init__(
+ self,
+ kernel_size,
+ n_blocks,
+ en_de_layers=5,
+ inter_layers=4,
+ in_channels=1,
+ en_out_channels=16,
+ ):
+ super(DeepUnet, self).__init__()
+ self.encoder = Encoder(
+ in_channels, 128, en_de_layers, kernel_size, n_blocks, en_out_channels
+ )
+ self.intermediate = Intermediate(
+ self.encoder.out_channel // 2,
+ self.encoder.out_channel,
+ inter_layers,
+ n_blocks,
+ )
+ self.decoder = Decoder(
+ self.encoder.out_channel, en_de_layers, kernel_size, n_blocks
+ )
+
+ def forward(self, x) :
+ x, concat_tensors = self.encoder(x)
+ x = self.intermediate(x)
+ x = self.decoder(x, concat_tensors)
+ return x
+
+
+class E2E(nn.Module):
+ def __init__(
+ self,
+ n_blocks,
+ n_gru,
+ kernel_size,
+ en_de_layers=5,
+ inter_layers=4,
+ in_channels=1,
+ en_out_channels=16,
+ ):
+ super(E2E, self).__init__()
+ self.unet = DeepUnet(
+ kernel_size,
+ n_blocks,
+ en_de_layers,
+ inter_layers,
+ in_channels,
+ en_out_channels,
+ )
+ self.cnn = nn.Conv2d(en_out_channels, 3, (3, 3), padding=(1, 1))
+ if n_gru:
+ self.fc = nn.Sequential(
+ BiGRU(3 * 128, 256, n_gru),
+ nn.Linear(512, 360),
+ nn.Dropout(0.25),
+ nn.Sigmoid(),
+ )
+ else:
+ self.fc = nn.Sequential(
+ nn.Linear(3 * nn.N_MELS, nn.N_CLASS), nn.Dropout(0.25), nn.Sigmoid()
+ )
+
+ def forward(self, mel):
+ # print(mel.shape)
+ mel = mel.transpose(-1, -2).unsqueeze(1)
+ x = self.cnn(self.unet(mel)).transpose(1, 2).flatten(-2)
+ x = self.fc(x)
+ # print(x.shape)
+ return x
+
+
+from librosa.filters import mel
+
+
+class MelSpectrogram(torch.nn.Module):
+ def __init__(
+ self,
+ is_half,
+ n_mel_channels,
+ sampling_rate,
+ win_length,
+ hop_length,
+ n_fft=None,
+ mel_fmin=0,
+ mel_fmax=None,
+ clamp=1e-5,
+ ):
+ super().__init__()
+ n_fft = win_length if n_fft is None else n_fft
+ self.hann_window = {}
+ mel_basis = mel(
+ sr=sampling_rate,
+ n_fft=n_fft,
+ n_mels=n_mel_channels,
+ fmin=mel_fmin,
+ fmax=mel_fmax,
+ htk=True,
+ )
+ mel_basis = torch.from_numpy(mel_basis).float()
+ self.register_buffer("mel_basis", mel_basis)
+ self.n_fft = win_length if n_fft is None else n_fft
+ self.hop_length = hop_length
+ self.win_length = win_length
+ self.sampling_rate = sampling_rate
+ self.n_mel_channels = n_mel_channels
+ self.clamp = clamp
+ self.is_half = is_half
+
+ def forward(self, audio, keyshift=0, speed=1, center=True):
+ factor = 2 ** (keyshift / 12)
+ n_fft_new = int(np.round(self.n_fft * factor))
+ win_length_new = int(np.round(self.win_length * factor))
+ hop_length_new = int(np.round(self.hop_length * speed))
+ keyshift_key = str(keyshift) + "_" + str(audio.device)
+ if keyshift_key not in self.hann_window:
+ self.hann_window[keyshift_key] = torch.hann_window(win_length_new).to(
+ audio.device
+ )
+ if "privateuseone" in str(audio.device):
+ if not hasattr(self, "stft"):
+ self.stft = STFT(
+ filter_length=n_fft_new,
+ hop_length=hop_length_new,
+ win_length=win_length_new,
+ window="hann",
+ ).to(audio.device)
+ magnitude = self.stft.transform(audio)
+ else:
+ fft = torch.stft(
+ audio,
+ n_fft=n_fft_new,
+ hop_length=hop_length_new,
+ win_length=win_length_new,
+ window=self.hann_window[keyshift_key],
+ center=center,
+ return_complex=True,
+ )
+ magnitude = torch.sqrt(fft.real.pow(2) + fft.imag.pow(2))
+ if keyshift != 0:
+ size = self.n_fft // 2 + 1
+ resize = magnitude.size(1)
+ if resize < size:
+ magnitude = F.pad(magnitude, (0, 0, 0, size - resize))
+ magnitude = magnitude[:, :size, :] * self.win_length / win_length_new
+ mel_output = torch.matmul(self.mel_basis, magnitude)
+ if self.is_half == True:
+ mel_output = mel_output.half()
+ log_mel_spec = torch.log(torch.clamp(mel_output, min=self.clamp))
+ return log_mel_spec
+
+
+class RMVPE:
+ def __init__(self, model_path, is_half, device=None):
+ self.resample_kernel = {}
+ self.resample_kernel = {}
+ if isinstance(is_half, str):
+ is_half = is_half.lower() == "true"
+ if device is None:
+ from configs.config import infer_device, infer_dtype
+
+ device = str(infer_device)
+ is_half = infer_dtype == torch.float16
+ elif str(device).startswith("cuda"):
+ from configs.config import get_device_dtype_sm
+
+ parsed_device = torch.device(device)
+ device_index = parsed_device.index
+ if device_index is None:
+ device_index = torch.cuda.current_device()
+ selected_device, selected_dtype, _, _ = get_device_dtype_sm(device_index)
+ device = str(selected_device)
+ is_half = selected_dtype == torch.float16
+ else:
+ is_half = False
+ self.is_half = is_half
+ self.device = device
+ self.mel_extractor = MelSpectrogram(
+ is_half, 128, 16000, 1024, 160, None, 30, 8000
+ ).to(device)
+ if "privateuseone" in str(device):
+ import onnxruntime as ort
+
+ ort_session = ort.InferenceSession(
+ os.path.splitext(model_path)[0] + ".onnx",
+ providers=["DmlExecutionProvider"],
+ )
+ self.model = ort_session
+ else:
+ if str(self.device) == "cuda":
+ self.device = torch.device("cuda:0")
+
+ def get_default_model():
+ model = E2E(4, 1, (2, 2))
+ ckpt = torch.load(model_path, map_location="cpu")
+ model.load_state_dict(ckpt)
+ model.eval()
+ if is_half:
+ model = model.half()
+ else:
+ model = model.float()
+ return model
+
+ self.model = get_default_model()
+
+ self.model = self.model.to(device)
+ cents_mapping = 20 * np.arange(360) + 1997.3794084376191
+ self.cents_mapping = np.pad(cents_mapping, (4, 4)) # 368
+
+ def mel2hidden(self, mel):
+ with torch.no_grad():
+ n_frames = mel.shape[-1]
+ n_pad = 32 * ((n_frames - 1) // 32 + 1) - n_frames
+ if n_pad > 0:
+ mel = F.pad(mel, (0, n_pad), mode="constant")
+ if "privateuseone" in str(self.device):
+ onnx_input_name = self.model.get_inputs()[0].name
+ onnx_outputs_names = self.model.get_outputs()[0].name
+ hidden = self.model.run(
+ [onnx_outputs_names],
+ input_feed={onnx_input_name: mel.cpu().numpy()},
+ )[0]
+ else:
+ mel = mel.half() if self.is_half else mel.float()
+ hidden = self.model(mel)
+ return hidden[:, :n_frames]
+
+ def decode(self, hidden, thred=0.03):
+ cents_pred = self.to_local_average_cents(hidden, thred=thred)
+ f0 = 10 * (2 ** (cents_pred / 1200))
+ f0[f0 == 10] = 0
+ # f0 = np.array([10 * (2 ** (cent_pred / 1200)) if cent_pred else 0 for cent_pred in cents_pred])
+ return f0
+
+ def infer_from_audio(self, audio, thred=0.03):
+ # torch.cuda.synchronize()
+ # t0 = ttime()
+ if not torch.is_tensor(audio):
+ audio = torch.from_numpy(audio)
+ mel = self.mel_extractor(
+ audio.float().to(self.device).unsqueeze(0), center=True
+ )
+ # print(123123123,mel.device.type)
+ # torch.cuda.synchronize()
+ # t1 = ttime()
+ hidden = self.mel2hidden(mel)
+ # torch.cuda.synchronize()
+ # t2 = ttime()
+ # print(234234,hidden.device.type)
+ if "privateuseone" not in str(self.device):
+ hidden = hidden.squeeze(0).cpu().numpy()
+ else:
+ hidden = hidden[0]
+ if self.is_half == True:
+ hidden = hidden.astype("float32")
+
+ f0 = self.decode(hidden, thred=thred)
+ # torch.cuda.synchronize()
+ # t3 = ttime()
+ # print("hmvpe:%s\t%s\t%s\t%s"%(t1-t0,t2-t1,t3-t2,t3-t0))
+ return f0
+
+ def to_local_average_cents(self, salience, thred=0.05):
+ # t0 = ttime()
+ center = np.argmax(salience, axis=1) # 帧长#index
+ salience = np.pad(salience, ((0, 0), (4, 4))) # 帧长,368
+ # t1 = ttime()
+ center += 4
+ todo_salience = []
+ todo_cents_mapping = []
+ starts = center - 4
+ ends = center + 5
+ for idx in range(salience.shape[0]):
+ todo_salience.append(salience[:, starts[idx] : ends[idx]][idx])
+ todo_cents_mapping.append(self.cents_mapping[starts[idx] : ends[idx]])
+ # t2 = ttime()
+ todo_salience = np.array(todo_salience) # 帧长,9
+ todo_cents_mapping = np.array(todo_cents_mapping) # 帧长,9
+ product_sum = np.sum(todo_salience * todo_cents_mapping, 1)
+ weight_sum = np.sum(todo_salience, 1) # 帧长
+ devided = product_sum / weight_sum # 帧长
+ # t3 = ttime()
+ maxx = np.max(salience, axis=1) # 帧长
+ devided[maxx <= thred] = 0
+ # t4 = ttime()
+ # print("decode:%s\t%s\t%s\t%s" % (t1 - t0, t2 - t1, t3 - t2, t4 - t3))
+ return devided
+
+
+if __name__ == "__main__":
+ import librosa
+ import soundfile as sf
+
+ audio, sampling_rate = sf.read(r"C:\Users\liujing04\Desktop\Z\冬之花clip1.wav")
+ if len(audio.shape) > 1:
+ audio = librosa.to_mono(audio.transpose(1, 0))
+ audio_bak = audio.copy()
+ if sampling_rate != 16000:
+ audio = librosa.resample(audio, orig_sr=sampling_rate, target_sr=16000)
+ model_path = r"D:\BaiduNetdiskDownload\RVC-beta-v2-0727AMD_realtime\rmvpe.pt"
+ thred = 0.03 # 0.01
+ device = "cuda" if torch.cuda.is_available() else "cpu"
+ rmvpe = RMVPE(model_path, is_half=False, device=device)
+ t0 = ttime()
+ f0 = rmvpe.infer_from_audio(audio, thred=thred)
+ # f0 = rmvpe.infer_from_audio(audio, thred=thred)
+ # f0 = rmvpe.infer_from_audio(audio, thred=thred)
+ # f0 = rmvpe.infer_from_audio(audio, thred=thred)
+ # f0 = rmvpe.infer_from_audio(audio, thred=thred)
+ t1 = ttime()
+ logger.info("%s %.2f", f0.shape, t1 - t0)
diff --git a/infer/rtrvc.py b/infer/rtrvc.py
new file mode 100644
index 0000000..4fced9f
--- /dev/null
+++ b/infer/rtrvc.py
@@ -0,0 +1,342 @@
+import traceback
+from time import time as ttime
+import faiss
+import numpy as np
+import parselmouth
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+from torchaudio.transforms import Resample
+
+from infer.hubert import extract_hubert_features, load_hubert_model
+from i18n.i18n import I18nAuto
+
+
+i18n = I18nAuto()
+
+
+def printt(strr, *args):
+ if len(args) == 0:
+ print(strr)
+ else:
+ print(strr % args)
+
+
+def get_synthesizer(pth_path, device=torch.device("cpu")):
+ from infer.module.models import (
+ SynthesizerTrnMs256NSFsid,
+ SynthesizerTrnMs256NSFsid_nono,
+ SynthesizerTrnMs768NSFsid,
+ SynthesizerTrnMs768NSFsid_nono,
+ )
+
+ cpt = torch.load(pth_path, map_location=torch.device("cpu"))
+ cpt["config"][-3] = cpt["weight"]["emb_g.weight"].shape[0]
+ if_f0 = cpt.get("f0", 1)
+ version = cpt.get("version", "v1")
+ if version == "v1":
+ if if_f0 == 1:
+ net_g = SynthesizerTrnMs256NSFsid(*cpt["config"], is_half=False)
+ else:
+ net_g = SynthesizerTrnMs256NSFsid_nono(*cpt["config"])
+ elif version == "v2":
+ if if_f0 == 1:
+ net_g = SynthesizerTrnMs768NSFsid(*cpt["config"], is_half=False)
+ else:
+ net_g = SynthesizerTrnMs768NSFsid_nono(*cpt["config"])
+ del net_g.enc_q
+ net_g.load_state_dict(cpt["weight"], strict=False)
+ net_g = net_g.float()
+ net_g.eval().to(device)
+ net_g.remove_weight_norm()
+ return net_g, cpt
+
+
+# config.device=torch.device("cpu")########强制cpu测试
+# config.is_half=False########强制cpu测试
+class RVC:
+ def __init__(
+ self,
+ key,
+ formant,
+ pth_path,
+ index_path,
+ index_rate,
+ config,
+ last_rvc=None,
+ ) :
+ """
+ 初始化
+ """
+ try:
+ # global config
+ self.config = config
+ # device="cpu"########强制cpu测试
+ self.device = config.device
+ self.f0_up_key = key
+ self.formant_shift = formant
+ self.f0_min = 50
+ self.f0_max = 1100
+ self.f0_mel_min = 1127 * np.log(1 + self.f0_min / 700)
+ self.f0_mel_max = 1127 * np.log(1 + self.f0_max / 700)
+ self.is_half = config.is_half
+ if index_rate != 0:
+ self.index = faiss.read_index(index_path)
+ self.big_npy = self.index.reconstruct_n(0, self.index.ntotal)
+ printt(i18n("已启用索引检索"))
+ self.pth_path = pth_path
+ self.index_path = index_path
+ self.index_rate = index_rate
+ self.cache_pitch = torch.zeros(
+ 1024, device=self.device, dtype=torch.long
+ )
+ self.cache_pitchf = torch.zeros(
+ 1024, device=self.device, dtype=torch.float32
+ )
+
+ self.resample_kernel = {}
+
+ if last_rvc is None:
+ self.model = load_hubert_model(self.device, self.is_half)
+ else:
+ self.model = last_rvc.model
+
+ self.net_g = None
+
+ def set_synthesizer():
+ self.net_g, cpt = get_synthesizer(self.pth_path, self.device)
+ self.tgt_sr = cpt["config"][-1]
+ cpt["config"][-3] = cpt["weight"]["emb_g.weight"].shape[0]
+ self.if_f0 = cpt.get("f0", 1)
+ self.version = cpt.get("version", "v1")
+ if self.is_half:
+ self.net_g = self.net_g.half()
+ else:
+ self.net_g = self.net_g.float()
+
+ if last_rvc is None or last_rvc.pth_path != self.pth_path:
+ set_synthesizer()
+ else:
+ self.tgt_sr = last_rvc.tgt_sr
+ self.if_f0 = last_rvc.if_f0
+ self.version = last_rvc.version
+ self.is_half = last_rvc.is_half
+ self.net_g = last_rvc.net_g
+
+ if last_rvc is not None and hasattr(last_rvc, "model_rmvpe"):
+ self.model_rmvpe = last_rvc.model_rmvpe
+ if last_rvc is not None and hasattr(last_rvc, "model_fcpe"):
+ self.model_fcpe = last_rvc.model_fcpe
+ except:
+ printt(traceback.format_exc())
+
+ def change_key(self, new_key):
+ self.f0_up_key = new_key
+
+ def change_formant(self, new_formant):
+ self.formant_shift = new_formant
+
+ def change_index_rate(self, new_index_rate):
+ if new_index_rate != 0 and self.index_rate == 0:
+ self.index = faiss.read_index(self.index_path)
+ self.big_npy = self.index.reconstruct_n(0, self.index.ntotal)
+ printt(i18n("已启用索引检索"))
+ self.index_rate = new_index_rate
+
+ def get_f0_post(self, f0):
+ if not torch.is_tensor(f0):
+ f0 = torch.from_numpy(f0)
+ f0 = f0.float().to(self.device).squeeze()
+ f0_mel = 1127 * torch.log(1 + f0 / 700)
+ f0_mel[f0_mel > 0] = (f0_mel[f0_mel > 0] - self.f0_mel_min) * 254 / (
+ self.f0_mel_max - self.f0_mel_min
+ ) + 1
+ f0_mel[f0_mel <= 1] = 1
+ f0_mel[f0_mel > 255] = 255
+ f0_coarse = torch.round(f0_mel).long()
+ return f0_coarse, f0
+
+ def get_f0(self, x, f0_up_key, method="rmvpe"):
+ if method == "rmvpe":
+ return self.get_f0_rmvpe(x, f0_up_key)
+ if method == "fcpe":
+ return self.get_f0_fcpe(x, f0_up_key)
+ if method != "pm":
+ raise ValueError(f"Unsupported F0 method: {method}")
+ x = x.cpu().numpy()
+ p_len = x.shape[0] // 160 + 1
+ f0_min = 65
+ l_pad = int(np.ceil(1.5 / f0_min * 16000))
+ r_pad = l_pad + 1
+ s = parselmouth.Sound(np.pad(x, (l_pad, r_pad)), 16000).to_pitch_ac(
+ time_step=0.01,
+ voicing_threshold=0.6,
+ pitch_floor=f0_min,
+ pitch_ceiling=1100,
+ )
+ assert np.abs(s.t1 - 1.5 / f0_min) < 0.001
+ f0 = s.selected_array["frequency"]
+ if len(f0) < p_len:
+ f0 = np.pad(f0, (0, p_len - len(f0)))
+ f0 = f0[:p_len]
+ try:
+ uv = f0 == 0
+ f0[uv] = np.interp(np.where(uv)[0], np.where(~uv)[0], f0[~uv])
+ except Exception:
+ traceback.print_exc()
+ f0 *= pow(2, f0_up_key / 12)
+ return self.get_f0_post(f0)
+
+ def get_f0_rmvpe(self, x, f0_up_key):
+ if hasattr(self, "model_rmvpe") == False:
+ from infer.rmvpe import RMVPE
+
+ printt(i18n("正在加载RMVPE模型"))
+ self.model_rmvpe = RMVPE(
+ "assets/rmvpe/rmvpe.pt",
+ is_half=self.is_half,
+ device=self.device,
+ )
+ f0 = self.model_rmvpe.infer_from_audio(x, thred=0.03)
+ try:
+ uv = f0 == 0
+ f0[uv] = np.interp(np.where(uv)[0], np.where(~uv)[0], f0[~uv])
+ except Exception:
+ traceback.print_exc()
+ f0 *= pow(2, f0_up_key / 12)
+ return self.get_f0_post(f0)
+
+ def get_f0_fcpe(self, x, f0_up_key):
+ if hasattr(self, "model_fcpe") == False:
+ from infer.fcpe import FCPEInfer
+
+ printt("Loading fcpe model")
+ self.model_fcpe = FCPEInfer(self.device)
+ f0 = self.model_fcpe.infer(
+ x.unsqueeze(0).float(),
+ sr=16000,
+ decoder_mode="local_argmax",
+ threshold=0.006,
+ ).squeeze().detach().cpu().numpy()
+ try:
+ uv = f0 == 0
+ f0[uv] = np.interp(np.where(uv)[0], np.where(~uv)[0], f0[~uv])
+ except Exception:
+ traceback.print_exc()
+ f0 *= pow(2, f0_up_key / 12)
+ return self.get_f0_post(f0)
+
+ def infer(
+ self,
+ input_wav,
+ block_frame_16k,
+ skip_head,
+ return_length,
+ f0method,
+ ) :
+ t1 = ttime()
+ with torch.no_grad():
+ if self.config.is_half:
+ feats = input_wav.half().view(1, -1)
+ else:
+ feats = input_wav.float().view(1, -1)
+ padding_mask = torch.BoolTensor(feats.shape).to(self.device).fill_(False)
+ feats = extract_hubert_features(
+ self.model,
+ feats,
+ self.version,
+ padding_mask=padding_mask,
+ )
+ feats = torch.cat((feats, feats[:, -1:, :]), 1)
+ t2 = ttime()
+ try:
+ if hasattr(self, "index") and self.index_rate != 0:
+ npy = feats[0][skip_head // 2 :].cpu().numpy().astype("float32")
+ score, ix = self.index.search(npy, k=8)
+ if (ix >= 0).all():
+ weight = np.square(1 / score)
+ weight /= weight.sum(axis=1, keepdims=True)
+ npy = np.sum(
+ self.big_npy[ix] * np.expand_dims(weight, axis=2), axis=1
+ )
+ if self.config.is_half:
+ npy = npy.astype("float16")
+ feats[0][skip_head // 2 :] = (
+ torch.from_numpy(npy).unsqueeze(0).to(self.device)
+ * self.index_rate
+ + (1 - self.index_rate) * feats[0][skip_head // 2 :]
+ )
+ else:
+ printt(
+ i18n("索引无效:必须使用added_xxxx.index,不能使用trained_xxxx.index")
+ )
+ else:
+ printt(i18n("索引检索失败或未启用"))
+ except Exception:
+ traceback.print_exc()
+ printt(i18n("索引检索失败"))
+ t3 = ttime()
+ p_len = input_wav.shape[0] // 160
+ factor = pow(2, self.formant_shift / 12)
+ return_length2 = int(np.ceil(return_length * factor))
+ if self.if_f0 == 1:
+ f0_extractor_frame = block_frame_16k + 800
+ if f0method == "rmvpe":
+ f0_extractor_frame = 5120 * ((f0_extractor_frame - 1) // 5120 + 1) - 160
+ pitch, pitchf = self.get_f0(
+ input_wav[-f0_extractor_frame:],
+ self.f0_up_key - self.formant_shift,
+ f0method,
+ )
+ shift = block_frame_16k // 160
+ self.cache_pitch[:-shift] = self.cache_pitch[shift:].clone()
+ self.cache_pitchf[:-shift] = self.cache_pitchf[shift:].clone()
+ self.cache_pitch[4 - pitch.shape[0] :] = pitch[3:-1]
+ self.cache_pitchf[4 - pitch.shape[0] :] = pitchf[3:-1]
+ cache_pitch = self.cache_pitch[None, -p_len:]
+ cache_pitchf = self.cache_pitchf[None, -p_len:] * return_length2 / return_length
+ t4 = ttime()
+ feats = F.interpolate(feats.permute(0, 2, 1), scale_factor=2).permute(0, 2, 1)
+ feats = feats[:, :p_len, :]
+ p_len = torch.LongTensor([p_len]).to(self.device)
+ sid = torch.LongTensor([0]).to(self.device)
+ skip_head = torch.LongTensor([skip_head])
+ return_length2 = torch.LongTensor([return_length2])
+ return_length = torch.LongTensor([return_length])
+ with torch.no_grad():
+ if self.if_f0 == 1:
+ infered_audio, _, _ = self.net_g.infer(
+ feats,
+ p_len,
+ cache_pitch,
+ cache_pitchf,
+ sid,
+ skip_head,
+ return_length,
+ return_length2,
+ )
+ else:
+ infered_audio, _, _ = self.net_g.infer(
+ feats, p_len, sid, skip_head, return_length, return_length2
+ )
+ infered_audio = infered_audio.squeeze(1).float()
+ upp_res = int(np.floor(factor * self.tgt_sr // 100))
+ if upp_res != self.tgt_sr // 100:
+ if upp_res not in self.resample_kernel:
+ self.resample_kernel[upp_res] = Resample(
+ orig_freq=upp_res,
+ new_freq=self.tgt_sr // 100,
+ dtype=torch.float32,
+ ).to(self.device)
+ infered_audio = self.resample_kernel[upp_res](
+ infered_audio[:, : return_length * upp_res]
+ )
+ t5 = ttime()
+ printt(
+ i18n("耗时:特征=%.3f秒,索引=%.3f秒,音高=%.3f秒,模型=%.3f秒"),
+ t2 - t1,
+ t3 - t2,
+ t4 - t3,
+ t5 - t4,
+ )
+ return infered_audio.squeeze()
diff --git a/infer/vc/__init__.py b/infer/vc/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/infer/vc/modules.py b/infer/vc/modules.py
new file mode 100644
index 0000000..54d62ac
--- /dev/null
+++ b/infer/vc/modules.py
@@ -0,0 +1,359 @@
+import traceback
+import logging
+
+logger = logging.getLogger(__name__)
+
+import numpy as np
+import soundfile as sf
+import torch
+from io import BytesIO
+
+from infer.audio import load_audio, wav2
+from infer.module.models import (
+ SynthesizerTrnMs256NSFsid,
+ SynthesizerTrnMs256NSFsid_nono,
+ SynthesizerTrnMs768NSFsid,
+ SynthesizerTrnMs768NSFsid_nono,
+)
+from infer.vc.pipeline import Pipeline
+from infer.vc.utils import *
+from i18n.i18n import I18nAuto
+from tools.progress import batch_status, should_report
+
+
+i18n = I18nAuto()
+
+
+def inference_status(title, state, detail=""):
+ lines = ["【%s】" % i18n(title), "%s:%s" % (i18n("状态"), i18n(state))]
+ if detail:
+ lines.extend(["", str(detail).strip()])
+ return "\n".join(lines)
+
+
+class VC:
+ def __init__(self, config):
+ self.n_spk = None
+ self.tgt_sr = None
+ self.net_g = None
+ self.pipeline = None
+ self.cpt = None
+ self.version = None
+ self.if_f0 = None
+ self.version = None
+ self.hubert_model = None
+
+ self.config = config
+
+ def get_vc(self, sid, *to_return_protect):
+ logger.info("%s: %s", i18n("选择模型"), sid)
+
+ to_return_protect0 = {
+ "visible": self.if_f0 != 0,
+ "value": (
+ to_return_protect[0] if self.if_f0 != 0 and to_return_protect else 0.5
+ ),
+ "__type__": "update",
+ }
+ to_return_protect1 = {
+ "visible": self.if_f0 != 0,
+ "value": (
+ to_return_protect[1] if self.if_f0 != 0 and to_return_protect else 0.33
+ ),
+ "__type__": "update",
+ }
+
+ if sid == "" or sid == []:
+ if (
+ self.hubert_model is not None
+ ): # 考虑到轮询, 需要加个判断看是否 sid 是由有模型切换到无模型的
+ logger.info(i18n("清理模型缓存"))
+ del (self.net_g, self.n_spk, self.hubert_model, self.tgt_sr) # ,cpt
+ self.hubert_model = self.net_g = self.n_spk = self.hubert_model = (
+ self.tgt_sr
+ ) = None
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ ###楼下不这么折腾清理不干净
+ self.if_f0 = self.cpt.get("f0", 1)
+ self.version = self.cpt.get("version", "v1")
+ if self.version == "v1":
+ if self.if_f0 == 1:
+ self.net_g = SynthesizerTrnMs256NSFsid(
+ *self.cpt["config"], is_half=self.config.is_half
+ )
+ else:
+ self.net_g = SynthesizerTrnMs256NSFsid_nono(*self.cpt["config"])
+ elif self.version == "v2":
+ if self.if_f0 == 1:
+ self.net_g = SynthesizerTrnMs768NSFsid(
+ *self.cpt["config"], is_half=self.config.is_half
+ )
+ else:
+ self.net_g = SynthesizerTrnMs768NSFsid_nono(*self.cpt["config"])
+ del self.net_g, self.cpt
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ return (
+ {"visible": False, "__type__": "update"},
+ {
+ "visible": True,
+ "value": to_return_protect0,
+ "__type__": "update",
+ },
+ {
+ "visible": True,
+ "value": to_return_protect1,
+ "__type__": "update",
+ },
+ "",
+ "",
+ )
+ person = f'{os.getenv("weight_root")}/{sid}'
+ logger.info("%s: %s", i18n("正在加载模型"), person)
+
+ self.cpt = torch.load(person, map_location="cpu")
+ self.tgt_sr = self.cpt["config"][-1]
+ self.cpt["config"][-3] = self.cpt["weight"]["emb_g.weight"].shape[0] # n_spk
+ self.if_f0 = self.cpt.get("f0", 1)
+ self.version = self.cpt.get("version", "v1")
+
+ synthesizer_class = {
+ ("v1", 1): SynthesizerTrnMs256NSFsid,
+ ("v1", 0): SynthesizerTrnMs256NSFsid_nono,
+ ("v2", 1): SynthesizerTrnMs768NSFsid,
+ ("v2", 0): SynthesizerTrnMs768NSFsid_nono,
+ }
+
+ self.net_g = synthesizer_class.get(
+ (self.version, self.if_f0), SynthesizerTrnMs256NSFsid
+ )(*self.cpt["config"], is_half=self.config.is_half)
+
+ del self.net_g.enc_q
+
+ self.net_g.load_state_dict(self.cpt["weight"], strict=False)
+ self.net_g.eval().to(self.config.device)
+ if self.config.is_half:
+ self.net_g = self.net_g.half()
+ else:
+ self.net_g = self.net_g.float()
+
+ self.pipeline = Pipeline(self.tgt_sr, self.config)
+ n_spk = self.cpt["config"][-3]
+ index = {"value": get_index_path_from_model(sid), "__type__": "update"}
+ logger.info("%s: %s", i18n("选择索引"), index["value"])
+
+ return (
+ (
+ {"visible": True, "maximum": n_spk, "__type__": "update"},
+ to_return_protect0,
+ to_return_protect1,
+ index,
+ index,
+ )
+ if to_return_protect
+ else {"visible": True, "maximum": n_spk, "__type__": "update"}
+ )
+
+ def vc_single(
+ self,
+ sid,
+ input_audio_path,
+ f0_up_key,
+ f0_method,
+ file_index,
+ index_rate,
+ resample_sr,
+ rms_mix_rate,
+ protect,
+ ):
+ if input_audio_path is None:
+ return inference_status("单次推理", "等待输入", i18n("请上传音频文件")), None
+ f0_up_key = int(f0_up_key)
+ try:
+ audio = load_audio(input_audio_path, 16000)
+ audio_max = np.abs(audio).max() / 0.95
+ if audio_max > 1:
+ audio /= audio_max
+ times = [0, 0, 0]
+
+ if self.hubert_model is None:
+ self.hubert_model = load_hubert(self.config)
+
+ if file_index:
+ file_index = (
+ file_index.strip(" ")
+ .strip('"')
+ .strip("\n")
+ .strip('"')
+ .strip(" ")
+ .replace("trained", "added")
+ )
+ else:
+ file_index = "" # 防止小白写错,自动帮他替换掉
+
+ audio_opt = self.pipeline.pipeline(
+ self.hubert_model,
+ self.net_g,
+ sid,
+ audio,
+ times,
+ f0_up_key,
+ f0_method,
+ file_index,
+ index_rate,
+ self.if_f0,
+ self.tgt_sr,
+ resample_sr,
+ rms_mix_rate,
+ self.version,
+ protect,
+ )
+ if self.tgt_sr != resample_sr >= 16000:
+ tgt_sr = resample_sr
+ else:
+ tgt_sr = self.tgt_sr
+ index_info = (
+ "%s:%s" % (i18n("索引"), file_index)
+ if os.path.exists(file_index)
+ else "%s:%s" % (i18n("索引"), i18n("未使用"))
+ )
+ return (
+ inference_status(
+ "单次推理",
+ "成功",
+ "%s\n%s:%s %.2fs | F0 %.2fs | %s %.2fs"
+ % (
+ index_info,
+ i18n("耗时"),
+ i18n("特征"),
+ times[0],
+ times[1],
+ i18n("合成"),
+ times[2],
+ ),
+ ),
+ (tgt_sr, audio_opt),
+ )
+ except Exception:
+ info = traceback.format_exc()
+ logger.warning(info)
+ return inference_status("单次推理", "失败", info), (None, None)
+
+ def vc_multi(
+ self,
+ sid,
+ dir_path,
+ opt_root,
+ paths,
+ f0_up_key,
+ f0_method,
+ file_index,
+ index_rate,
+ resample_sr,
+ rms_mix_rate,
+ protect,
+ format1,
+ ):
+ try:
+ dir_path = (
+ (dir_path or "")
+ .strip(" ")
+ .strip('"')
+ .strip("\n")
+ .strip('"')
+ .strip(" ")
+ ) # 防止小白拷路径头尾带了空格和"和回车
+ opt_root = (
+ (opt_root or "")
+ .strip(" ")
+ .strip('"')
+ .strip("\n")
+ .strip('"')
+ .strip(" ")
+ )
+ if not opt_root:
+ yield inference_status(
+ "批量推理", "等待输入", i18n("请填写输出文件夹路径")
+ )
+ return
+ os.makedirs(opt_root, exist_ok=True)
+ try:
+ if dir_path != "":
+ paths = [
+ os.path.join(dir_path, name) for name in os.listdir(dir_path)
+ ]
+ else:
+ paths = [path if isinstance(path, str) else path.name for path in (paths or [])]
+ except Exception:
+ traceback.print_exc()
+ paths = [
+ path if isinstance(path, str) else path.name for path in (paths or [])
+ ]
+ total = len(paths)
+ if total == 0:
+ yield batch_status(i18n("批量推理"), 0, 0, 0, 0)
+ return
+ success = 0
+ failed = 0
+ failures = []
+ for idx, path in enumerate(paths):
+ item_failed = False
+ info, opt = self.vc_single(
+ sid,
+ path,
+ f0_up_key,
+ f0_method,
+ file_index,
+ index_rate,
+ resample_sr,
+ rms_mix_rate,
+ protect,
+ )
+ if opt and opt[0] is not None and opt[1] is not None:
+ try:
+ tgt_sr, audio_opt = opt
+ if format1 in ["wav", "flac"]:
+ sf.write(
+ "%s/%s.%s"
+ % (
+ opt_root,
+ os.path.splitext(os.path.basename(path))[0],
+ format1,
+ ),
+ audio_opt,
+ tgt_sr,
+ )
+ else:
+ path = "%s/%s.%s" % (
+ opt_root,
+ os.path.splitext(os.path.basename(path))[0],
+ format1,
+ )
+ with BytesIO() as wavf:
+ sf.write(wavf, audio_opt, tgt_sr, format="wav")
+ wavf.seek(0, 0)
+ with open(path, "wb") as outf:
+ wav2(wavf, outf, format1)
+ success += 1
+ except Exception:
+ info = "%s\n%s" % (info, traceback.format_exc())
+ failed += 1
+ item_failed = True
+ failures.append("%s:%s" % (os.path.basename(path), info))
+ else:
+ failed += 1
+ item_failed = True
+ failures.append("%s:%s" % (os.path.basename(path), info))
+ if should_report(idx, total) or item_failed:
+ yield batch_status(
+ i18n("批量推理"),
+ idx + 1,
+ total,
+ success,
+ failed,
+ os.path.basename(path),
+ failures,
+ )
+ except Exception:
+ yield inference_status("批量推理", "失败", traceback.format_exc())
diff --git a/infer/vc/pipeline.py b/infer/vc/pipeline.py
new file mode 100644
index 0000000..6ba32dc
--- /dev/null
+++ b/infer/vc/pipeline.py
@@ -0,0 +1,386 @@
+import os
+import traceback
+import logging
+
+logger = logging.getLogger(__name__)
+
+from time import time as ttime
+
+import faiss
+import librosa
+import numpy as np
+import parselmouth
+import torch
+import torch.nn.functional as F
+from scipy import signal
+
+from infer.hubert import extract_hubert_features
+
+bh, ah = signal.butter(N=5, Wn=48, btype="high", fs=16000)
+
+
+def change_rms(data1, sr1, data2, sr2, rate): # 1是输入音频,2是输出音频,rate是2的占比
+ # print(data1.max(),data2.max())
+ rms1 = librosa.feature.rms(
+ y=data1, frame_length=sr1 // 2 * 2, hop_length=sr1 // 2
+ ) # 每半秒一个点
+ rms2 = librosa.feature.rms(y=data2, frame_length=sr2 // 2 * 2, hop_length=sr2 // 2)
+ rms1 = torch.from_numpy(rms1)
+ rms1 = F.interpolate(
+ rms1.unsqueeze(0), size=data2.shape[0], mode="linear"
+ ).squeeze()
+ rms2 = torch.from_numpy(rms2)
+ rms2 = F.interpolate(
+ rms2.unsqueeze(0), size=data2.shape[0], mode="linear"
+ ).squeeze()
+ rms2 = torch.max(rms2, torch.zeros_like(rms2) + 1e-6)
+ data2 *= (
+ torch.pow(rms1, torch.tensor(1 - rate))
+ * torch.pow(rms2, torch.tensor(rate - 1))
+ ).numpy()
+ return data2
+
+
+class Pipeline(object):
+ def __init__(self, tgt_sr, config):
+ self.x_pad, self.x_query, self.x_center, self.x_max, self.is_half = (
+ config.x_pad,
+ config.x_query,
+ config.x_center,
+ config.x_max,
+ config.is_half,
+ )
+ self.sr = 16000 # hubert输入采样率
+ self.window = 160 # 每帧点数
+ self.t_pad = self.sr * self.x_pad # 每条前后pad时间
+ self.t_pad_tgt = tgt_sr * self.x_pad
+ self.t_pad2 = self.t_pad * 2
+ self.t_query = self.sr * self.x_query # 查询切点前后查询时间
+ self.t_center = self.sr * self.x_center # 查询切点位置
+ self.t_max = self.sr * self.x_max # 免查询时长阈值
+ self.device = config.device
+
+ def get_f0(
+ self,
+ x,
+ p_len,
+ f0_up_key,
+ f0_method,
+ ):
+ if f0_method not in ("pm", "rmvpe", "fcpe"):
+ raise ValueError(f"Unsupported F0 method: {f0_method}")
+ time_step = self.window / self.sr * 1000
+ f0_min = 50
+ f0_max = 1100
+ f0_mel_min = 1127 * np.log(1 + f0_min / 700)
+ f0_mel_max = 1127 * np.log(1 + f0_max / 700)
+ if f0_method == "pm":
+ f0 = (
+ parselmouth.Sound(x, self.sr)
+ .to_pitch_ac(
+ time_step=time_step / 1000,
+ voicing_threshold=0.6,
+ pitch_floor=f0_min,
+ pitch_ceiling=f0_max,
+ )
+ .selected_array["frequency"]
+ )
+ pad_size = (p_len - len(f0) + 1) // 2
+ if pad_size > 0 or p_len - len(f0) - pad_size > 0:
+ f0 = np.pad(
+ f0, [[pad_size, p_len - len(f0) - pad_size]], mode="constant"
+ )
+ elif f0_method == "rmvpe":
+ if not hasattr(self, "model_rmvpe"):
+ from infer.rmvpe import RMVPE
+
+ logger.info(
+ "Loading rmvpe model,%s" % "%s/rmvpe.pt" % os.environ["rmvpe_root"]
+ )
+ self.model_rmvpe = RMVPE(
+ "%s/rmvpe.pt" % os.environ["rmvpe_root"],
+ is_half=self.is_half,
+ device=self.device,
+ )
+ f0 = self.model_rmvpe.infer_from_audio(x, thred=0.03)
+
+ if "privateuseone" in str(self.device): # clean ortruntime memory
+ del self.model_rmvpe.model
+ del self.model_rmvpe
+ logger.info("Cleaning ortruntime memory")
+ elif f0_method == "fcpe":
+ if not hasattr(self, "model_fcpe"):
+ from infer.fcpe import FCPEInfer
+
+ logger.info("Loading fcpe model")
+ self.model_fcpe = FCPEInfer(self.device)
+ f0 = self.model_fcpe.infer(
+ torch.from_numpy(x).unsqueeze(0).float(),
+ sr=self.sr,
+ decoder_mode="local_argmax",
+ threshold=0.006,
+ ).squeeze().detach().cpu().numpy()
+
+ try:
+ uv = f0 == 0
+ f0[uv] = np.interp(np.where(uv)[0], np.where(~uv)[0], f0[~uv])
+ except Exception:
+ traceback.print_exc()
+ f0 *= pow(2, f0_up_key / 12)
+ f0bak = f0.copy()
+ f0_mel = 1127 * np.log(1 + f0 / 700)
+ f0_mel[f0_mel > 0] = (f0_mel[f0_mel > 0] - f0_mel_min) * 254 / (
+ f0_mel_max - f0_mel_min
+ ) + 1
+ f0_mel[f0_mel <= 1] = 1
+ f0_mel[f0_mel > 255] = 255
+ f0_coarse = np.rint(f0_mel).astype(np.int32)
+ return f0_coarse, f0bak # 1-0
+
+ def vc(
+ self,
+ model,
+ net_g,
+ sid,
+ audio0,
+ pitch,
+ pitchf,
+ times,
+ index,
+ index_vectors,
+ index_rate,
+ version,
+ protect,
+ ):
+ feats = torch.from_numpy(audio0)
+ if self.is_half:
+ feats = feats.half()
+ else:
+ feats = feats.float()
+ if feats.dim() == 2: # double channels
+ feats = feats.mean(-1)
+ assert feats.dim() == 1, feats.dim()
+ feats = feats.view(1, -1)
+ padding_mask = torch.BoolTensor(feats.shape).to(self.device).fill_(False)
+
+ t0 = ttime()
+ with torch.no_grad():
+ feats = extract_hubert_features(
+ model,
+ feats.to(self.device),
+ version,
+ padding_mask=padding_mask,
+ )
+ if protect < 0.5 and pitch is not None and pitchf is not None:
+ feats0 = feats.clone()
+ if (
+ not isinstance(index, type(None))
+ and not isinstance(index_vectors, type(None))
+ and index_rate != 0
+ ):
+ npy = feats[0].cpu().numpy()
+ if self.is_half:
+ npy = npy.astype("float32")
+
+ score, ix = index.search(npy, k=8)
+ weight = np.square(1 / score)
+ weight /= weight.sum(axis=1, keepdims=True)
+ npy = np.sum(index_vectors[ix] * np.expand_dims(weight, axis=2), axis=1)
+
+ if self.is_half:
+ npy = npy.astype("float16")
+ feats = (
+ torch.from_numpy(npy).unsqueeze(0).to(self.device) * index_rate
+ + (1 - index_rate) * feats
+ )
+
+ feats = F.interpolate(feats.permute(0, 2, 1), scale_factor=2).permute(0, 2, 1)
+ if protect < 0.5 and pitch is not None and pitchf is not None:
+ feats0 = F.interpolate(feats0.permute(0, 2, 1), scale_factor=2).permute(
+ 0, 2, 1
+ )
+ t1 = ttime()
+ p_len = audio0.shape[0] // self.window
+ if feats.shape[1] < p_len:
+ p_len = feats.shape[1]
+ if pitch is not None and pitchf is not None:
+ pitch = pitch[:, :p_len]
+ pitchf = pitchf[:, :p_len]
+
+ if protect < 0.5 and pitch is not None and pitchf is not None:
+ pitchff = pitchf.clone()
+ pitchff[pitchf > 0] = 1
+ pitchff[pitchf < 1] = protect
+ pitchff = pitchff.unsqueeze(-1)
+ feats = feats * pitchff + feats0 * (1 - pitchff)
+ feats = feats.to(feats0.dtype)
+ p_len = torch.tensor([p_len], device=self.device).long()
+ with torch.no_grad():
+ hasp = pitch is not None and pitchf is not None
+ arg = (feats, p_len, pitch, pitchf, sid) if hasp else (feats, p_len, sid)
+ audio1 = (net_g.infer(*arg)[0][0, 0]).data.cpu().float().numpy()
+ del hasp, arg
+ del feats, p_len, padding_mask
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ t2 = ttime()
+ times[0] += t1 - t0
+ times[2] += t2 - t1
+ return audio1
+
+ def pipeline(
+ self,
+ model,
+ net_g,
+ sid,
+ audio,
+ times,
+ f0_up_key,
+ f0_method,
+ file_index,
+ index_rate,
+ if_f0,
+ tgt_sr,
+ resample_sr,
+ rms_mix_rate,
+ version,
+ protect,
+ ):
+ if (
+ file_index != ""
+ and os.path.exists(file_index)
+ and index_rate != 0
+ ):
+ try:
+ index = faiss.read_index(file_index)
+ index_vectors = index.reconstruct_n(0, index.ntotal)
+ except:
+ traceback.print_exc()
+ index = index_vectors = None
+ else:
+ index = index_vectors = None
+ audio = signal.filtfilt(bh, ah, audio)
+ audio_pad = np.pad(audio, (self.window // 2, self.window // 2), mode="reflect")
+ opt_ts = []
+ if audio_pad.shape[0] > self.t_max:
+ audio_sum = np.zeros_like(audio)
+ for i in range(self.window):
+ audio_sum += np.abs(audio_pad[i : i - self.window])
+ for t in range(self.t_center, audio.shape[0], self.t_center):
+ opt_ts.append(
+ t
+ - self.t_query
+ + np.where(
+ audio_sum[t - self.t_query : t + self.t_query]
+ == audio_sum[t - self.t_query : t + self.t_query].min()
+ )[0][0]
+ )
+ s = 0
+ audio_opt = []
+ t = None
+ t1 = ttime()
+ audio_pad = np.pad(audio, (self.t_pad, self.t_pad), mode="reflect")
+ p_len = audio_pad.shape[0] // self.window
+ sid = torch.tensor(sid, device=self.device).unsqueeze(0).long()
+ pitch, pitchf = None, None
+ if if_f0 == 1:
+ pitch, pitchf = self.get_f0(
+ audio_pad,
+ p_len,
+ f0_up_key,
+ f0_method,
+ )
+ pitch = pitch[:p_len]
+ pitchf = pitchf[:p_len]
+ pitchf = pitchf.astype(np.float32)
+ pitch = torch.tensor(pitch, device=self.device).unsqueeze(0).long()
+ pitchf = torch.tensor(pitchf, device=self.device).unsqueeze(0).float()
+ t2 = ttime()
+ times[1] += t2 - t1
+ for t in opt_ts:
+ t = t // self.window * self.window
+ if if_f0 == 1:
+ audio_opt.append(
+ self.vc(
+ model,
+ net_g,
+ sid,
+ audio_pad[s : t + self.t_pad2 + self.window],
+ pitch[:, s // self.window : (t + self.t_pad2) // self.window],
+ pitchf[:, s // self.window : (t + self.t_pad2) // self.window],
+ times,
+ index,
+ index_vectors,
+ index_rate,
+ version,
+ protect,
+ )[self.t_pad_tgt : -self.t_pad_tgt]
+ )
+ else:
+ audio_opt.append(
+ self.vc(
+ model,
+ net_g,
+ sid,
+ audio_pad[s : t + self.t_pad2 + self.window],
+ None,
+ None,
+ times,
+ index,
+ index_vectors,
+ index_rate,
+ version,
+ protect,
+ )[self.t_pad_tgt : -self.t_pad_tgt]
+ )
+ s = t
+ if if_f0 == 1:
+ audio_opt.append(
+ self.vc(
+ model,
+ net_g,
+ sid,
+ audio_pad[t:],
+ pitch[:, t // self.window :] if t is not None else pitch,
+ pitchf[:, t // self.window :] if t is not None else pitchf,
+ times,
+ index,
+ index_vectors,
+ index_rate,
+ version,
+ protect,
+ )[self.t_pad_tgt : -self.t_pad_tgt]
+ )
+ else:
+ audio_opt.append(
+ self.vc(
+ model,
+ net_g,
+ sid,
+ audio_pad[t:],
+ None,
+ None,
+ times,
+ index,
+ index_vectors,
+ index_rate,
+ version,
+ protect,
+ )[self.t_pad_tgt : -self.t_pad_tgt]
+ )
+ audio_opt = np.concatenate(audio_opt)
+ if rms_mix_rate != 1:
+ audio_opt = change_rms(audio, 16000, audio_opt, tgt_sr, rms_mix_rate)
+ if tgt_sr != resample_sr >= 16000:
+ audio_opt = librosa.resample(
+ audio_opt, orig_sr=tgt_sr, target_sr=resample_sr
+ )
+ audio_max = np.abs(audio_opt).max() / 0.99
+ max_int16 = 32768
+ if audio_max > 1:
+ max_int16 /= audio_max
+ audio_opt = (audio_opt * max_int16).astype(np.int16)
+ del pitch, pitchf, sid
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ return audio_opt
diff --git a/infer/vc/utils.py b/infer/vc/utils.py
new file mode 100644
index 0000000..9ab33c5
--- /dev/null
+++ b/infer/vc/utils.py
@@ -0,0 +1,44 @@
+import os
+import re
+
+from infer.hubert import load_hubert_model
+
+
+def get_index_path_from_model(sid):
+ model_stem = os.path.splitext(os.path.basename(str(sid or "")))[0]
+ experiment_name = re.sub(r"_e\d+_s\d+$", "", model_stem, flags=re.IGNORECASE)
+ if not experiment_name:
+ return ""
+
+ candidates = []
+ roots = [os.getenv("outside_index_root"), os.getenv("index_root")]
+ for index_root in roots:
+ if not index_root or not os.path.isdir(index_root):
+ continue
+ for root, _, files in os.walk(index_root, topdown=False):
+ for name in files:
+ if not name.lower().endswith(".index") or "trained" in name.lower():
+ continue
+ index_stem = os.path.splitext(name)[0]
+ lower_index = index_stem.lower()
+ lower_experiment = experiment_name.lower()
+ standard_match = (
+ lower_index.startswith(lower_experiment + "_added_")
+ or ("_" + lower_experiment + "_v1") in lower_index
+ or ("_" + lower_experiment + "_v2") in lower_index
+ )
+ exact_model_match = model_stem.lower() in lower_index
+ if standard_match or exact_model_match:
+ path = os.path.abspath(os.path.join(root, name))
+ score = (
+ 0 if standard_match else 1,
+ 0 if os.path.abspath(index_root) == os.path.abspath(roots[0]) else 1,
+ -os.path.getmtime(path),
+ path.lower(),
+ )
+ candidates.append((score, path))
+ return min(candidates, default=(None, ""), key=lambda item: item[0])[1]
+
+
+def load_hubert(config):
+ return load_hubert_model(config.device, config.is_half)
diff --git a/realtime_gui.py b/realtime_gui.py
new file mode 100644
index 0000000..2efe967
--- /dev/null
+++ b/realtime_gui.py
@@ -0,0 +1,918 @@
+import os
+import sys
+
+now_dir = os.path.dirname(os.path.abspath(__file__))
+
+from tools.file_io import read_text
+
+os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
+
+os.environ["OMP_NUM_THREADS"] = "4"
+
+realtime_config_path = os.path.join(now_dir, "configs", "config.json")
+
+flag_vc = False
+
+
+def printt(strr, *args):
+ if len(args) == 0:
+ print(strr)
+ else:
+ print(strr % args)
+
+
+if __name__ == "__main__":
+ import json
+ import re
+ import time
+ import traceback
+
+ import librosa
+ from tools.torchgate import TorchGate
+ import numpy as np
+ import FreeSimpleGUI as sg
+ import sounddevice as sd
+ import torch
+ import torch.nn.functional as F
+ import torchaudio.transforms as tat
+
+ from infer import rtrvc as rvc_for_realtime
+ from i18n.i18n import I18nAuto
+ from configs.config import Config
+
+ i18n = I18nAuto()
+
+ class GUIConfig:
+ def __init__(self) :
+ self.pth_path = ""
+ self.index_path = ""
+ self.pitch = 0
+ self.formant=0.0
+ self.sr_type = "sr_model"
+ self.block_time = 0.25 # s
+ self.threhold = -60
+ self.crossfade_time = 0.05
+ self.extra_time = 2.5
+ self.I_noise_reduce = False
+ self.O_noise_reduce = False
+ self.rms_mix_rate = 0.0
+ self.index_rate = 0.0
+ self.f0method = "rmvpe"
+ self.sg_hostapi = ""
+ self.wasapi_exclusive = False
+ self.sg_input_device = ""
+ self.sg_output_device = ""
+
+ class GUI:
+ def __init__(self) :
+ self.gui_config = GUIConfig()
+ self.config = Config()
+ self.function = "vc"
+ self.delay_time = 0
+ self.hostapis = None
+ self.input_devices = None
+ self.output_devices = None
+ self.input_devices_indices = None
+ self.output_devices_indices = None
+ self.stream = None
+ self.update_devices()
+ self.launcher()
+
+ def load(self):
+ try:
+ data = json.loads(read_text(realtime_config_path))
+ data["sr_model"] = data["sr_type"] == "sr_model"
+ data["sr_device"] = data["sr_type"] == "sr_device"
+ if data.get("f0method") not in ("pm", "rmvpe", "fcpe"):
+ data["f0method"] = "rmvpe"
+ data["pm"] = data["f0method"] == "pm"
+ data["rmvpe"] = data["f0method"] == "rmvpe"
+ data["fcpe"] = data["f0method"] == "fcpe"
+ if data["sg_hostapi"] in self.hostapis:
+ self.update_devices(hostapi_name=data["sg_hostapi"])
+ if (
+ data["sg_input_device"] not in self.input_devices
+ or data["sg_output_device"] not in self.output_devices
+ ):
+ self.update_devices()
+ data["sg_hostapi"] = self.hostapis[0]
+ data["sg_input_device"] = self.input_devices[
+ self.input_devices_indices.index(sd.default.device[0])
+ ]
+ data["sg_output_device"] = self.output_devices[
+ self.output_devices_indices.index(sd.default.device[1])
+ ]
+ else:
+ data["sg_hostapi"] = self.hostapis[0]
+ data["sg_input_device"] = self.input_devices[
+ self.input_devices_indices.index(sd.default.device[0])
+ ]
+ data["sg_output_device"] = self.output_devices[
+ self.output_devices_indices.index(sd.default.device[1])
+ ]
+ except:
+ with open(realtime_config_path, "w", encoding="utf8") as j:
+ data = {
+ "pth_path": "",
+ "index_path": "",
+ "sg_hostapi": self.hostapis[0],
+ "sg_wasapi_exclusive": False,
+ "sg_input_device": self.input_devices[
+ self.input_devices_indices.index(sd.default.device[0])
+ ],
+ "sg_output_device": self.output_devices[
+ self.output_devices_indices.index(sd.default.device[1])
+ ],
+ "sr_type": "sr_model",
+ "threhold": -60,
+ "pitch": 0,
+ "formant": 0.0,
+ "index_rate": 0,
+ "rms_mix_rate": 0,
+ "block_time": 0.25,
+ "crossfade_length": 0.05,
+ "extra_time": 2.5,
+ "f0method": "rmvpe",
+ }
+ data["sr_model"] = data["sr_type"] == "sr_model"
+ data["sr_device"] = data["sr_type"] == "sr_device"
+ data["pm"] = data["f0method"] == "pm"
+ data["rmvpe"] = data["f0method"] == "rmvpe"
+ data["fcpe"] = data["f0method"] == "fcpe"
+ return data
+
+ def launcher(self):
+ data = self.load()
+ sg.theme("LightBlue3")
+ layout = [
+ [
+ sg.Frame(
+ title=i18n("加载模型"),
+ layout=[
+ [
+ sg.Input(
+ default_text=data.get("pth_path", ""),
+ key="pth_path",
+ ),
+ sg.FileBrowse(
+ i18n("选择.pth文件"),
+ initial_folder=os.path.join(
+ os.getcwd(), "assets/weights"
+ ),
+ file_types=((". pth"),),
+ ),
+ ],
+ [
+ sg.Input(
+ default_text=data.get("index_path", ""),
+ key="index_path",
+ ),
+ sg.FileBrowse(
+ i18n("选择.index文件"),
+ initial_folder=os.path.join(os.getcwd(), "logs"),
+ file_types=((". index"),),
+ ),
+ ],
+ ],
+ )
+ ],
+ [
+ sg.Frame(
+ layout=[
+ [
+ sg.Text(i18n("设备类型")),
+ sg.Combo(
+ self.hostapis,
+ key="sg_hostapi",
+ default_value=data.get("sg_hostapi", ""),
+ enable_events=True,
+ size=(20, 1),
+ ),
+ sg.Checkbox(
+ i18n("独占 WASAPI 设备"),
+ key="sg_wasapi_exclusive",
+ default=data.get("sg_wasapi_exclusive", False),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Text(i18n("输入设备")),
+ sg.Combo(
+ self.input_devices,
+ key="sg_input_device",
+ default_value=data.get("sg_input_device", ""),
+ enable_events=True,
+ size=(45, 1),
+ ),
+ ],
+ [
+ sg.Text(i18n("输出设备")),
+ sg.Combo(
+ self.output_devices,
+ key="sg_output_device",
+ default_value=data.get("sg_output_device", ""),
+ enable_events=True,
+ size=(45, 1),
+ ),
+ ],
+ [
+ sg.Button(i18n("重载设备列表"), key="reload_devices"),
+ sg.Radio(
+ i18n("使用模型采样率"),
+ "sr_type",
+ key="sr_model",
+ default=data.get("sr_model", True),
+ enable_events=True,
+ ),
+ sg.Radio(
+ i18n("使用设备采样率"),
+ "sr_type",
+ key="sr_device",
+ default=data.get("sr_device", False),
+ enable_events=True,
+ ),
+ sg.Text(i18n("采样率:")),
+ sg.Text("", key="sr_stream"),
+ ],
+ ],
+ title=i18n("音频设备"),
+ )
+ ],
+ [
+ sg.Frame(
+ layout=[
+ [
+ sg.Text(i18n("响应阈值")),
+ sg.Slider(
+ range=(-60, 0),
+ key="threhold",
+ resolution=1,
+ orientation="h",
+ default_value=data.get("threhold", -60),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Text(i18n("音调设置")),
+ sg.Slider(
+ range=(-16, 16),
+ key="pitch",
+ resolution=1,
+ orientation="h",
+ default_value=data.get("pitch", 0),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Text(i18n("性别因子/声线粗细")),
+ sg.Slider(
+ range=(-2, 2),
+ key="formant",
+ resolution=0.05,
+ orientation="h",
+ default_value=data.get("formant", 0.0),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Text(i18n("Index Rate")),
+ sg.Slider(
+ range=(0.0, 1.0),
+ key="index_rate",
+ resolution=0.01,
+ orientation="h",
+ default_value=data.get("index_rate", 0),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Text(i18n("响度因子")),
+ sg.Slider(
+ range=(0.0, 1.0),
+ key="rms_mix_rate",
+ resolution=0.01,
+ orientation="h",
+ default_value=data.get("rms_mix_rate", 0),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Text(i18n("音高算法")),
+ sg.Radio(
+ "pm",
+ "f0method",
+ key="pm",
+ default=data.get("pm", False),
+ enable_events=True,
+ ),
+ sg.Radio(
+ "rmvpe",
+ "f0method",
+ key="rmvpe",
+ default=data.get("rmvpe", True),
+ enable_events=True,
+ ),
+ sg.Radio(
+ "fcpe",
+ "f0method",
+ key="fcpe",
+ default=data.get("fcpe", False),
+ enable_events=True,
+ ),
+ ],
+ ],
+ title=i18n("常规设置"),
+ ),
+ sg.Frame(
+ layout=[
+ [
+ sg.Text(i18n("采样长度")),
+ sg.Slider(
+ range=(0.02, 1.5),
+ key="block_time",
+ resolution=0.01,
+ orientation="h",
+ default_value=data.get("block_time", 0.25),
+ enable_events=True,
+ ),
+ ],
+ # [
+ # sg.Text("设备延迟"),
+ # sg.Slider(
+ # range=(0, 1),
+ # key="device_latency",
+ # resolution=0.001,
+ # orientation="h",
+ # default_value=data.get("device_latency", 0.1),
+ # enable_events=True,
+ # ),
+ # ],
+ [
+ sg.Text(i18n("淡入淡出长度")),
+ sg.Slider(
+ range=(0.01, 0.15),
+ key="crossfade_length",
+ resolution=0.01,
+ orientation="h",
+ default_value=data.get("crossfade_length", 0.05),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Text(i18n("额外推理时长")),
+ sg.Slider(
+ range=(0.05, 5.00),
+ key="extra_time",
+ resolution=0.01,
+ orientation="h",
+ default_value=data.get("extra_time", 2.5),
+ enable_events=True,
+ ),
+ ],
+ [
+ sg.Checkbox(
+ i18n("输入降噪"),
+ key="I_noise_reduce",
+ enable_events=True,
+ ),
+ sg.Checkbox(
+ i18n("输出降噪"),
+ key="O_noise_reduce",
+ enable_events=True,
+ ),
+ ],
+ ],
+ title=i18n("性能设置"),
+ ),
+ ],
+ [
+ sg.Button(i18n("开始音频转换"), key="start_vc"),
+ sg.Button(i18n("停止音频转换"), key="stop_vc"),
+ sg.Radio(
+ i18n("输入监听"),
+ "function",
+ key="im",
+ default=False,
+ enable_events=True,
+ ),
+ sg.Radio(
+ i18n("输出变声"),
+ "function",
+ key="vc",
+ default=True,
+ enable_events=True,
+ ),
+ sg.Text(i18n("算法延迟(ms):")),
+ sg.Text("0", key="delay_time"),
+ sg.Text(i18n("推理时间(ms):")),
+ sg.Text("0", key="infer_time"),
+ ],
+ ]
+ self.window = sg.Window("RVC - GUI", layout=layout, finalize=True)
+ self.event_handler()
+
+ def event_handler(self):
+ global flag_vc
+ while True:
+ event, values = self.window.read()
+ if event == sg.WINDOW_CLOSED:
+ self.stop_stream()
+ exit()
+ if event == "reload_devices" or event == "sg_hostapi":
+ self.gui_config.sg_hostapi = values["sg_hostapi"]
+ self.update_devices(hostapi_name=values["sg_hostapi"])
+ if self.gui_config.sg_hostapi not in self.hostapis:
+ self.gui_config.sg_hostapi = self.hostapis[0]
+ self.window["sg_hostapi"].Update(values=self.hostapis)
+ self.window["sg_hostapi"].Update(value=self.gui_config.sg_hostapi)
+ if (
+ self.gui_config.sg_input_device not in self.input_devices
+ and len(self.input_devices) > 0
+ ):
+ self.gui_config.sg_input_device = self.input_devices[0]
+ self.window["sg_input_device"].Update(values=self.input_devices)
+ self.window["sg_input_device"].Update(
+ value=self.gui_config.sg_input_device
+ )
+ if self.gui_config.sg_output_device not in self.output_devices:
+ self.gui_config.sg_output_device = self.output_devices[0]
+ self.window["sg_output_device"].Update(values=self.output_devices)
+ self.window["sg_output_device"].Update(
+ value=self.gui_config.sg_output_device
+ )
+ if event == "start_vc" and not flag_vc:
+ if self.set_values(values) == True:
+ printt(i18n("CUDA可用:%s"), torch.cuda.is_available())
+ self.start_vc()
+ settings = {
+ "pth_path": values["pth_path"],
+ "index_path": values["index_path"],
+ "sg_hostapi": values["sg_hostapi"],
+ "sg_wasapi_exclusive": values["sg_wasapi_exclusive"],
+ "sg_input_device": values["sg_input_device"],
+ "sg_output_device": values["sg_output_device"],
+ "sr_type": ["sr_model", "sr_device"][
+ [
+ values["sr_model"],
+ values["sr_device"],
+ ].index(True)
+ ],
+ "threhold": values["threhold"],
+ "pitch": values["pitch"],
+ "rms_mix_rate": values["rms_mix_rate"],
+ "index_rate": values["index_rate"],
+ # "device_latency": values["device_latency"],
+ "block_time": values["block_time"],
+ "crossfade_length": values["crossfade_length"],
+ "extra_time": values["extra_time"],
+ "f0method": ["pm", "rmvpe", "fcpe"][
+ [values["pm"], values["rmvpe"], values["fcpe"]].index(True)
+ ],
+ }
+ with open(realtime_config_path, "w", encoding="utf8") as j:
+ json.dump(settings, j)
+ if self.stream is not None:
+ self.delay_time = (
+ self.stream.latency[-1]
+ + values["block_time"]
+ + values["crossfade_length"]
+ + 0.01
+ )
+ if values["I_noise_reduce"]:
+ self.delay_time += min(values["crossfade_length"], 0.04)
+ self.window["sr_stream"].update(self.gui_config.samplerate)
+ self.window["delay_time"].update(
+ int(np.round(self.delay_time * 1000))
+ )
+ # Parameter hot update
+ if event == "threhold":
+ self.gui_config.threhold = values["threhold"]
+ elif event == "pitch":
+ self.gui_config.pitch = values["pitch"]
+ if hasattr(self, "rvc"):
+ self.rvc.change_key(values["pitch"])
+ elif event == "formant":
+ self.gui_config.formant = values["formant"]
+ if hasattr(self, "rvc"):
+ self.rvc.change_formant(values["formant"])
+ elif event == "index_rate":
+ self.gui_config.index_rate = values["index_rate"]
+ if hasattr(self, "rvc"):
+ self.rvc.change_index_rate(values["index_rate"])
+ elif event == "rms_mix_rate":
+ self.gui_config.rms_mix_rate = values["rms_mix_rate"]
+ elif event in ["pm", "rmvpe", "fcpe"]:
+ self.gui_config.f0method = event
+ elif event == "I_noise_reduce":
+ self.gui_config.I_noise_reduce = values["I_noise_reduce"]
+ if self.stream is not None:
+ self.delay_time += (
+ 1 if values["I_noise_reduce"] else -1
+ ) * min(values["crossfade_length"], 0.04)
+ self.window["delay_time"].update(
+ int(np.round(self.delay_time * 1000))
+ )
+ elif event == "O_noise_reduce":
+ self.gui_config.O_noise_reduce = values["O_noise_reduce"]
+ elif event in ["vc", "im"]:
+ self.function = event
+ elif event == "stop_vc" or event != "start_vc":
+ # Other parameters do not support hot update
+ self.stop_stream()
+
+ def set_values(self, values):
+ if len(values["pth_path"].strip()) == 0:
+ sg.popup(i18n("请选择pth文件"))
+ return False
+ if len(values["index_path"].strip()) == 0:
+ sg.popup(i18n("请选择index文件"))
+ return False
+ pattern = re.compile("[^\x00-\x7F]+")
+ if pattern.findall(values["pth_path"]):
+ sg.popup(i18n("pth文件路径不可包含中文"))
+ return False
+ if pattern.findall(values["index_path"]):
+ sg.popup(i18n("index文件路径不可包含中文"))
+ return False
+ self.set_devices(values["sg_input_device"], values["sg_output_device"])
+ # self.device_latency = values["device_latency"]
+ self.gui_config.sg_hostapi = values["sg_hostapi"]
+ self.gui_config.sg_wasapi_exclusive = values["sg_wasapi_exclusive"]
+ self.gui_config.sg_input_device = values["sg_input_device"]
+ self.gui_config.sg_output_device = values["sg_output_device"]
+ self.gui_config.pth_path = values["pth_path"]
+ self.gui_config.index_path = values["index_path"]
+ self.gui_config.sr_type = ["sr_model", "sr_device"][
+ [
+ values["sr_model"],
+ values["sr_device"],
+ ].index(True)
+ ]
+ self.gui_config.threhold = values["threhold"]
+ self.gui_config.pitch = values["pitch"]
+ self.gui_config.formant = values["formant"]
+ self.gui_config.block_time = values["block_time"]
+ self.gui_config.crossfade_time = values["crossfade_length"]
+ self.gui_config.extra_time = values["extra_time"]
+ self.gui_config.I_noise_reduce = values["I_noise_reduce"]
+ self.gui_config.O_noise_reduce = values["O_noise_reduce"]
+ self.gui_config.rms_mix_rate = values["rms_mix_rate"]
+ self.gui_config.index_rate = values["index_rate"]
+ self.gui_config.f0method = ["pm", "rmvpe", "fcpe"][
+ [values["pm"], values["rmvpe"], values["fcpe"]].index(True)
+ ]
+ return True
+
+ def start_vc(self):
+ torch.cuda.empty_cache()
+ self.rvc = rvc_for_realtime.RVC(
+ self.gui_config.pitch,
+ self.gui_config.formant,
+ self.gui_config.pth_path,
+ self.gui_config.index_path,
+ self.gui_config.index_rate,
+ self.config,
+ self.rvc if hasattr(self, "rvc") else None,
+ )
+ self.gui_config.samplerate = (
+ self.rvc.tgt_sr
+ if self.gui_config.sr_type == "sr_model"
+ else self.get_device_samplerate()
+ )
+ self.gui_config.channels = self.get_device_channels()
+ self.zc = self.gui_config.samplerate // 100
+ self.block_frame = (
+ int(
+ np.round(
+ self.gui_config.block_time
+ * self.gui_config.samplerate
+ / self.zc
+ )
+ )
+ * self.zc
+ )
+ self.block_frame_16k = 160 * self.block_frame // self.zc
+ self.crossfade_frame = (
+ int(
+ np.round(
+ self.gui_config.crossfade_time
+ * self.gui_config.samplerate
+ / self.zc
+ )
+ )
+ * self.zc
+ )
+ self.sola_buffer_frame = min(self.crossfade_frame, 4 * self.zc)
+ self.sola_search_frame = self.zc
+ self.extra_frame = (
+ int(
+ np.round(
+ self.gui_config.extra_time
+ * self.gui_config.samplerate
+ / self.zc
+ )
+ )
+ * self.zc
+ )
+ self.input_wav = torch.zeros(
+ self.extra_frame
+ + self.crossfade_frame
+ + self.sola_search_frame
+ + self.block_frame,
+ device=self.config.device,
+ dtype=torch.float32,
+ )
+ self.input_wav_denoise = self.input_wav.clone()
+ self.input_wav_res = torch.zeros(
+ 160 * self.input_wav.shape[0] // self.zc,
+ device=self.config.device,
+ dtype=torch.float32,
+ )
+ self.rms_buffer = np.zeros(4 * self.zc, dtype="float32")
+ self.sola_buffer = torch.zeros(
+ self.sola_buffer_frame, device=self.config.device, dtype=torch.float32
+ )
+ self.nr_buffer = self.sola_buffer.clone()
+ self.output_buffer = self.input_wav.clone()
+ self.skip_head = self.extra_frame // self.zc
+ self.return_length = (
+ self.block_frame + self.sola_buffer_frame + self.sola_search_frame
+ ) // self.zc
+ self.fade_in_window = (
+ torch.sin(
+ 0.5
+ * np.pi
+ * torch.linspace(
+ 0.0,
+ 1.0,
+ steps=self.sola_buffer_frame,
+ device=self.config.device,
+ dtype=torch.float32,
+ )
+ )
+ ** 2
+ )
+ self.fade_out_window = 1 - self.fade_in_window
+ self.resampler = tat.Resample(
+ orig_freq=self.gui_config.samplerate,
+ new_freq=16000,
+ dtype=torch.float32,
+ ).to(self.config.device)
+ if self.rvc.tgt_sr != self.gui_config.samplerate:
+ self.resampler2 = tat.Resample(
+ orig_freq=self.rvc.tgt_sr,
+ new_freq=self.gui_config.samplerate,
+ dtype=torch.float32,
+ ).to(self.config.device)
+ else:
+ self.resampler2 = None
+ self.tg = TorchGate(
+ sr=self.gui_config.samplerate, n_fft=4 * self.zc, prop_decrease=0.9
+ ).to(self.config.device)
+ self.start_stream()
+
+ def start_stream(self):
+ global flag_vc
+ if not flag_vc:
+ flag_vc = True
+ if (
+ "WASAPI" in self.gui_config.sg_hostapi
+ and self.gui_config.sg_wasapi_exclusive
+ ):
+ extra_settings = sd.WasapiSettings(exclusive=True)
+ else:
+ extra_settings = None
+ self.stream = sd.Stream(
+ callback=self.audio_callback,
+ blocksize=self.block_frame,
+ samplerate=self.gui_config.samplerate,
+ channels=self.gui_config.channels,
+ dtype="float32",
+ extra_settings=extra_settings,
+ )
+ self.stream.start()
+
+ def stop_stream(self):
+ global flag_vc
+ if flag_vc:
+ flag_vc = False
+ if self.stream is not None:
+ self.stream.abort()
+ self.stream.close()
+ self.stream = None
+
+ def audio_callback(
+ self, indata, outdata, frames, times, status
+ ):
+ """
+ 音频处理
+ """
+ global flag_vc
+ start_time = time.perf_counter()
+ indata = librosa.to_mono(indata.T)
+ if self.gui_config.threhold > -60:
+ indata = np.append(self.rms_buffer, indata)
+ rms = librosa.feature.rms(
+ y=indata, frame_length=4 * self.zc, hop_length=self.zc
+ )[:, 2:]
+ self.rms_buffer[:] = indata[-4 * self.zc :]
+ indata = indata[2 * self.zc - self.zc // 2 :]
+ db_threhold = (
+ librosa.amplitude_to_db(rms, ref=1.0)[0] < self.gui_config.threhold
+ )
+ for i in range(db_threhold.shape[0]):
+ if db_threhold[i]:
+ indata[i * self.zc : (i + 1) * self.zc] = 0
+ indata = indata[self.zc // 2 :]
+ self.input_wav[: -self.block_frame] = self.input_wav[
+ self.block_frame :
+ ].clone()
+ self.input_wav[-indata.shape[0] :] = torch.from_numpy(indata).to(
+ self.config.device
+ )
+ self.input_wav_res[: -self.block_frame_16k] = self.input_wav_res[
+ self.block_frame_16k :
+ ].clone()
+ # input noise reduction and resampling
+ if self.gui_config.I_noise_reduce:
+ self.input_wav_denoise[: -self.block_frame] = self.input_wav_denoise[
+ self.block_frame :
+ ].clone()
+ input_wav = self.input_wav[-self.sola_buffer_frame - self.block_frame :]
+ input_wav = self.tg(
+ input_wav.unsqueeze(0), self.input_wav.unsqueeze(0)
+ ).squeeze(0)
+ input_wav[: self.sola_buffer_frame] *= self.fade_in_window
+ input_wav[: self.sola_buffer_frame] += (
+ self.nr_buffer * self.fade_out_window
+ )
+ self.input_wav_denoise[-self.block_frame :] = input_wav[
+ : self.block_frame
+ ]
+ self.nr_buffer[:] = input_wav[self.block_frame :]
+ self.input_wav_res[-self.block_frame_16k - 160 :] = self.resampler(
+ self.input_wav_denoise[-self.block_frame - 2 * self.zc :]
+ )[160:]
+ else:
+ self.input_wav_res[-160 * (indata.shape[0] // self.zc + 1) :] = (
+ self.resampler(self.input_wav[-indata.shape[0] - 2 * self.zc :])[
+ 160:
+ ]
+ )
+ # infer
+ if self.function == "vc":
+ infer_wav = self.rvc.infer(
+ self.input_wav_res,
+ self.block_frame_16k,
+ self.skip_head,
+ self.return_length,
+ self.gui_config.f0method,
+ )
+ if self.resampler2 is not None:
+ infer_wav = self.resampler2(infer_wav)
+ elif self.gui_config.I_noise_reduce:
+ infer_wav = self.input_wav_denoise[self.extra_frame :].clone()
+ else:
+ infer_wav = self.input_wav[self.extra_frame :].clone()
+ # output noise reduction
+ if self.gui_config.O_noise_reduce and self.function == "vc":
+ self.output_buffer[: -self.block_frame] = self.output_buffer[
+ self.block_frame :
+ ].clone()
+ self.output_buffer[-self.block_frame :] = infer_wav[-self.block_frame :]
+ infer_wav = self.tg(
+ infer_wav.unsqueeze(0), self.output_buffer.unsqueeze(0)
+ ).squeeze(0)
+ # volume envelop mixing
+ if self.gui_config.rms_mix_rate < 1 and self.function == "vc":
+ if self.gui_config.I_noise_reduce:
+ input_wav = self.input_wav_denoise[self.extra_frame :]
+ else:
+ input_wav = self.input_wav[self.extra_frame :]
+ rms1 = librosa.feature.rms(
+ y=input_wav[: infer_wav.shape[0]].cpu().numpy(),
+ frame_length=4 * self.zc,
+ hop_length=self.zc,
+ )
+ rms1 = torch.from_numpy(rms1).to(self.config.device)
+ rms1 = F.interpolate(
+ rms1.unsqueeze(0),
+ size=infer_wav.shape[0] + 1,
+ mode="linear",
+ align_corners=True,
+ )[0, 0, :-1]
+ rms2 = librosa.feature.rms(
+ y=infer_wav[:].cpu().numpy(),
+ frame_length=4 * self.zc,
+ hop_length=self.zc,
+ )
+ rms2 = torch.from_numpy(rms2).to(self.config.device)
+ rms2 = F.interpolate(
+ rms2.unsqueeze(0),
+ size=infer_wav.shape[0] + 1,
+ mode="linear",
+ align_corners=True,
+ )[0, 0, :-1]
+ rms2 = torch.max(rms2, torch.zeros_like(rms2) + 1e-3)
+ infer_wav *= torch.pow(
+ rms1 / rms2, torch.tensor(1 - self.gui_config.rms_mix_rate)
+ )
+ # SOLA algorithm from https://github.com/yxlllc/DDSP-SVC
+ conv_input = infer_wav[
+ None, None, : self.sola_buffer_frame + self.sola_search_frame
+ ]
+ cor_nom = F.conv1d(conv_input, self.sola_buffer[None, None, :])
+ cor_den = torch.sqrt(
+ F.conv1d(
+ conv_input**2,
+ torch.ones(1, 1, self.sola_buffer_frame, device=self.config.device),
+ )
+ + 1e-8
+ )
+ if sys.platform == "darwin":
+ _, sola_offset = torch.max(cor_nom[0, 0] / cor_den[0, 0])
+ sola_offset = sola_offset.item()
+ else:
+ sola_offset = torch.argmax(cor_nom[0, 0] / cor_den[0, 0])
+ printt(i18n("SOLA偏移:%d"), int(sola_offset))
+ infer_wav = infer_wav[sola_offset:]
+ infer_wav[: self.sola_buffer_frame] *= self.fade_in_window
+ infer_wav[: self.sola_buffer_frame] += (
+ self.sola_buffer * self.fade_out_window
+ )
+ self.sola_buffer[:] = infer_wav[
+ self.block_frame : self.block_frame + self.sola_buffer_frame
+ ]
+ outdata[:] = (
+ infer_wav[: self.block_frame]
+ .repeat(self.gui_config.channels, 1)
+ .t()
+ .cpu()
+ .numpy()
+ )
+ total_time = time.perf_counter() - start_time
+ if flag_vc:
+ self.window["infer_time"].update(int(total_time * 1000))
+ printt(i18n("推理耗时:%.2f秒"), total_time)
+
+ def update_devices(self, hostapi_name=None):
+ """获取设备列表"""
+ global flag_vc
+ flag_vc = False
+ sd._terminate()
+ sd._initialize()
+ devices = sd.query_devices()
+ hostapis = sd.query_hostapis()
+ for hostapi in hostapis:
+ for device_idx in hostapi["devices"]:
+ devices[device_idx]["hostapi_name"] = hostapi["name"]
+ self.hostapis = [hostapi["name"] for hostapi in hostapis]
+ if hostapi_name not in self.hostapis:
+ hostapi_name = self.hostapis[0]
+ self.input_devices = [
+ d["name"]
+ for d in devices
+ if d["max_input_channels"] > 0 and d["hostapi_name"] == hostapi_name
+ ]
+ self.output_devices = [
+ d["name"]
+ for d in devices
+ if d["max_output_channels"] > 0 and d["hostapi_name"] == hostapi_name
+ ]
+ self.input_devices_indices = [
+ d["index"] if "index" in d else d["name"]
+ for d in devices
+ if d["max_input_channels"] > 0 and d["hostapi_name"] == hostapi_name
+ ]
+ self.output_devices_indices = [
+ d["index"] if "index" in d else d["name"]
+ for d in devices
+ if d["max_output_channels"] > 0 and d["hostapi_name"] == hostapi_name
+ ]
+
+ def set_devices(self, input_device, output_device):
+ """设置输出设备"""
+ sd.default.device[0] = self.input_devices_indices[
+ self.input_devices.index(input_device)
+ ]
+ sd.default.device[1] = self.output_devices_indices[
+ self.output_devices.index(output_device)
+ ]
+ printt(i18n("输入设备:%s:%s"), str(sd.default.device[0]), input_device)
+ printt(i18n("输出设备:%s:%s"), str(sd.default.device[1]), output_device)
+
+ def get_device_samplerate(self):
+ return int(
+ sd.query_devices(device=sd.default.device[0])["default_samplerate"]
+ )
+
+ def get_device_channels(self):
+ max_input_channels = sd.query_devices(device=sd.default.device[0])[
+ "max_input_channels"
+ ]
+ max_output_channels = sd.query_devices(device=sd.default.device[1])[
+ "max_output_channels"
+ ]
+ return min(max_input_channels, max_output_channels, 2)
+
+ gui = GUI()
diff --git a/requirments_cpu_py312.txt b/requirments_cpu_py312.txt
new file mode 100644
index 0000000..8dc5b54
--- /dev/null
+++ b/requirments_cpu_py312.txt
@@ -0,0 +1,71 @@
+# Python 3.12 x64 / Windows / CPU + DirectML
+#
+# Install the complete environment in one stage from the project root:
+# runtime\python.exe -I -m pip install -r requirments_cpu_py312.txt
+#
+# torch-directml 0.2.5 is built against the PyTorch 2.4.1 family. These exact
+# CPU versions are therefore intentional; they also ensure that no CUDA Torch
+# binaries are introduced into this runtime.
+
+--index-url https://mirrors.pku.edu.cn/pypi/simple
+--extra-index-url https://mirrors.nju.edu.cn/pytorch/whl/cpu
+--extra-index-url https://pypi.org/simple
+
+torch==2.4.1+cpu
+torchaudio==2.4.1+cpu
+torchvision==0.19.1+cpu
+torch-directml==0.2.5.dev240914
+
+# Packaging support. Gradio 3.14 still imports pkg_resources, so Setuptools
+# must remain on the last generation that provides it.
+setuptools>=75.0,<81
+wheel>=0.45,<1
+packaging>=24.0
+
+# Direct project dependencies. Upper bounds are used only where a newer major
+# release changes an API/ABI used by this project or breaks NumPy 1.x support.
+av>=15.1.0,<16
+einops>=0.8.0,<1
+faiss-cpu>=1.13.0,<2
+ffmpeg-python>=0.2.0,<1
+FreeSimpleGUI>=5.1.0,<5.2
+librosa>=0.10.2,<0.11
+local-attention>=1.11.0,<2
+# hyper-connections 0.4.3+ requires Torch 2.5+, while DirectML currently pins
+# Torch 2.4.1. 0.4.2 is the newest compatible generation.
+hyper-connections>=0.1.8,<0.4.3
+matplotlib>=3.8.2,<4
+networkx>=3.2.0,<4
+numpy>=1.26.4,<2
+
+# DirectML provider for RMVPE and ONNX UVR inference. ORT variants share the
+# same Python module, so this environment contains only the DirectML package.
+onnxruntime-directml>=1.24.4,<2
+coloredlogs>=15.0,<16
+
+opencv-python-headless>=4.10.0,<5
+praat-parselmouth>=0.4.5,<1
+PyYAML>=6.0
+scikit-learn>=1.6.0,<2
+scipy>=1.13.1,<2
+sounddevice>=0.5.0,<1
+soundfile>=0.13.0,<1
+tensorboard>=2.19.0
+torchfcpe>=0.0.4,<0.1
+tqdm>=4.67.0,<5
+transformers>=4.49.0,<4.50
+
+# Gradio 3.14 API compatibility. Newer Gradio/FastAPI/Pydantic generations
+# changed component, event, generator, request and launch behavior used here.
+gradio>=3.14.0,<3.15
+altair>=4.2.0,<5
+anyio>=3.6.2,<4
+fastapi>=0.88.0,<0.100
+httpx>=0.23.0,<0.24
+markdown-it-py>=2.2.0,<3
+mdit-py-plugins>=0.3.3,<0.4
+pydantic>=1.10.13,<2
+pyparsing>=3.0.9,<3.1
+starlette>=0.22.0,<0.28
+uvicorn>=0.20.0,<0.23
+websockets>=10.4,<11
diff --git a/tools/file_io.py b/tools/file_io.py
new file mode 100644
index 0000000..62dc2f2
--- /dev/null
+++ b/tools/file_io.py
@@ -0,0 +1,15 @@
+def read_text(path, errors="strict", newline=None):
+ last_error = None
+ for encoding in (None, "utf8", "gbk"):
+ try:
+ kwargs = {"errors": "strict", "newline": newline}
+ if encoding is not None:
+ kwargs["encoding"] = encoding
+ with open(path, "r", **kwargs) as file:
+ return file.read()
+ except UnicodeDecodeError as error:
+ last_error = error
+ if errors != "strict":
+ with open(path, "r", encoding="gbk", errors=errors, newline=newline) as file:
+ return file.read()
+ raise last_error
diff --git a/tools/progress.py b/tools/progress.py
new file mode 100644
index 0000000..253e3a6
--- /dev/null
+++ b/tools/progress.py
@@ -0,0 +1,46 @@
+import math
+
+from i18n.i18n import I18nAuto
+
+
+i18n = I18nAuto()
+
+
+def should_report(index, total, max_updates=12):
+ if total <= 0:
+ return False
+ if total <= max_updates:
+ return True
+ interval = max(1, math.ceil(total / max_updates))
+ return index == 0 or index + 1 == total or (index + 1) % interval == 0
+
+
+def batch_status(title, current, total, success, failed, latest="", failures=None):
+ if total <= 0:
+ state = i18n("等待输入")
+ elif current >= total:
+ state = i18n("已完成")
+ else:
+ state = i18n("处理中")
+ lines = [
+ "【%s】" % title,
+ "%s:%s" % (i18n("状态"), state),
+ "%s:%s/%s | %s:%s | %s:%s"
+ % (
+ i18n("进度"),
+ current,
+ total,
+ i18n("成功"),
+ success,
+ i18n("失败"),
+ failed,
+ ),
+ ]
+ if latest:
+ lines.append("%s:%s" % (i18n("当前"), latest))
+ if failures:
+ lines.append("%s:" % i18n("失败记录"))
+ lines.extend(failures[-10:])
+ if len(failures) > 10:
+ lines.append(i18n("……仅显示最近10条失败记录"))
+ return "\n".join(lines)
diff --git a/tools/torchgate/__init__.py b/tools/torchgate/__init__.py
new file mode 100644
index 0000000..8c1b549
--- /dev/null
+++ b/tools/torchgate/__init__.py
@@ -0,0 +1,13 @@
+"""
+TorchGating is a PyTorch-based implementation of Spectral Gating
+================================================
+Author: Asaf Zorea
+
+Contents
+--------
+torchgate imports all the functions from PyTorch, and in addition provides:
+ TorchGating --- A PyTorch module that applies a spectral gate to an input signal
+
+"""
+
+from .torchgate import TorchGate
diff --git a/tools/torchgate/torchgate.py b/tools/torchgate/torchgate.py
new file mode 100644
index 0000000..90da006
--- /dev/null
+++ b/tools/torchgate/torchgate.py
@@ -0,0 +1,280 @@
+import torch
+from infer.rmvpe import STFT
+from torch.nn.functional import conv1d, conv2d
+from typing import Union, Optional
+from .utils import linspace, temperature_sigmoid, amp_to_db
+
+
+class TorchGate(torch.nn.Module):
+ """
+ A PyTorch module that applies a spectral gate to an input signal.
+
+ Arguments:
+ sr {int} -- Sample rate of the input signal.
+ nonstationary {bool} -- Whether to use non-stationary or stationary masking (default: {False}).
+ n_std_thresh_stationary {float} -- Number of standard deviations above mean to threshold noise for
+ stationary masking (default: {1.5}).
+ n_thresh_nonstationary {float} -- Number of multiplies above smoothed magnitude spectrogram. for
+ non-stationary masking (default: {1.3}).
+ temp_coeff_nonstationary {float} -- Temperature coefficient for non-stationary masking (default: {0.1}).
+ n_movemean_nonstationary {int} -- Number of samples for moving average smoothing in non-stationary masking
+ (default: {20}).
+ prop_decrease {float} -- Proportion to decrease signal by where the mask is zero (default: {1.0}).
+ n_fft {int} -- Size of FFT for STFT (default: {1024}).
+ win_length {[int]} -- Window length for STFT. If None, defaults to `n_fft` (default: {None}).
+ hop_length {[int]} -- Hop length for STFT. If None, defaults to `win_length` // 4 (default: {None}).
+ freq_mask_smooth_hz {float} -- Frequency smoothing width for mask (in Hz). If None, no smoothing is applied
+ (default: {500}).
+ time_mask_smooth_ms {float} -- Time smoothing width for mask (in ms). If None, no smoothing is applied
+ (default: {50}).
+ """
+
+ @torch.no_grad()
+ def __init__(
+ self,
+ sr,
+ nonstationary = False,
+ n_std_thresh_stationary = 1.5,
+ n_thresh_nonstationary = 1.3,
+ temp_coeff_nonstationary = 0.1,
+ n_movemean_nonstationary = 20,
+ prop_decrease = 1.0,
+ n_fft = 1024,
+ win_length = None,
+ hop_length = None,
+ freq_mask_smooth_hz = 500,
+ time_mask_smooth_ms = 50,
+ ):
+ super().__init__()
+
+ # General Params
+ self.sr = sr
+ self.nonstationary = nonstationary
+ assert 0.0 <= prop_decrease <= 1.0
+ self.prop_decrease = prop_decrease
+
+ # STFT Params
+ self.n_fft = n_fft
+ self.win_length = self.n_fft if win_length is None else win_length
+ self.hop_length = self.win_length // 4 if hop_length is None else hop_length
+
+ # Stationary Params
+ self.n_std_thresh_stationary = n_std_thresh_stationary
+
+ # Non-Stationary Params
+ self.temp_coeff_nonstationary = temp_coeff_nonstationary
+ self.n_movemean_nonstationary = n_movemean_nonstationary
+ self.n_thresh_nonstationary = n_thresh_nonstationary
+
+ # Smooth Mask Params
+ self.freq_mask_smooth_hz = freq_mask_smooth_hz
+ self.time_mask_smooth_ms = time_mask_smooth_ms
+ self.register_buffer("smoothing_filter", self._generate_mask_smoothing_filter())
+
+ @torch.no_grad()
+ def _generate_mask_smoothing_filter(self) :
+ """
+ A PyTorch module that applies a spectral gate to an input signal using the STFT.
+
+ Returns:
+ smoothing_filter (torch.Tensor): a 2D tensor representing the smoothing filter,
+ with shape (n_grad_freq, n_grad_time), where n_grad_freq is the number of frequency
+ bins to smooth and n_grad_time is the number of time frames to smooth.
+ If both self.freq_mask_smooth_hz and self.time_mask_smooth_ms are None, returns None.
+ """
+ if self.freq_mask_smooth_hz is None and self.time_mask_smooth_ms is None:
+ return None
+
+ n_grad_freq = (
+ 1
+ if self.freq_mask_smooth_hz is None
+ else int(self.freq_mask_smooth_hz / (self.sr / (self.n_fft / 2)))
+ )
+ if n_grad_freq < 1:
+ raise ValueError(
+ f"freq_mask_smooth_hz needs to be at least {int((self.sr / (self._n_fft / 2)))} Hz"
+ )
+
+ n_grad_time = (
+ 1
+ if self.time_mask_smooth_ms is None
+ else int(self.time_mask_smooth_ms / ((self.hop_length / self.sr) * 1000))
+ )
+ if n_grad_time < 1:
+ raise ValueError(
+ f"time_mask_smooth_ms needs to be at least {int((self.hop_length / self.sr) * 1000)} ms"
+ )
+
+ if n_grad_time == 1 and n_grad_freq == 1:
+ return None
+
+ v_f = torch.cat(
+ [
+ linspace(0, 1, n_grad_freq + 1, endpoint=False),
+ linspace(1, 0, n_grad_freq + 2),
+ ]
+ )[1:-1]
+ v_t = torch.cat(
+ [
+ linspace(0, 1, n_grad_time + 1, endpoint=False),
+ linspace(1, 0, n_grad_time + 2),
+ ]
+ )[1:-1]
+ smoothing_filter = torch.outer(v_f, v_t).unsqueeze(0).unsqueeze(0)
+
+ return smoothing_filter / smoothing_filter.sum()
+
+ @torch.no_grad()
+ def _stationary_mask(
+ self, X_db, xn = None
+ ) :
+ """
+ Computes a stationary binary mask to filter out noise in a log-magnitude spectrogram.
+
+ Arguments:
+ X_db (torch.Tensor): 2D tensor of shape (frames, freq_bins) containing the log-magnitude spectrogram.
+ xn (torch.Tensor): 1D tensor containing the audio signal corresponding to X_db.
+
+ Returns:
+ sig_mask (torch.Tensor): Binary mask of the same shape as X_db, where values greater than the threshold
+ are set to 1, and the rest are set to 0.
+ """
+ if xn is not None:
+ if "privateuseone" in str(xn.device):
+ if not hasattr(self, "stft"):
+ self.stft = STFT(
+ filter_length=self.n_fft,
+ hop_length=self.hop_length,
+ win_length=self.win_length,
+ window="hann",
+ ).to(xn.device)
+ XN = self.stft.transform(xn)
+ else:
+ XN = torch.stft(
+ xn,
+ n_fft=self.n_fft,
+ hop_length=self.hop_length,
+ win_length=self.win_length,
+ return_complex=True,
+ pad_mode="constant",
+ center=True,
+ window=torch.hann_window(self.win_length).to(xn.device),
+ )
+ XN_db = amp_to_db(XN).to(dtype=X_db.dtype)
+ else:
+ XN_db = X_db
+
+ # calculate mean and standard deviation along the frequency axis
+ std_freq_noise, mean_freq_noise = torch.std_mean(XN_db, dim=-1)
+
+ # compute noise threshold
+ noise_thresh = mean_freq_noise + std_freq_noise * self.n_std_thresh_stationary
+
+ # create binary mask by thresholding the spectrogram
+ sig_mask = X_db > noise_thresh.unsqueeze(2)
+ return sig_mask
+
+ @torch.no_grad()
+ def _nonstationary_mask(self, X_abs) :
+ """
+ Computes a non-stationary binary mask to filter out noise in a log-magnitude spectrogram.
+
+ Arguments:
+ X_abs (torch.Tensor): 2D tensor of shape (frames, freq_bins) containing the magnitude spectrogram.
+
+ Returns:
+ sig_mask (torch.Tensor): Binary mask of the same shape as X_abs, where values greater than the threshold
+ are set to 1, and the rest are set to 0.
+ """
+ X_smoothed = (
+ conv1d(
+ X_abs.reshape(-1, 1, X_abs.shape[-1]),
+ torch.ones(
+ self.n_movemean_nonstationary,
+ dtype=X_abs.dtype,
+ device=X_abs.device,
+ ).view(1, 1, -1),
+ padding="same",
+ ).view(X_abs.shape)
+ / self.n_movemean_nonstationary
+ )
+
+ # Compute slowness ratio and apply temperature sigmoid
+ slowness_ratio = (X_abs - X_smoothed) / (X_smoothed + 1e-6)
+ sig_mask = temperature_sigmoid(
+ slowness_ratio, self.n_thresh_nonstationary, self.temp_coeff_nonstationary
+ )
+
+ return sig_mask
+
+ def forward(
+ self, x, xn = None
+ ) :
+ """
+ Apply the proposed algorithm to the input signal.
+
+ Arguments:
+ x (torch.Tensor): The input audio signal, with shape (batch_size, signal_length).
+ xn (Optional[torch.Tensor]): The noise signal used for stationary noise reduction. If `None`, the input
+ signal is used as the noise signal. Default: `None`.
+
+ Returns:
+ torch.Tensor: The denoised audio signal, with the same shape as the input signal.
+ """
+
+ # Compute short-time Fourier transform (STFT)
+ if "privateuseone" in str(x.device):
+ if not hasattr(self, "stft"):
+ self.stft = STFT(
+ filter_length=self.n_fft,
+ hop_length=self.hop_length,
+ win_length=self.win_length,
+ window="hann",
+ ).to(x.device)
+ X, phase = self.stft.transform(x, return_phase=True)
+ else:
+ X = torch.stft(
+ x,
+ n_fft=self.n_fft,
+ hop_length=self.hop_length,
+ win_length=self.win_length,
+ return_complex=True,
+ pad_mode="constant",
+ center=True,
+ window=torch.hann_window(self.win_length).to(x.device),
+ )
+
+ # Compute signal mask based on stationary or nonstationary assumptions
+ if self.nonstationary:
+ sig_mask = self._nonstationary_mask(X.abs())
+ else:
+ sig_mask = self._stationary_mask(amp_to_db(X), xn)
+
+ # Propagate decrease in signal power
+ sig_mask = self.prop_decrease * (sig_mask.float() - 1.0) + 1.0
+
+ # Smooth signal mask with 2D convolution
+ if self.smoothing_filter is not None:
+ sig_mask = conv2d(
+ sig_mask.unsqueeze(1),
+ self.smoothing_filter.to(sig_mask.dtype),
+ padding="same",
+ )
+
+ # Apply signal mask to STFT magnitude and phase components
+ Y = X * sig_mask.squeeze(1)
+
+ # Inverse STFT to obtain time-domain signal
+ if "privateuseone" in str(Y.device):
+ y = self.stft.inverse(Y, phase)
+ else:
+ y = torch.istft(
+ Y,
+ n_fft=self.n_fft,
+ hop_length=self.hop_length,
+ win_length=self.win_length,
+ center=True,
+ window=torch.hann_window(self.win_length).to(Y.device),
+ )
+
+ return y.to(dtype=x.dtype)
diff --git a/tools/torchgate/utils.py b/tools/torchgate/utils.py
new file mode 100644
index 0000000..e1633df
--- /dev/null
+++ b/tools/torchgate/utils.py
@@ -0,0 +1,70 @@
+import torch
+from torch.types import Number
+
+
+@torch.no_grad()
+def amp_to_db(
+ x, eps=torch.finfo(torch.float64).eps, top_db=40
+) :
+ """
+ Convert the input tensor from amplitude to decibel scale.
+
+ Arguments:
+ x {[torch.Tensor]} -- [Input tensor.]
+
+ Keyword Arguments:
+ eps {[float]} -- [Small value to avoid numerical instability.]
+ (default: {torch.finfo(torch.float64).eps})
+ top_db {[float]} -- [threshold the output at ``top_db`` below the peak]
+ ` (default: {40})
+
+ Returns:
+ [torch.Tensor] -- [Output tensor in decibel scale.]
+ """
+ x_db = 20 * torch.log10(x.abs() + eps)
+ return torch.max(x_db, (x_db.max(-1).values - top_db).unsqueeze(-1))
+
+
+@torch.no_grad()
+def temperature_sigmoid(x, x0, temp_coeff) :
+ """
+ Apply a sigmoid function with temperature scaling.
+
+ Arguments:
+ x {[torch.Tensor]} -- [Input tensor.]
+ x0 {[float]} -- [Parameter that controls the threshold of the sigmoid.]
+ temp_coeff {[float]} -- [Parameter that controls the slope of the sigmoid.]
+
+ Returns:
+ [torch.Tensor] -- [Output tensor after applying the sigmoid with temperature scaling.]
+ """
+ return torch.sigmoid((x - x0) / temp_coeff)
+
+
+@torch.no_grad()
+def linspace(
+ start, stop, num = 50, endpoint = True, **kwargs
+) :
+ """
+ Generate a linearly spaced 1-D tensor.
+
+ Arguments:
+ start {[Number]} -- [The starting value of the sequence.]
+ stop {[Number]} -- [The end value of the sequence, unless `endpoint` is set to False.
+ In that case, the sequence consists of all but the last of ``num + 1``
+ evenly spaced samples, so that `stop` is excluded. Note that the step
+ size changes when `endpoint` is False.]
+
+ Keyword Arguments:
+ num {[int]} -- [Number of samples to generate. Default is 50. Must be non-negative.]
+ endpoint {[bool]} -- [If True, `stop` is the last sample. Otherwise, it is not included.
+ Default is True.]
+ **kwargs -- [Additional arguments to be passed to the underlying PyTorch `linspace` function.]
+
+ Returns:
+ [torch.Tensor] -- [1-D tensor of `num` equally spaced samples from `start` to `stop`.]
+ """
+ if endpoint:
+ return torch.linspace(start, stop, num, **kwargs)
+ else:
+ return torch.linspace(start, stop, num + 1, **kwargs)[:-1]
diff --git a/tools/uvr5/bs_roformer/__init__.py b/tools/uvr5/bs_roformer/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tools/uvr5/bs_roformer/attend.py b/tools/uvr5/bs_roformer/attend.py
new file mode 100644
index 0000000..1782bbd
--- /dev/null
+++ b/tools/uvr5/bs_roformer/attend.py
@@ -0,0 +1,70 @@
+from packaging import version
+import torch
+from torch import nn, einsum
+import torch.nn.functional as F
+
+
+def exists(val):
+ return val is not None
+
+
+def default(v, d):
+ return v if exists(v) else d
+
+
+class Attend(nn.Module):
+ def __init__(self, dropout=0.0, flash=False, scale=None):
+ super().__init__()
+ self.scale = scale
+ self.dropout = dropout
+ self.attn_dropout = nn.Dropout(dropout)
+
+ self.flash = flash
+ assert not (flash and version.parse(torch.__version__) < version.parse("2.0.0")), (
+ "in order to use flash attention, you must be using pytorch 2.0 or above"
+ )
+
+ def flash_attn(self, q, k, v):
+ # _, heads, q_len, _, k_len, is_cuda, device = *q.shape, k.shape[-2], q.is_cuda, q.device
+
+ if exists(self.scale):
+ default_scale = q.shape[-1] ** -0.5
+ q = q * (self.scale / default_scale)
+
+ # pytorch 2.0 flash attn: q, k, v, mask, dropout, softmax_scale
+ # with torch.backends.cuda.sdp_kernel(enable_flash=True, enable_math=True, enable_mem_efficient=True):
+ return F.scaled_dot_product_attention(q, k, v, dropout_p=self.dropout if self.training else 0.0)
+
+ def forward(self, q, k, v):
+ """
+ einstein notation
+ b - batch
+ h - heads
+ n, i, j - sequence length (base sequence length, source, target)
+ d - feature dimension
+ """
+
+ # q_len, k_len, device = q.shape[-2], k.shape[-2], q.device
+
+ scale = default(self.scale, q.shape[-1] ** -0.5)
+
+ # DirectML does not expose PyTorch's SDPA kernels. Keep the existing
+ # SDPA path for CUDA/CPU/MPS and use the mathematically equivalent
+ # einsum implementation below for PrivateUse1 tensors.
+ if self.flash and q.device.type != "privateuseone":
+ return self.flash_attn(q, k, v)
+
+ # similarity
+
+ sim = einsum("b h i d, b h j d -> b h i j", q, k) * scale
+
+ # attention
+
+ attn = sim.softmax(dim=-1)
+ attn = self.attn_dropout(attn)
+
+ # aggregate values
+
+ out = einsum("b h i j, b h j d -> b h i d", attn, v)
+
+ return out
diff --git a/tools/uvr5/bs_roformer/bs_roformer.py b/tools/uvr5/bs_roformer/bs_roformer.py
new file mode 100644
index 0000000..85a506f
--- /dev/null
+++ b/tools/uvr5/bs_roformer/bs_roformer.py
@@ -0,0 +1,347 @@
+from functools import partial
+import torch
+from torch import nn
+from torch.nn import Module, ModuleList
+import torch.nn.functional as F
+from tools.uvr5.bs_roformer.attend import Attend
+from torch.utils.checkpoint import checkpoint
+from typing import Tuple, Optional, Callable
+from tools.uvr5.rotary_embedding_torch import RotaryEmbedding
+from einops import rearrange, pack, unpack
+from einops.layers.torch import Rearrange
+
+def exists(val):
+ return val is not None
+
+def default(v, d):
+ return v if exists(v) else d
+
+def pack_one(t, pattern):
+ return pack([t], pattern)
+
+def unpack_one(t, ps, pattern):
+ return unpack(t, ps, pattern)[0]
+
+def l2norm(t):
+ return F.normalize(t, dim=-1, p=2)
+
+class RMSNorm(Module):
+
+ def __init__(self, dim):
+ super().__init__()
+ self.scale = dim ** 0.5
+ self.gamma = nn.Parameter(torch.ones(dim))
+
+ def forward(self, x):
+ return F.normalize(x, dim=-1) * self.scale * self.gamma
+
+class FeedForward(Module):
+
+ def __init__(self, dim, mult=4, dropout=0.0):
+ super().__init__()
+ dim_inner = int(dim * mult)
+ self.net = nn.Sequential(RMSNorm(dim), nn.Linear(dim, dim_inner), nn.GELU(), nn.Dropout(dropout), nn.Linear(dim_inner, dim), nn.Dropout(dropout))
+
+ def forward(self, x):
+ return self.net(x)
+
+class Attention(Module):
+
+ def __init__(self, dim, heads=8, dim_head=64, dropout=0.0, rotary_embed=None, flash=True):
+ super().__init__()
+ self.heads = heads
+ self.scale = dim_head ** (-0.5)
+ dim_inner = heads * dim_head
+ self.rotary_embed = rotary_embed
+ self.attend = Attend(flash=flash, dropout=dropout)
+ self.norm = RMSNorm(dim)
+ self.to_qkv = nn.Linear(dim, dim_inner * 3, bias=False)
+ self.to_gates = nn.Linear(dim, heads)
+ self.to_out = nn.Sequential(nn.Linear(dim_inner, dim, bias=False), nn.Dropout(dropout))
+
+ def forward(self, x):
+ x = self.norm(x)
+ (q, k, v) = rearrange(self.to_qkv(x), 'b n (qkv h d) -> qkv b h n d', qkv=3, h=self.heads)
+ if exists(self.rotary_embed):
+ q = self.rotary_embed.rotate_queries_or_keys(q)
+ k = self.rotary_embed.rotate_queries_or_keys(k)
+ out = self.attend(q, k, v)
+ gates = self.to_gates(x)
+ out = out * rearrange(gates, 'b n h -> b h n 1').sigmoid()
+ out = rearrange(out, 'b h n d -> b n (h d)')
+ return self.to_out(out)
+
+class LinearAttention(Module):
+ """
+ this flavor of linear attention proposed in https://arxiv.org/abs/2106.09681 by El-Nouby et al.
+ """
+
+ def __init__(self, *, dim, dim_head=32, heads=8, scale=8, flash=False, dropout=0.0):
+ super().__init__()
+ dim_inner = dim_head * heads
+ self.norm = RMSNorm(dim)
+ self.to_qkv = nn.Sequential(nn.Linear(dim, dim_inner * 3, bias=False), Rearrange('b n (qkv h d) -> qkv b h d n', qkv=3, h=heads))
+ self.temperature = nn.Parameter(torch.ones(heads, 1, 1))
+ self.attend = Attend(scale=scale, dropout=dropout, flash=flash)
+ self.to_out = nn.Sequential(Rearrange('b h d n -> b n (h d)'), nn.Linear(dim_inner, dim, bias=False))
+
+ def forward(self, x):
+ x = self.norm(x)
+ (q, k, v) = self.to_qkv(x)
+ (q, k) = map(l2norm, (q, k))
+ q = q * self.temperature.exp()
+ out = self.attend(q, k, v)
+ return self.to_out(out)
+
+class Transformer(Module):
+
+ def __init__(self, *, dim, depth, dim_head=64, heads=8, attn_dropout=0.0, ff_dropout=0.0, ff_mult=4, norm_output=True, rotary_embed=None, flash_attn=True, linear_attn=False):
+ super().__init__()
+ self.layers = ModuleList([])
+ for _ in range(depth):
+ if linear_attn:
+ attn = LinearAttention(dim=dim, dim_head=dim_head, heads=heads, dropout=attn_dropout, flash=flash_attn)
+ else:
+ attn = Attention(dim=dim, dim_head=dim_head, heads=heads, dropout=attn_dropout, rotary_embed=rotary_embed, flash=flash_attn)
+ self.layers.append(ModuleList([attn, FeedForward(dim=dim, mult=ff_mult, dropout=ff_dropout)]))
+ self.norm = RMSNorm(dim) if norm_output else nn.Identity()
+
+ def forward(self, x):
+ for (attn, ff) in self.layers:
+ x = attn(x) + x
+ x = ff(x) + x
+ return self.norm(x)
+
+class BandSplit(Module):
+
+ def __init__(self, dim, dim_inputs):
+ super().__init__()
+ self.dim_inputs = dim_inputs
+ self.to_features = ModuleList([])
+ for dim_in in dim_inputs:
+ net = nn.Sequential(RMSNorm(dim_in), nn.Linear(dim_in, dim))
+ self.to_features.append(net)
+
+ def forward(self, x):
+ x = x.split(self.dim_inputs, dim=-1)
+ outs = []
+ for (split_input, to_feature) in zip(x, self.to_features):
+ split_output = to_feature(split_input)
+ outs.append(split_output)
+ return torch.stack(outs, dim=-2)
+
+def MLP(dim_in, dim_out, dim_hidden=None, depth=1, activation=nn.Tanh):
+ dim_hidden = default(dim_hidden, dim_in)
+ net = []
+ dims = (dim_in, *(dim_hidden,) * (depth - 1), dim_out)
+ for (ind, (layer_dim_in, layer_dim_out)) in enumerate(zip(dims[:-1], dims[1:])):
+ is_last = ind == len(dims) - 2
+ net.append(nn.Linear(layer_dim_in, layer_dim_out))
+ if is_last:
+ continue
+ net.append(activation())
+ return nn.Sequential(*net)
+
+class MaskEstimator(Module):
+
+ def __init__(self, dim, dim_inputs, depth, mlp_expansion_factor=4):
+ super().__init__()
+ self.dim_inputs = dim_inputs
+ self.to_freqs = ModuleList([])
+ dim_hidden = dim * mlp_expansion_factor
+ for dim_in in dim_inputs:
+ net = []
+ mlp = nn.Sequential(MLP(dim, dim_in * 2, dim_hidden=dim_hidden, depth=depth), nn.GLU(dim=-1))
+ self.to_freqs.append(mlp)
+
+ def forward(self, x):
+ x = x.unbind(dim=-2)
+ outs = []
+ for (band_features, mlp) in zip(x, self.to_freqs):
+ freq_out = mlp(band_features)
+ outs.append(freq_out)
+ return torch.cat(outs, dim=-1)
+DEFAULT_FREQS_PER_BANDS = (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 12, 12, 12, 12, 12, 12, 12, 24, 24, 24, 24, 24, 24, 24, 24, 48, 48, 48, 48, 48, 48, 48, 48, 128, 129)
+
+class BSRoformer(Module):
+
+ def __init__(self, dim, *, depth, stereo=False, num_stems=1, time_transformer_depth=2, freq_transformer_depth=2, linear_transformer_depth=0, freqs_per_bands=DEFAULT_FREQS_PER_BANDS, dim_head=64, heads=8, attn_dropout=0.0, ff_dropout=0.0, flash_attn=True, dim_freqs_in=1025, stft_n_fft=2048, stft_hop_length=512, stft_win_length=2048, stft_normalized=False, stft_window_fn=None, mask_estimator_depth=2, multi_stft_resolution_loss_weight=1.0, multi_stft_resolutions_window_sizes=(4096, 2048, 1024, 512, 256), multi_stft_hop_size=147, multi_stft_normalized=False, multi_stft_window_fn=torch.hann_window, mlp_expansion_factor=4, use_torch_checkpoint=False, skip_connection=False):
+ super().__init__()
+ self.stereo = stereo
+ self.audio_channels = 2 if stereo else 1
+ self.num_stems = num_stems
+ self.use_torch_checkpoint = use_torch_checkpoint
+ self.skip_connection = skip_connection
+ self.layers = ModuleList([])
+ transformer_kwargs = dict(dim=dim, heads=heads, dim_head=dim_head, attn_dropout=attn_dropout, ff_dropout=ff_dropout, flash_attn=flash_attn, norm_output=False)
+ time_rotary_embed = RotaryEmbedding(dim=dim_head)
+ freq_rotary_embed = RotaryEmbedding(dim=dim_head)
+ for _ in range(depth):
+ tran_modules = []
+ if linear_transformer_depth > 0:
+ tran_modules.append(Transformer(depth=linear_transformer_depth, linear_attn=True, **transformer_kwargs))
+ tran_modules.append(Transformer(depth=time_transformer_depth, rotary_embed=time_rotary_embed, **transformer_kwargs))
+ tran_modules.append(Transformer(depth=freq_transformer_depth, rotary_embed=freq_rotary_embed, **transformer_kwargs))
+ self.layers.append(nn.ModuleList(tran_modules))
+ self.final_norm = RMSNorm(dim)
+ self.stft_kwargs = dict(n_fft=stft_n_fft, hop_length=stft_hop_length, win_length=stft_win_length, normalized=stft_normalized)
+ self.stft_window_fn = partial(default(stft_window_fn, torch.hann_window), stft_win_length)
+ freqs = torch.stft(torch.randn(1, 4096), **self.stft_kwargs, window=torch.ones(stft_win_length), return_complex=True).shape[1]
+ assert len(freqs_per_bands) > 1
+ assert sum(freqs_per_bands) == freqs, f'the number of freqs in the bands must equal {freqs} based on the STFT settings, but got {sum(freqs_per_bands)}'
+ freqs_per_bands_with_complex = tuple((2 * f * self.audio_channels for f in freqs_per_bands))
+ self.band_split = BandSplit(dim=dim, dim_inputs=freqs_per_bands_with_complex)
+ self.mask_estimators = nn.ModuleList([])
+ for _ in range(num_stems):
+ mask_estimator = MaskEstimator(dim=dim, dim_inputs=freqs_per_bands_with_complex, depth=mask_estimator_depth, mlp_expansion_factor=mlp_expansion_factor)
+ self.mask_estimators.append(mask_estimator)
+ self.multi_stft_resolution_loss_weight = multi_stft_resolution_loss_weight
+ self.multi_stft_resolutions_window_sizes = multi_stft_resolutions_window_sizes
+ self.multi_stft_n_fft = stft_n_fft
+ self.multi_stft_window_fn = multi_stft_window_fn
+ self.multi_stft_kwargs = dict(hop_length=multi_stft_hop_size, normalized=multi_stft_normalized)
+
+ def forward(self, raw_audio, target=None, return_loss_breakdown=False):
+ """
+ einops
+
+ b - batch
+ f - freq
+ t - time
+ s - audio channel (1 for mono, 2 for stereo)
+ n - number of 'stems'
+ c - complex (2)
+ d - feature dimension
+ """
+ device = raw_audio.device
+ x_is_dml = device.type == 'privateuseone'
+ x_is_mps = True if device.type == 'mps' else False
+ if raw_audio.ndim == 2:
+ raw_audio = rearrange(raw_audio, 'b t -> b 1 t')
+ channels = raw_audio.shape[1]
+ assert not self.stereo and channels == 1 or (self.stereo and channels == 2), 'stereo needs to be set to True if passing in audio signal that is stereo (channel dimension of 2). also need to be False if mono (channel dimension of 1)'
+ (raw_audio, batch_audio_channel_packed_shape) = pack_one(raw_audio, '* t')
+ if x_is_dml:
+ # DirectML has no complex/STFT kernels. Keep only the spectral
+ # boundary on CPU and move its real representation to DirectML.
+ stft_window = self.stft_window_fn(device='cpu')
+ stft_complex = torch.stft(
+ raw_audio.cpu(),
+ **self.stft_kwargs,
+ window=stft_window,
+ return_complex=True,
+ )
+ stft_repr_cpu = torch.view_as_real(stft_complex)
+ stft_repr_cpu = unpack_one(
+ stft_repr_cpu, batch_audio_channel_packed_shape, '* f t c'
+ )
+ stft_repr_cpu = rearrange(
+ stft_repr_cpu, 'b s f t c -> b (f s) t c'
+ )
+ stft_repr = stft_repr_cpu.to(device)
+ else:
+ stft_window = self.stft_window_fn(device=device)
+ try:
+ stft_repr = torch.stft(raw_audio, **self.stft_kwargs, window=stft_window, return_complex=True)
+ except:
+ stft_repr = torch.stft(raw_audio.cpu() if x_is_mps else raw_audio, **self.stft_kwargs, window=stft_window.cpu() if x_is_mps else stft_window, return_complex=True).to(device)
+ stft_repr = torch.view_as_real(stft_repr)
+ stft_repr = unpack_one(stft_repr, batch_audio_channel_packed_shape, '* f t c')
+ stft_repr = rearrange(stft_repr, 'b s f t c -> b (f s) t c')
+ x = rearrange(stft_repr, 'b f t c -> b t (f c)')
+ if self.use_torch_checkpoint:
+ x = checkpoint(self.band_split, x, use_reentrant=False)
+ else:
+ x = self.band_split(x)
+ store = [None] * len(self.layers)
+ for (i, transformer_block) in enumerate(self.layers):
+ if len(transformer_block) == 3:
+ (linear_transformer, time_transformer, freq_transformer) = transformer_block
+ (x, ft_ps) = pack([x], 'b * d')
+ if self.use_torch_checkpoint:
+ x = checkpoint(linear_transformer, x, use_reentrant=False)
+ else:
+ x = linear_transformer(x)
+ (x,) = unpack(x, ft_ps, 'b * d')
+ else:
+ (time_transformer, freq_transformer) = transformer_block
+ if self.skip_connection:
+ for j in range(i):
+ x = x + store[j]
+ x = rearrange(x, 'b t f d -> b f t d')
+ (x, ps) = pack([x], '* t d')
+ if self.use_torch_checkpoint:
+ x = checkpoint(time_transformer, x, use_reentrant=False)
+ else:
+ x = time_transformer(x)
+ (x,) = unpack(x, ps, '* t d')
+ x = rearrange(x, 'b f t d -> b t f d')
+ (x, ps) = pack([x], '* f d')
+ if self.use_torch_checkpoint:
+ x = checkpoint(freq_transformer, x, use_reentrant=False)
+ else:
+ x = freq_transformer(x)
+ (x,) = unpack(x, ps, '* f d')
+ if self.skip_connection:
+ store[i] = x
+ x = self.final_norm(x)
+ num_stems = len(self.mask_estimators)
+ if self.use_torch_checkpoint:
+ mask = torch.stack([checkpoint(fn, x, use_reentrant=False) for fn in self.mask_estimators], dim=1)
+ else:
+ mask = torch.stack([fn(x) for fn in self.mask_estimators], dim=1)
+ mask = rearrange(mask, 'b n t (f c) -> b n f t c', c=2)
+ if x_is_dml:
+ # Complex masking and ISTFT stay on CPU; all learned real-valued
+ # layers above remain on DirectML.
+ stft_repr = rearrange(stft_repr_cpu, 'b f t c -> b 1 f t c')
+ stft_repr = torch.view_as_complex(stft_repr.contiguous())
+ mask = torch.view_as_complex(mask.float().cpu().contiguous())
+ stft_repr = stft_repr * mask
+ stft_repr = rearrange(
+ stft_repr,
+ 'b n (f s) t -> (b n s) f t',
+ s=self.audio_channels,
+ )
+ recon_audio = torch.istft(
+ stft_repr,
+ **self.stft_kwargs,
+ window=stft_window,
+ return_complex=False,
+ length=raw_audio.shape[-1],
+ ).to(device)
+ else:
+ stft_repr = rearrange(stft_repr, 'b f t c -> b 1 f t c')
+ stft_repr = torch.view_as_complex(stft_repr)
+ mask = torch.view_as_complex(mask)
+ stft_repr = stft_repr * mask
+ stft_repr = rearrange(stft_repr, 'b n (f s) t -> (b n s) f t', s=self.audio_channels)
+ try:
+ recon_audio = torch.istft(stft_repr, **self.stft_kwargs, window=stft_window, return_complex=False, length=raw_audio.shape[-1])
+ except:
+ recon_audio = torch.istft(stft_repr.cpu() if x_is_mps else stft_repr, **self.stft_kwargs, window=stft_window.cpu() if x_is_mps else stft_window, return_complex=False, length=raw_audio.shape[-1]).to(device)
+ recon_audio = rearrange(recon_audio, '(b n s) t -> b n s t', s=self.audio_channels, n=num_stems)
+ if num_stems == 1:
+ recon_audio = rearrange(recon_audio, 'b 1 s t -> b s t')
+ if not exists(target):
+ return recon_audio
+ if self.num_stems > 1:
+ assert target.ndim == 4 and target.shape[1] == self.num_stems
+ if target.ndim == 2:
+ target = rearrange(target, '... t -> ... 1 t')
+ target = target[..., :recon_audio.shape[-1]]
+ loss_audio = recon_audio.cpu() if x_is_dml else recon_audio
+ loss_target = target.cpu() if x_is_dml else target
+ loss = F.l1_loss(loss_audio, loss_target)
+ multi_stft_resolution_loss = 0.0
+ for window_size in self.multi_stft_resolutions_window_sizes:
+ spectral_device = 'cpu' if x_is_dml else device
+ res_stft_kwargs = dict(n_fft=max(window_size, self.multi_stft_n_fft), win_length=window_size, return_complex=True, window=self.multi_stft_window_fn(window_size, device=spectral_device), **self.multi_stft_kwargs)
+ recon_Y = torch.stft(rearrange(loss_audio, '... s t -> (... s) t'), **res_stft_kwargs)
+ target_Y = torch.stft(rearrange(loss_target, '... s t -> (... s) t'), **res_stft_kwargs)
+ multi_stft_resolution_loss = multi_stft_resolution_loss + F.l1_loss(recon_Y, target_Y)
+ weighted_multi_resolution_loss = multi_stft_resolution_loss * self.multi_stft_resolution_loss_weight
+ total_loss = loss + weighted_multi_resolution_loss
+ if not return_loss_breakdown:
+ return total_loss
+ return (total_loss, (loss, multi_stft_resolution_loss))
diff --git a/tools/uvr5/bs_roformer/mel_band_roformer.py b/tools/uvr5/bs_roformer/mel_band_roformer.py
new file mode 100644
index 0000000..3c6dc83
--- /dev/null
+++ b/tools/uvr5/bs_roformer/mel_band_roformer.py
@@ -0,0 +1,354 @@
+from functools import partial
+import torch
+from torch import nn
+from torch.nn import Module, ModuleList
+import torch.nn.functional as F
+from tools.uvr5.bs_roformer.attend import Attend
+from torch.utils.checkpoint import checkpoint
+from typing import Tuple, Optional, Callable
+from tools.uvr5.rotary_embedding_torch import RotaryEmbedding
+from einops import rearrange, pack, unpack, reduce, repeat
+from einops.layers.torch import Rearrange
+from librosa import filters
+
+def exists(val):
+ return val is not None
+
+def default(v, d):
+ return v if exists(v) else d
+
+def pack_one(t, pattern):
+ return pack([t], pattern)
+
+def unpack_one(t, ps, pattern):
+ return unpack(t, ps, pattern)[0]
+
+def pad_at_dim(t, pad, dim=-1, value=0.0):
+ dims_from_right = -dim - 1 if dim < 0 else t.ndim - dim - 1
+ zeros = (0, 0) * dims_from_right
+ return F.pad(t, (*zeros, *pad), value=value)
+
+def l2norm(t):
+ return F.normalize(t, dim=-1, p=2)
+
+class RMSNorm(Module):
+
+ def __init__(self, dim):
+ super().__init__()
+ self.scale = dim ** 0.5
+ self.gamma = nn.Parameter(torch.ones(dim))
+
+ def forward(self, x):
+ return F.normalize(x, dim=-1) * self.scale * self.gamma
+
+class FeedForward(Module):
+
+ def __init__(self, dim, mult=4, dropout=0.0):
+ super().__init__()
+ dim_inner = int(dim * mult)
+ self.net = nn.Sequential(RMSNorm(dim), nn.Linear(dim, dim_inner), nn.GELU(), nn.Dropout(dropout), nn.Linear(dim_inner, dim), nn.Dropout(dropout))
+
+ def forward(self, x):
+ return self.net(x)
+
+class Attention(Module):
+
+ def __init__(self, dim, heads=8, dim_head=64, dropout=0.0, rotary_embed=None, flash=True):
+ super().__init__()
+ self.heads = heads
+ self.scale = dim_head ** (-0.5)
+ dim_inner = heads * dim_head
+ self.rotary_embed = rotary_embed
+ self.attend = Attend(flash=flash, dropout=dropout)
+ self.norm = RMSNorm(dim)
+ self.to_qkv = nn.Linear(dim, dim_inner * 3, bias=False)
+ self.to_gates = nn.Linear(dim, heads)
+ self.to_out = nn.Sequential(nn.Linear(dim_inner, dim, bias=False), nn.Dropout(dropout))
+
+ def forward(self, x):
+ x = self.norm(x)
+ (q, k, v) = rearrange(self.to_qkv(x), 'b n (qkv h d) -> qkv b h n d', qkv=3, h=self.heads)
+ if exists(self.rotary_embed):
+ q = self.rotary_embed.rotate_queries_or_keys(q)
+ k = self.rotary_embed.rotate_queries_or_keys(k)
+ out = self.attend(q, k, v)
+ gates = self.to_gates(x)
+ out = out * rearrange(gates, 'b n h -> b h n 1').sigmoid()
+ out = rearrange(out, 'b h n d -> b n (h d)')
+ return self.to_out(out)
+
+class LinearAttention(Module):
+ """
+ this flavor of linear attention proposed in https://arxiv.org/abs/2106.09681 by El-Nouby et al.
+ """
+
+ def __init__(self, *, dim, dim_head=32, heads=8, scale=8, flash=False, dropout=0.0):
+ super().__init__()
+ dim_inner = dim_head * heads
+ self.norm = RMSNorm(dim)
+ self.to_qkv = nn.Sequential(nn.Linear(dim, dim_inner * 3, bias=False), Rearrange('b n (qkv h d) -> qkv b h d n', qkv=3, h=heads))
+ self.temperature = nn.Parameter(torch.ones(heads, 1, 1))
+ self.attend = Attend(scale=scale, dropout=dropout, flash=flash)
+ self.to_out = nn.Sequential(Rearrange('b h d n -> b n (h d)'), nn.Linear(dim_inner, dim, bias=False))
+
+ def forward(self, x):
+ x = self.norm(x)
+ (q, k, v) = self.to_qkv(x)
+ (q, k) = map(l2norm, (q, k))
+ q = q * self.temperature.exp()
+ out = self.attend(q, k, v)
+ return self.to_out(out)
+
+class Transformer(Module):
+
+ def __init__(self, *, dim, depth, dim_head=64, heads=8, attn_dropout=0.0, ff_dropout=0.0, ff_mult=4, norm_output=True, rotary_embed=None, flash_attn=True, linear_attn=False):
+ super().__init__()
+ self.layers = ModuleList([])
+ for _ in range(depth):
+ if linear_attn:
+ attn = LinearAttention(dim=dim, dim_head=dim_head, heads=heads, dropout=attn_dropout, flash=flash_attn)
+ else:
+ attn = Attention(dim=dim, dim_head=dim_head, heads=heads, dropout=attn_dropout, rotary_embed=rotary_embed, flash=flash_attn)
+ self.layers.append(ModuleList([attn, FeedForward(dim=dim, mult=ff_mult, dropout=ff_dropout)]))
+ self.norm = RMSNorm(dim) if norm_output else nn.Identity()
+
+ def forward(self, x):
+ for (attn, ff) in self.layers:
+ x = attn(x) + x
+ x = ff(x) + x
+ return self.norm(x)
+
+class BandSplit(Module):
+
+ def __init__(self, dim, dim_inputs):
+ super().__init__()
+ self.dim_inputs = dim_inputs
+ self.to_features = ModuleList([])
+ for dim_in in dim_inputs:
+ net = nn.Sequential(RMSNorm(dim_in), nn.Linear(dim_in, dim))
+ self.to_features.append(net)
+
+ def forward(self, x):
+ x = x.split(self.dim_inputs, dim=-1)
+ outs = []
+ for (split_input, to_feature) in zip(x, self.to_features):
+ split_output = to_feature(split_input)
+ outs.append(split_output)
+ return torch.stack(outs, dim=-2)
+
+def MLP(dim_in, dim_out, dim_hidden=None, depth=1, activation=nn.Tanh):
+ dim_hidden = default(dim_hidden, dim_in)
+ net = []
+ dims = (dim_in, *(dim_hidden,) * depth, dim_out)
+ for (ind, (layer_dim_in, layer_dim_out)) in enumerate(zip(dims[:-1], dims[1:])):
+ is_last = ind == len(dims) - 2
+ net.append(nn.Linear(layer_dim_in, layer_dim_out))
+ if is_last:
+ continue
+ net.append(activation())
+ return nn.Sequential(*net)
+
+class MaskEstimator(Module):
+
+ def __init__(self, dim, dim_inputs, depth, mlp_expansion_factor=4):
+ super().__init__()
+ self.dim_inputs = dim_inputs
+ self.to_freqs = ModuleList([])
+ dim_hidden = dim * mlp_expansion_factor
+ for dim_in in dim_inputs:
+ net = []
+ mlp = nn.Sequential(MLP(dim, dim_in * 2, dim_hidden=dim_hidden, depth=depth), nn.GLU(dim=-1))
+ self.to_freqs.append(mlp)
+
+ def forward(self, x):
+ x = x.unbind(dim=-2)
+ outs = []
+ for (band_features, mlp) in zip(x, self.to_freqs):
+ freq_out = mlp(band_features)
+ outs.append(freq_out)
+ return torch.cat(outs, dim=-1)
+
+class MelBandRoformer(Module):
+
+ def __init__(self, dim, *, depth, stereo=False, num_stems=1, time_transformer_depth=2, freq_transformer_depth=2, linear_transformer_depth=0, num_bands=60, dim_head=64, heads=8, attn_dropout=0.1, ff_dropout=0.1, flash_attn=True, dim_freqs_in=1025, sample_rate=44100, stft_n_fft=2048, stft_hop_length=512, stft_win_length=2048, stft_normalized=False, stft_window_fn=None, mask_estimator_depth=1, multi_stft_resolution_loss_weight=1.0, multi_stft_resolutions_window_sizes=(4096, 2048, 1024, 512, 256), multi_stft_hop_size=147, multi_stft_normalized=False, multi_stft_window_fn=torch.hann_window, match_input_audio_length=False, mlp_expansion_factor=4, use_torch_checkpoint=False, skip_connection=False):
+ super().__init__()
+ self.stereo = stereo
+ self.audio_channels = 2 if stereo else 1
+ self.num_stems = num_stems
+ self.use_torch_checkpoint = use_torch_checkpoint
+ self.skip_connection = skip_connection
+ self.layers = ModuleList([])
+ transformer_kwargs = dict(dim=dim, heads=heads, dim_head=dim_head, attn_dropout=attn_dropout, ff_dropout=ff_dropout, flash_attn=flash_attn)
+ time_rotary_embed = RotaryEmbedding(dim=dim_head)
+ freq_rotary_embed = RotaryEmbedding(dim=dim_head)
+ for _ in range(depth):
+ tran_modules = []
+ if linear_transformer_depth > 0:
+ tran_modules.append(Transformer(depth=linear_transformer_depth, linear_attn=True, **transformer_kwargs))
+ tran_modules.append(Transformer(depth=time_transformer_depth, rotary_embed=time_rotary_embed, **transformer_kwargs))
+ tran_modules.append(Transformer(depth=freq_transformer_depth, rotary_embed=freq_rotary_embed, **transformer_kwargs))
+ self.layers.append(nn.ModuleList(tran_modules))
+ self.stft_window_fn = partial(default(stft_window_fn, torch.hann_window), stft_win_length)
+ self.stft_kwargs = dict(n_fft=stft_n_fft, hop_length=stft_hop_length, win_length=stft_win_length, normalized=stft_normalized)
+ freqs = torch.stft(torch.randn(1, 4096), **self.stft_kwargs, window=torch.ones(stft_n_fft), return_complex=True).shape[1]
+ mel_filter_bank_numpy = filters.mel(sr=sample_rate, n_fft=stft_n_fft, n_mels=num_bands)
+ mel_filter_bank = torch.from_numpy(mel_filter_bank_numpy)
+ mel_filter_bank[0][0] = 1.0
+ mel_filter_bank[-1, -1] = 1.0
+ freqs_per_band = mel_filter_bank > 0
+ assert freqs_per_band.any(dim=0).all(), 'all frequencies need to be covered by all bands for now'
+ repeated_freq_indices = repeat(torch.arange(freqs), 'f -> b f', b=num_bands)
+ freq_indices = repeated_freq_indices[freqs_per_band]
+ if stereo:
+ freq_indices = repeat(freq_indices, 'f -> f s', s=2)
+ freq_indices = freq_indices * 2 + torch.arange(2)
+ freq_indices = rearrange(freq_indices, 'f s -> (f s)')
+ self.register_buffer('freq_indices', freq_indices, persistent=False)
+ self.register_buffer('freqs_per_band', freqs_per_band, persistent=False)
+ num_freqs_per_band = reduce(freqs_per_band, 'b f -> b', 'sum')
+ num_bands_per_freq = reduce(freqs_per_band, 'b f -> f', 'sum')
+ self.register_buffer('num_freqs_per_band', num_freqs_per_band, persistent=False)
+ self.register_buffer('num_bands_per_freq', num_bands_per_freq, persistent=False)
+ freqs_per_bands_with_complex = tuple((2 * f * self.audio_channels for f in num_freqs_per_band.tolist()))
+ self.band_split = BandSplit(dim=dim, dim_inputs=freqs_per_bands_with_complex)
+ self.mask_estimators = nn.ModuleList([])
+ for _ in range(num_stems):
+ mask_estimator = MaskEstimator(dim=dim, dim_inputs=freqs_per_bands_with_complex, depth=mask_estimator_depth, mlp_expansion_factor=mlp_expansion_factor)
+ self.mask_estimators.append(mask_estimator)
+ self.multi_stft_resolution_loss_weight = multi_stft_resolution_loss_weight
+ self.multi_stft_resolutions_window_sizes = multi_stft_resolutions_window_sizes
+ self.multi_stft_n_fft = stft_n_fft
+ self.multi_stft_window_fn = multi_stft_window_fn
+ self.multi_stft_kwargs = dict(hop_length=multi_stft_hop_size, normalized=multi_stft_normalized)
+ self.match_input_audio_length = match_input_audio_length
+
+ def forward(self, raw_audio, target=None, return_loss_breakdown=False):
+ """
+ einops
+
+ b - batch
+ f - freq
+ t - time
+ s - audio channel (1 for mono, 2 for stereo)
+ n - number of 'stems'
+ c - complex (2)
+ d - feature dimension
+ """
+ device = raw_audio.device
+ x_is_dml = device.type == 'privateuseone'
+ if raw_audio.ndim == 2:
+ raw_audio = rearrange(raw_audio, 'b t -> b 1 t')
+ (batch, channels, raw_audio_length) = raw_audio.shape
+ istft_length = raw_audio_length if self.match_input_audio_length else None
+ assert not self.stereo and channels == 1 or (self.stereo and channels == 2), 'stereo needs to be set to True if passing in audio signal that is stereo (channel dimension of 2). also need to be False if mono (channel dimension of 1)'
+ (raw_audio, batch_audio_channel_packed_shape) = pack_one(raw_audio, '* t')
+ if x_is_dml:
+ # DirectML has no STFT or complex tensor support. Build the real
+ # spectral features on CPU, then run the learned network on DML.
+ stft_window = self.stft_window_fn(device='cpu')
+ stft_complex = torch.stft(
+ raw_audio.cpu(),
+ **self.stft_kwargs,
+ window=stft_window,
+ return_complex=True,
+ )
+ stft_repr = torch.view_as_real(stft_complex)
+ stft_repr = unpack_one(
+ stft_repr, batch_audio_channel_packed_shape, '* f t c'
+ )
+ stft_repr = rearrange(stft_repr, 'b s f t c -> b (f s) t c')
+ batch_arange = torch.arange(batch)[..., None]
+ x = stft_repr[batch_arange, self.freq_indices.cpu()].to(device)
+ else:
+ stft_window = self.stft_window_fn(device=device)
+ stft_repr = torch.stft(raw_audio, **self.stft_kwargs, window=stft_window, return_complex=True)
+ stft_repr = torch.view_as_real(stft_repr)
+ stft_repr = unpack_one(stft_repr, batch_audio_channel_packed_shape, '* f t c')
+ stft_repr = rearrange(stft_repr, 'b s f t c -> b (f s) t c')
+ batch_arange = torch.arange(batch, device=device)[..., None]
+ x = stft_repr[batch_arange, self.freq_indices]
+ x = rearrange(x, 'b f t c -> b t (f c)')
+ if self.use_torch_checkpoint:
+ x = checkpoint(self.band_split, x, use_reentrant=False)
+ else:
+ x = self.band_split(x)
+ store = [None] * len(self.layers)
+ for (i, transformer_block) in enumerate(self.layers):
+ if len(transformer_block) == 3:
+ (linear_transformer, time_transformer, freq_transformer) = transformer_block
+ (x, ft_ps) = pack([x], 'b * d')
+ if self.use_torch_checkpoint:
+ x = checkpoint(linear_transformer, x, use_reentrant=False)
+ else:
+ x = linear_transformer(x)
+ (x,) = unpack(x, ft_ps, 'b * d')
+ else:
+ (time_transformer, freq_transformer) = transformer_block
+ if self.skip_connection:
+ for j in range(i):
+ x = x + store[j]
+ x = rearrange(x, 'b t f d -> b f t d')
+ (x, ps) = pack([x], '* t d')
+ if self.use_torch_checkpoint:
+ x = checkpoint(time_transformer, x, use_reentrant=False)
+ else:
+ x = time_transformer(x)
+ (x,) = unpack(x, ps, '* t d')
+ x = rearrange(x, 'b f t d -> b t f d')
+ (x, ps) = pack([x], '* f d')
+ if self.use_torch_checkpoint:
+ x = checkpoint(freq_transformer, x, use_reentrant=False)
+ else:
+ x = freq_transformer(x)
+ (x,) = unpack(x, ps, '* f d')
+ if self.skip_connection:
+ store[i] = x
+ num_stems = len(self.mask_estimators)
+ if self.use_torch_checkpoint:
+ masks = torch.stack([checkpoint(fn, x, use_reentrant=False) for fn in self.mask_estimators], dim=1)
+ else:
+ masks = torch.stack([fn(x) for fn in self.mask_estimators], dim=1)
+ masks = rearrange(masks, 'b n t (f c) -> b n f t c', c=2)
+ if x_is_dml:
+ masks = masks.float().cpu()
+ stft_repr = rearrange(stft_repr, 'b f t c -> b 1 f t c')
+ stft_repr = torch.view_as_complex(stft_repr.contiguous())
+ masks = torch.view_as_complex(masks.contiguous())
+ masks = masks.type(stft_repr.dtype)
+ freq_indices = self.freq_indices.cpu() if x_is_dml else self.freq_indices
+ scatter_indices = repeat(freq_indices, 'f -> b n f t', b=batch, n=num_stems, t=stft_repr.shape[-1])
+ stft_repr_expanded_stems = repeat(stft_repr, 'b 1 ... -> b n ...', n=num_stems)
+ masks_summed = torch.zeros_like(stft_repr_expanded_stems).scatter_add_(2, scatter_indices, masks)
+ num_bands_per_freq = self.num_bands_per_freq.cpu() if x_is_dml else self.num_bands_per_freq
+ denom = repeat(num_bands_per_freq, 'f -> (f r) 1', r=channels)
+ masks_averaged = masks_summed / denom.clamp(min=1e-08)
+ stft_repr = stft_repr * masks_averaged
+ stft_repr = rearrange(stft_repr, 'b n (f s) t -> (b n s) f t', s=self.audio_channels)
+ recon_audio = torch.istft(stft_repr, **self.stft_kwargs, window=stft_window, return_complex=False, length=istft_length)
+ if x_is_dml:
+ recon_audio = recon_audio.to(device)
+ recon_audio = rearrange(recon_audio, '(b n s) t -> b n s t', b=batch, s=self.audio_channels, n=num_stems)
+ if num_stems == 1:
+ recon_audio = rearrange(recon_audio, 'b 1 s t -> b s t')
+ if not exists(target):
+ return recon_audio
+ if self.num_stems > 1:
+ assert target.ndim == 4 and target.shape[1] == self.num_stems
+ if target.ndim == 2:
+ target = rearrange(target, '... t -> ... 1 t')
+ target = target[..., :recon_audio.shape[-1]]
+ loss_audio = recon_audio.cpu() if x_is_dml else recon_audio
+ loss_target = target.cpu() if x_is_dml else target
+ loss = F.l1_loss(loss_audio, loss_target)
+ multi_stft_resolution_loss = 0.0
+ for window_size in self.multi_stft_resolutions_window_sizes:
+ spectral_device = 'cpu' if x_is_dml else device
+ res_stft_kwargs = dict(n_fft=max(window_size, self.multi_stft_n_fft), win_length=window_size, return_complex=True, window=self.multi_stft_window_fn(window_size, device=spectral_device), **self.multi_stft_kwargs)
+ recon_Y = torch.stft(rearrange(loss_audio, '... s t -> (... s) t'), **res_stft_kwargs)
+ target_Y = torch.stft(rearrange(loss_target, '... s t -> (... s) t'), **res_stft_kwargs)
+ multi_stft_resolution_loss = multi_stft_resolution_loss + F.l1_loss(recon_Y, target_Y)
+ weighted_multi_resolution_loss = multi_stft_resolution_loss * self.multi_stft_resolution_loss_weight
+ total_loss = loss + weighted_multi_resolution_loss
+ if not return_loss_breakdown:
+ return total_loss
+ return (total_loss, (loss, multi_stft_resolution_loss))
diff --git a/tools/uvr5/bsroformer.py b/tools/uvr5/bsroformer.py
new file mode 100644
index 0000000..ed595e6
--- /dev/null
+++ b/tools/uvr5/bsroformer.py
@@ -0,0 +1,317 @@
+# This code is modified from https://github.com/ZFTurbo/
+import os
+import warnings
+from contextlib import nullcontext
+
+import librosa
+import numpy as np
+import soundfile as sf
+import torch
+import torch.nn as nn
+import yaml
+from tqdm import tqdm
+from tools.file_io import read_text
+from i18n.i18n import I18nAuto
+
+warnings.filterwarnings("ignore")
+i18n = I18nAuto()
+
+
+class Roformer_Loader:
+ def get_config(self, config_path):
+ return yaml.load(read_text(config_path), Loader=yaml.FullLoader)
+
+ def get_default_config(self):
+ default_config = None
+ if self.model_type == "bs_roformer":
+ # Use model_bs_roformer_ep_368_sdr_12.9628.yaml and model_bs_roformer_ep_317_sdr_12.9755.yaml as default configuration files
+ # Other BS_Roformer models may not be compatible
+ # fmt: off
+ default_config = {
+ "audio": {"chunk_size": 352800, "sample_rate": 44100},
+ "model": {
+ "dim": 512,
+ "depth": 12,
+ "stereo": True,
+ "num_stems": 1,
+ "time_transformer_depth": 1,
+ "freq_transformer_depth": 1,
+ "linear_transformer_depth": 0,
+ "freqs_per_bands": (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 12, 12, 12, 12, 12, 12, 12, 24, 24, 24, 24, 24, 24, 24, 24, 48, 48, 48, 48, 48, 48, 48, 48, 128, 129),
+ "dim_head": 64,
+ "heads": 8,
+ "attn_dropout": 0.1,
+ "ff_dropout": 0.1,
+ "flash_attn": True,
+ "dim_freqs_in": 1025,
+ "stft_n_fft": 2048,
+ "stft_hop_length": 441,
+ "stft_win_length": 2048,
+ "stft_normalized": False,
+ "mask_estimator_depth": 2,
+ "multi_stft_resolution_loss_weight": 1.0,
+ "multi_stft_resolutions_window_sizes": (4096, 2048, 1024, 512, 256),
+ "multi_stft_hop_size": 147,
+ "multi_stft_normalized": False,
+ },
+ "training": {"instruments": ["vocals", "other"], "target_instrument": "vocals"},
+ "inference": {"batch_size": 2, "num_overlap": 2},
+ }
+ # fmt: on
+ elif self.model_type == "mel_band_roformer":
+ # Use model_mel_band_roformer_ep_3005_sdr_11.4360.yaml as default configuration files
+ # Other Mel_Band_Roformer models may not be compatible
+ default_config = {
+ "audio": {"chunk_size": 352800, "sample_rate": 44100},
+ "model": {
+ "dim": 384,
+ "depth": 12,
+ "stereo": True,
+ "num_stems": 1,
+ "time_transformer_depth": 1,
+ "freq_transformer_depth": 1,
+ "linear_transformer_depth": 0,
+ "num_bands": 60,
+ "dim_head": 64,
+ "heads": 8,
+ "attn_dropout": 0.1,
+ "ff_dropout": 0.1,
+ "flash_attn": True,
+ "dim_freqs_in": 1025,
+ "sample_rate": 44100,
+ "stft_n_fft": 2048,
+ "stft_hop_length": 441,
+ "stft_win_length": 2048,
+ "stft_normalized": False,
+ "mask_estimator_depth": 2,
+ "multi_stft_resolution_loss_weight": 1.0,
+ "multi_stft_resolutions_window_sizes": (4096, 2048, 1024, 512, 256),
+ "multi_stft_hop_size": 147,
+ "multi_stft_normalized": False,
+ },
+ "training": {"instruments": ["vocals", "other"], "target_instrument": "vocals"},
+ "inference": {"batch_size": 2, "num_overlap": 2},
+ }
+
+ return default_config
+
+ def get_model_from_config(self):
+ if self.model_type == "bs_roformer":
+ from tools.uvr5.bs_roformer.bs_roformer import BSRoformer
+
+ model = BSRoformer(**dict(self.config["model"]))
+ elif self.model_type == "mel_band_roformer":
+ from tools.uvr5.bs_roformer.mel_band_roformer import MelBandRoformer
+
+ model = MelBandRoformer(**dict(self.config["model"]))
+ else:
+ print(i18n("错误:未知模型:%s") % self.model_type)
+ model = None
+ return model
+
+ def demix_track(self, model, mix, device):
+ C = self.config["audio"]["chunk_size"] # chunk_size
+ N = self.config["inference"]["num_overlap"]
+ fade_size = C // 10
+ step = int(C // N)
+ border = C - step
+ batch_size = self.config["inference"]["batch_size"]
+
+ length_init = mix.shape[-1]
+ progress_bar = tqdm(total=length_init // step + 1, desc="Processing", leave=False)
+
+ # Do pad from the beginning and end to account floating window results better
+ if length_init > 2 * border and (border > 0):
+ mix = nn.functional.pad(mix, (border, border), mode="reflect")
+
+ # Prepare windows arrays (do 1 time for speed up). This trick repairs click problems on the edges of segment
+ window_size = C
+ fadein = torch.linspace(0, 1, fade_size)
+ fadeout = torch.linspace(1, 0, fade_size)
+ window_start = torch.ones(window_size)
+ window_middle = torch.ones(window_size)
+ window_finish = torch.ones(window_size)
+ window_start[-fade_size:] *= fadeout # First audio chunk, no fadein
+ window_finish[:fade_size] *= fadein # Last audio chunk, no fadeout
+ window_middle[-fade_size:] *= fadeout
+ window_middle[:fade_size] *= fadein
+
+ device_type = device.type if isinstance(device, torch.device) else torch.device(device).type
+ amp_context = (
+ torch.amp.autocast("cuda") if device_type == "cuda" else nullcontext()
+ )
+ grad_context = (
+ torch.no_grad()
+ if device_type == "privateuseone"
+ else torch.inference_mode()
+ )
+ with amp_context:
+ # DirectML updates version counters in several linear kernels and
+ # therefore needs no_grad rather than inference_mode. CUDA and CPU
+ # retain the existing inference-mode path.
+ with grad_context:
+ if self.config["training"]["target_instrument"] is None:
+ req_shape = (len(self.config["training"]["instruments"]),) + tuple(mix.shape)
+ else:
+ req_shape = (1,) + tuple(mix.shape)
+
+ result = torch.zeros(req_shape, dtype=torch.float32)
+ counter = torch.zeros(req_shape, dtype=torch.float32)
+ i = 0
+ batch_data = []
+ batch_locations = []
+ while i < mix.shape[1]:
+ part = mix[:, i : i + C].to(device)
+ length = part.shape[-1]
+ if length < C:
+ if length > C // 2 + 1:
+ part = nn.functional.pad(input=part, pad=(0, C - length), mode="reflect")
+ else:
+ part = nn.functional.pad(input=part, pad=(0, C - length, 0, 0), mode="constant", value=0)
+ if self.is_half:
+ part = part.half()
+ batch_data.append(part)
+ batch_locations.append((i, length))
+ i += step
+ progress_bar.update(1)
+
+ if len(batch_data) >= batch_size or (i >= mix.shape[1]):
+ arr = torch.stack(batch_data, dim=0)
+ # print(23333333,arr.dtype)
+ x = model(arr)
+
+ window = window_middle
+ if i - step == 0: # First audio chunk, no fadein
+ window = window_start
+ elif i >= mix.shape[1]: # Last audio chunk, no fadeout
+ window = window_finish
+
+ for j in range(len(batch_locations)):
+ start, l = batch_locations[j]
+ result[..., start : start + l] += x[j][..., :l].cpu() * window[..., :l]
+ counter[..., start : start + l] += window[..., :l]
+
+ batch_data = []
+ batch_locations = []
+
+ estimated_sources = result / counter
+ estimated_sources = estimated_sources.cpu().numpy()
+ np.nan_to_num(estimated_sources, copy=False, nan=0.0)
+
+ if length_init > 2 * border and (border > 0):
+ # Remove pad
+ estimated_sources = estimated_sources[..., border:-border]
+
+ progress_bar.close()
+
+ if self.config["training"]["target_instrument"] is None:
+ return {k: v for k, v in zip(self.config["training"]["instruments"], estimated_sources)}
+ else:
+ return {k: v for k, v in zip([self.config["training"]["target_instrument"]], estimated_sources)}
+
+ def run_folder(self, input, vocal_root, others_root, format):
+ self.model.eval()
+ path = input
+ os.makedirs(vocal_root, exist_ok=True)
+ os.makedirs(others_root, exist_ok=True)
+ file_base_name = os.path.splitext(os.path.basename(path))[0]
+
+ sample_rate = 44100
+ if "sample_rate" in self.config["audio"]:
+ sample_rate = self.config["audio"]["sample_rate"]
+
+ try:
+ mix, sr = librosa.load(path, sr=sample_rate, mono=False)
+ except Exception as e:
+ print(i18n("无法读取音频:%s") % path)
+ print(i18n("错误信息:%s") % str(e))
+ return
+
+ # in case if model only supports mono tracks
+ isstereo = self.config["model"].get("stereo", True)
+ if not isstereo and len(mix.shape) != 1:
+ mix = np.mean(mix, axis=0) # if more than 2 channels, take mean
+ print(i18n("音频包含多个声道,但模型仅支持单声道,将对所有声道取平均值"))
+
+ mix_orig = mix.copy()
+
+ mixture = torch.tensor(mix, dtype=torch.float32)
+ res = self.demix_track(self.model, mixture, self.device)
+
+ if self.config["training"]["target_instrument"] is not None:
+ # if target instrument is specified, save target instrument as vocal and other instruments as others
+ # other instruments are caculated by subtracting target instrument from mixture
+ target_instrument = self.config["training"]["target_instrument"]
+ other_instruments = [i for i in self.config["training"]["instruments"] if i != target_instrument]
+ other = mix_orig - res[target_instrument] # caculate other instruments
+
+ path_vocal = "{}/{}_{}.wav".format(vocal_root, file_base_name, target_instrument)
+ path_other = "{}/{}_{}.wav".format(others_root, file_base_name, other_instruments[0])
+ self.save_audio(path_vocal, res[target_instrument].T, sr, format)
+ self.save_audio(path_other, other.T, sr, format)
+ else:
+ # if target instrument is not specified, save the first instrument as vocal and the rest as others
+ vocal_inst = self.config["training"]["instruments"][0]
+ path_vocal = "{}/{}_{}.wav".format(vocal_root, file_base_name, vocal_inst)
+ self.save_audio(path_vocal, res[vocal_inst].T, sr, format)
+ for other in self.config["training"]["instruments"][1:]: # save other instruments
+ path_other = "{}/{}_{}.wav".format(others_root, file_base_name, other)
+ self.save_audio(path_other, res[other].T, sr, format)
+
+ def save_audio(self, path, data, sr, format):
+ # input path should be endwith '.wav'
+ if format in ["wav", "flac"]:
+ if format == "flac":
+ path = path[:-3] + "flac"
+ sf.write(path, data, sr)
+ else:
+ sf.write(path, data, sr)
+ os.system('ffmpeg -i "{}" -vn "{}" -q:a 2 -y'.format(path, path[:-3] + format))
+ try:
+ os.remove(path)
+ except:
+ pass
+
+ def __init__(self, model_path, config_path, device, is_half):
+ self.device = device
+ self.is_half = is_half
+ self.model_type = None
+ self.config = None
+
+ # get model_type, first try:
+ if "bs_roformer" in model_path.lower() or "bsroformer" in model_path.lower():
+ self.model_type = "bs_roformer"
+ elif "mel_band_roformer" in model_path.lower() or "melbandroformer" in model_path.lower():
+ self.model_type = "mel_band_roformer"
+
+ if not os.path.exists(config_path):
+ if self.model_type is None:
+ # if model_type is still None, raise an error
+ raise ValueError(
+ "Error: Unknown model type. If you are using a model without a configuration file, Ensure that your model name includes 'bs_roformer', 'bsroformer', 'mel_band_roformer', or 'melbandroformer'. Otherwise, you can manually place the model configuration file into 'tools/uvr5/uvr5w_weights' and ensure that the configuration file is named as '.yaml' then try it again."
+ )
+ self.config = self.get_default_config()
+ else:
+ # if there is a configuration file
+ self.config = self.get_config(config_path)
+ if self.model_type is None:
+ # if model_type is still None, second try, get model_type from the configuration file
+ if "freqs_per_bands" in self.config["model"]:
+ # if freqs_per_bands in config, it's a bs_roformer model
+ self.model_type = "bs_roformer"
+ else:
+ # else it's a mel_band_roformer model
+ self.model_type = "mel_band_roformer"
+
+ print(i18n("检测到模型类型:%s") % self.model_type)
+ model = self.get_model_from_config()
+ state_dict = torch.load(model_path, map_location="cpu")
+ model.load_state_dict(state_dict)
+
+ if is_half == False:
+ self.model = model.to(device)
+ else:
+ self.model = model.half().to(device)
+
+ def _path_audio_(self, input, others_root, vocal_root, format, is_hp3=False):
+ self.run_folder(input, vocal_root, others_root, format)
diff --git a/tools/uvr5/lib/lib_v5/layers_123821KB.py b/tools/uvr5/lib/lib_v5/layers_123821KB.py
new file mode 100644
index 0000000..2b9101e
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/layers_123821KB.py
@@ -0,0 +1,106 @@
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from . import spec_utils
+
+
+class Conv2DBNActiv(nn.Module):
+ def __init__(self, nin, nout, ksize=3, stride=1, pad=1, dilation=1, activ=nn.ReLU):
+ super(Conv2DBNActiv, self).__init__()
+ self.conv = nn.Sequential(
+ nn.Conv2d(
+ nin,
+ nout,
+ kernel_size=ksize,
+ stride=stride,
+ padding=pad,
+ dilation=dilation,
+ bias=False,
+ ),
+ nn.BatchNorm2d(nout),
+ activ(),
+ )
+
+ def __call__(self, x):
+ return self.conv(x)
+
+
+class SeperableConv2DBNActiv(nn.Module):
+ def __init__(self, nin, nout, ksize=3, stride=1, pad=1, dilation=1, activ=nn.ReLU):
+ super(SeperableConv2DBNActiv, self).__init__()
+ self.conv = nn.Sequential(
+ nn.Conv2d(
+ nin,
+ nin,
+ kernel_size=ksize,
+ stride=stride,
+ padding=pad,
+ dilation=dilation,
+ groups=nin,
+ bias=False,
+ ),
+ nn.Conv2d(nin, nout, kernel_size=1, bias=False),
+ nn.BatchNorm2d(nout),
+ activ(),
+ )
+
+ def __call__(self, x):
+ return self.conv(x)
+
+
+class Encoder(nn.Module):
+ def __init__(self, nin, nout, ksize=3, stride=1, pad=1, activ=nn.LeakyReLU):
+ super(Encoder, self).__init__()
+ self.conv1 = Conv2DBNActiv(nin, nout, ksize, 1, pad, activ=activ)
+ self.conv2 = Conv2DBNActiv(nout, nout, ksize, stride, pad, activ=activ)
+
+ def __call__(self, x):
+ skip = self.conv1(x)
+ h = self.conv2(skip)
+
+ return h, skip
+
+
+class Decoder(nn.Module):
+ def __init__(self, nin, nout, ksize=3, stride=1, pad=1, activ=nn.ReLU, dropout=False):
+ super(Decoder, self).__init__()
+ self.conv = Conv2DBNActiv(nin, nout, ksize, 1, pad, activ=activ)
+ self.dropout = nn.Dropout2d(0.1) if dropout else None
+
+ def __call__(self, x, skip=None):
+ x = F.interpolate(x, scale_factor=2, mode="bilinear", align_corners=True)
+ if skip is not None:
+ skip = spec_utils.crop_center(skip, x)
+ x = torch.cat([x, skip], dim=1)
+ h = self.conv(x)
+
+ if self.dropout is not None:
+ h = self.dropout(h)
+
+ return h
+
+
+class ASPPModule(nn.Module):
+ def __init__(self, nin, nout, dilations=(4, 8, 16), activ=nn.ReLU):
+ super(ASPPModule, self).__init__()
+ self.conv1 = nn.Sequential(
+ nn.AdaptiveAvgPool2d((1, None)),
+ Conv2DBNActiv(nin, nin, 1, 1, 0, activ=activ),
+ )
+ self.conv2 = Conv2DBNActiv(nin, nin, 1, 1, 0, activ=activ)
+ self.conv3 = SeperableConv2DBNActiv(nin, nin, 3, 1, dilations[0], dilations[0], activ=activ)
+ self.conv4 = SeperableConv2DBNActiv(nin, nin, 3, 1, dilations[1], dilations[1], activ=activ)
+ self.conv5 = SeperableConv2DBNActiv(nin, nin, 3, 1, dilations[2], dilations[2], activ=activ)
+ self.bottleneck = nn.Sequential(Conv2DBNActiv(nin * 5, nout, 1, 1, 0, activ=activ), nn.Dropout2d(0.1))
+
+ def forward(self, x):
+ _, _, h, w = x.size()
+ feat1 = F.interpolate(self.conv1(x), size=(h, w), mode="bilinear", align_corners=True)
+ feat2 = self.conv2(x)
+ feat3 = self.conv3(x)
+ feat4 = self.conv4(x)
+ feat5 = self.conv5(x)
+ out = torch.cat((feat1, feat2, feat3, feat4, feat5), dim=1)
+ bottle = self.bottleneck(out)
+ return bottle
diff --git a/tools/uvr5/lib/lib_v5/layers_new.py b/tools/uvr5/lib/lib_v5/layers_new.py
new file mode 100644
index 0000000..7d7005c
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/layers_new.py
@@ -0,0 +1,111 @@
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from . import spec_utils
+
+
+class Conv2DBNActiv(nn.Module):
+ def __init__(self, nin, nout, ksize=3, stride=1, pad=1, dilation=1, activ=nn.ReLU):
+ super(Conv2DBNActiv, self).__init__()
+ self.conv = nn.Sequential(
+ nn.Conv2d(
+ nin,
+ nout,
+ kernel_size=ksize,
+ stride=stride,
+ padding=pad,
+ dilation=dilation,
+ bias=False,
+ ),
+ nn.BatchNorm2d(nout),
+ activ(),
+ )
+
+ def __call__(self, x):
+ return self.conv(x)
+
+
+class Encoder(nn.Module):
+ def __init__(self, nin, nout, ksize=3, stride=1, pad=1, activ=nn.LeakyReLU):
+ super(Encoder, self).__init__()
+ self.conv1 = Conv2DBNActiv(nin, nout, ksize, stride, pad, activ=activ)
+ self.conv2 = Conv2DBNActiv(nout, nout, ksize, 1, pad, activ=activ)
+
+ def __call__(self, x):
+ h = self.conv1(x)
+ h = self.conv2(h)
+
+ return h
+
+
+class Decoder(nn.Module):
+ def __init__(self, nin, nout, ksize=3, stride=1, pad=1, activ=nn.ReLU, dropout=False):
+ super(Decoder, self).__init__()
+ self.conv1 = Conv2DBNActiv(nin, nout, ksize, 1, pad, activ=activ)
+ # self.conv2 = Conv2DBNActiv(nout, nout, ksize, 1, pad, activ=activ)
+ self.dropout = nn.Dropout2d(0.1) if dropout else None
+
+ def __call__(self, x, skip=None):
+ x = F.interpolate(x, scale_factor=2, mode="bilinear", align_corners=True)
+
+ if skip is not None:
+ skip = spec_utils.crop_center(skip, x)
+ x = torch.cat([x, skip], dim=1)
+
+ h = self.conv1(x)
+ # h = self.conv2(h)
+
+ if self.dropout is not None:
+ h = self.dropout(h)
+
+ return h
+
+
+class ASPPModule(nn.Module):
+ def __init__(self, nin, nout, dilations=(4, 8, 12), activ=nn.ReLU, dropout=False):
+ super(ASPPModule, self).__init__()
+ self.conv1 = nn.Sequential(
+ nn.AdaptiveAvgPool2d((1, None)),
+ Conv2DBNActiv(nin, nout, 1, 1, 0, activ=activ),
+ )
+ self.conv2 = Conv2DBNActiv(nin, nout, 1, 1, 0, activ=activ)
+ self.conv3 = Conv2DBNActiv(nin, nout, 3, 1, dilations[0], dilations[0], activ=activ)
+ self.conv4 = Conv2DBNActiv(nin, nout, 3, 1, dilations[1], dilations[1], activ=activ)
+ self.conv5 = Conv2DBNActiv(nin, nout, 3, 1, dilations[2], dilations[2], activ=activ)
+ self.bottleneck = Conv2DBNActiv(nout * 5, nout, 1, 1, 0, activ=activ)
+ self.dropout = nn.Dropout2d(0.1) if dropout else None
+
+ def forward(self, x):
+ _, _, h, w = x.size()
+ feat1 = F.interpolate(self.conv1(x), size=(h, w), mode="bilinear", align_corners=True)
+ feat2 = self.conv2(x)
+ feat3 = self.conv3(x)
+ feat4 = self.conv4(x)
+ feat5 = self.conv5(x)
+ out = torch.cat((feat1, feat2, feat3, feat4, feat5), dim=1)
+ out = self.bottleneck(out)
+
+ if self.dropout is not None:
+ out = self.dropout(out)
+
+ return out
+
+
+class LSTMModule(nn.Module):
+ def __init__(self, nin_conv, nin_lstm, nout_lstm):
+ super(LSTMModule, self).__init__()
+ self.conv = Conv2DBNActiv(nin_conv, 1, 1, 1, 0)
+ self.lstm = nn.LSTM(input_size=nin_lstm, hidden_size=nout_lstm // 2, bidirectional=True)
+ self.dense = nn.Sequential(nn.Linear(nout_lstm, nin_lstm), nn.BatchNorm1d(nin_lstm), nn.ReLU())
+
+ def forward(self, x):
+ N, _, nbins, nframes = x.size()
+ h = self.conv(x)[:, 0] # N, nbins, nframes
+ h = h.permute(2, 0, 1) # nframes, N, nbins
+ h, _ = self.lstm(h)
+ h = self.dense(h.reshape(-1, h.size()[-1])) # nframes * N, nbins
+ h = h.reshape(nframes, N, 1, nbins)
+ h = h.permute(1, 2, 3, 0)
+
+ return h
diff --git a/tools/uvr5/lib/lib_v5/model_param_init.py b/tools/uvr5/lib/lib_v5/model_param_init.py
new file mode 100644
index 0000000..0708f1f
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/model_param_init.py
@@ -0,0 +1,68 @@
+import json
+import pathlib
+from tools.file_io import read_text
+
+default_param = {}
+default_param["bins"] = 768
+default_param["unstable_bins"] = 9 # training only
+default_param["reduction_bins"] = 762 # training only
+default_param["sr"] = 44100
+default_param["pre_filter_start"] = 757
+default_param["pre_filter_stop"] = 768
+default_param["band"] = {}
+
+
+default_param["band"][1] = {
+ "sr": 11025,
+ "hl": 128,
+ "n_fft": 960,
+ "crop_start": 0,
+ "crop_stop": 245,
+ "lpf_start": 61, # inference only
+ "res_type": "polyphase",
+}
+
+default_param["band"][2] = {
+ "sr": 44100,
+ "hl": 512,
+ "n_fft": 1536,
+ "crop_start": 24,
+ "crop_stop": 547,
+ "hpf_start": 81, # inference only
+ "res_type": "sinc_best",
+}
+
+
+def int_keys(d):
+ r = {}
+ for k, v in d:
+ if k.isdigit():
+ k = int(k)
+ r[k] = v
+ return r
+
+
+class ModelParameters(object):
+ def __init__(self, config_path=""):
+ if ".pth" == pathlib.Path(config_path).suffix:
+ import zipfile
+
+ with zipfile.ZipFile(config_path, "r") as zip:
+ self.param = json.loads(zip.read("param.json"), object_pairs_hook=int_keys)
+ elif ".json" == pathlib.Path(config_path).suffix:
+ self.param = json.loads(
+ read_text(config_path), object_pairs_hook=int_keys
+ )
+ else:
+ self.param = default_param
+
+ for k in [
+ "mid_side",
+ "mid_side_b",
+ "mid_side_b2",
+ "stereo_w",
+ "stereo_n",
+ "reverse",
+ ]:
+ if k not in self.param:
+ self.param[k] = False
diff --git a/tools/uvr5/lib/lib_v5/modelparams/4band_v2.json b/tools/uvr5/lib/lib_v5/modelparams/4band_v2.json
new file mode 100644
index 0000000..33281a0
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/modelparams/4band_v2.json
@@ -0,0 +1,54 @@
+{
+ "bins": 672,
+ "unstable_bins": 8,
+ "reduction_bins": 637,
+ "band": {
+ "1": {
+ "sr": 7350,
+ "hl": 80,
+ "n_fft": 640,
+ "crop_start": 0,
+ "crop_stop": 85,
+ "lpf_start": 25,
+ "lpf_stop": 53,
+ "res_type": "polyphase"
+ },
+ "2": {
+ "sr": 7350,
+ "hl": 80,
+ "n_fft": 320,
+ "crop_start": 4,
+ "crop_stop": 87,
+ "hpf_start": 25,
+ "hpf_stop": 12,
+ "lpf_start": 31,
+ "lpf_stop": 62,
+ "res_type": "polyphase"
+ },
+ "3": {
+ "sr": 14700,
+ "hl": 160,
+ "n_fft": 512,
+ "crop_start": 17,
+ "crop_stop": 216,
+ "hpf_start": 48,
+ "hpf_stop": 24,
+ "lpf_start": 139,
+ "lpf_stop": 210,
+ "res_type": "polyphase"
+ },
+ "4": {
+ "sr": 44100,
+ "hl": 480,
+ "n_fft": 960,
+ "crop_start": 78,
+ "crop_stop": 383,
+ "hpf_start": 130,
+ "hpf_stop": 86,
+ "res_type": "kaiser_fast"
+ }
+ },
+ "sr": 44100,
+ "pre_filter_start": 668,
+ "pre_filter_stop": 672
+}
\ No newline at end of file
diff --git a/tools/uvr5/lib/lib_v5/modelparams/4band_v3.json b/tools/uvr5/lib/lib_v5/modelparams/4band_v3.json
new file mode 100644
index 0000000..2a73bc9
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/modelparams/4band_v3.json
@@ -0,0 +1,54 @@
+{
+ "bins": 672,
+ "unstable_bins": 8,
+ "reduction_bins": 530,
+ "band": {
+ "1": {
+ "sr": 7350,
+ "hl": 80,
+ "n_fft": 640,
+ "crop_start": 0,
+ "crop_stop": 85,
+ "lpf_start": 25,
+ "lpf_stop": 53,
+ "res_type": "polyphase"
+ },
+ "2": {
+ "sr": 7350,
+ "hl": 80,
+ "n_fft": 320,
+ "crop_start": 4,
+ "crop_stop": 87,
+ "hpf_start": 25,
+ "hpf_stop": 12,
+ "lpf_start": 31,
+ "lpf_stop": 62,
+ "res_type": "polyphase"
+ },
+ "3": {
+ "sr": 14700,
+ "hl": 160,
+ "n_fft": 512,
+ "crop_start": 17,
+ "crop_stop": 216,
+ "hpf_start": 48,
+ "hpf_stop": 24,
+ "lpf_start": 139,
+ "lpf_stop": 210,
+ "res_type": "polyphase"
+ },
+ "4": {
+ "sr": 44100,
+ "hl": 480,
+ "n_fft": 960,
+ "crop_start": 78,
+ "crop_stop": 383,
+ "hpf_start": 130,
+ "hpf_stop": 86,
+ "res_type": "kaiser_fast"
+ }
+ },
+ "sr": 44100,
+ "pre_filter_start": 668,
+ "pre_filter_stop": 672
+}
\ No newline at end of file
diff --git a/tools/uvr5/lib/lib_v5/nets_61968KB.py b/tools/uvr5/lib/lib_v5/nets_61968KB.py
new file mode 100644
index 0000000..167d4cb
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/nets_61968KB.py
@@ -0,0 +1,122 @@
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from . import layers_123821KB as layers
+
+
+class BaseASPPNet(nn.Module):
+ def __init__(self, nin, ch, dilations=(4, 8, 16)):
+ super(BaseASPPNet, self).__init__()
+ self.enc1 = layers.Encoder(nin, ch, 3, 2, 1)
+ self.enc2 = layers.Encoder(ch, ch * 2, 3, 2, 1)
+ self.enc3 = layers.Encoder(ch * 2, ch * 4, 3, 2, 1)
+ self.enc4 = layers.Encoder(ch * 4, ch * 8, 3, 2, 1)
+
+ self.aspp = layers.ASPPModule(ch * 8, ch * 16, dilations)
+
+ self.dec4 = layers.Decoder(ch * (8 + 16), ch * 8, 3, 1, 1)
+ self.dec3 = layers.Decoder(ch * (4 + 8), ch * 4, 3, 1, 1)
+ self.dec2 = layers.Decoder(ch * (2 + 4), ch * 2, 3, 1, 1)
+ self.dec1 = layers.Decoder(ch * (1 + 2), ch, 3, 1, 1)
+
+ def __call__(self, x):
+ h, e1 = self.enc1(x)
+ h, e2 = self.enc2(h)
+ h, e3 = self.enc3(h)
+ h, e4 = self.enc4(h)
+
+ h = self.aspp(h)
+
+ h = self.dec4(h, e4)
+ h = self.dec3(h, e3)
+ h = self.dec2(h, e2)
+ h = self.dec1(h, e1)
+
+ return h
+
+
+class CascadedASPPNet(nn.Module):
+ def __init__(self, n_fft):
+ super(CascadedASPPNet, self).__init__()
+ self.stg1_low_band_net = BaseASPPNet(2, 32)
+ self.stg1_high_band_net = BaseASPPNet(2, 32)
+
+ self.stg2_bridge = layers.Conv2DBNActiv(34, 16, 1, 1, 0)
+ self.stg2_full_band_net = BaseASPPNet(16, 32)
+
+ self.stg3_bridge = layers.Conv2DBNActiv(66, 32, 1, 1, 0)
+ self.stg3_full_band_net = BaseASPPNet(32, 64)
+
+ self.out = nn.Conv2d(64, 2, 1, bias=False)
+ self.aux1_out = nn.Conv2d(32, 2, 1, bias=False)
+ self.aux2_out = nn.Conv2d(32, 2, 1, bias=False)
+
+ self.max_bin = n_fft // 2
+ self.output_bin = n_fft // 2 + 1
+
+ self.offset = 128
+
+ def forward(self, x, aggressiveness=None):
+ mix = x.detach()
+ x = x.clone()
+
+ x = x[:, :, : self.max_bin]
+
+ bandw = x.size()[2] // 2
+ aux1 = torch.cat(
+ [
+ self.stg1_low_band_net(x[:, :, :bandw]),
+ self.stg1_high_band_net(x[:, :, bandw:]),
+ ],
+ dim=2,
+ )
+
+ h = torch.cat([x, aux1], dim=1)
+ aux2 = self.stg2_full_band_net(self.stg2_bridge(h))
+
+ h = torch.cat([x, aux1, aux2], dim=1)
+ h = self.stg3_full_band_net(self.stg3_bridge(h))
+
+ mask = torch.sigmoid(self.out(h))
+ mask = F.pad(
+ input=mask,
+ pad=(0, 0, 0, self.output_bin - mask.size()[2]),
+ mode="replicate",
+ )
+
+ if self.training:
+ aux1 = torch.sigmoid(self.aux1_out(aux1))
+ aux1 = F.pad(
+ input=aux1,
+ pad=(0, 0, 0, self.output_bin - aux1.size()[2]),
+ mode="replicate",
+ )
+ aux2 = torch.sigmoid(self.aux2_out(aux2))
+ aux2 = F.pad(
+ input=aux2,
+ pad=(0, 0, 0, self.output_bin - aux2.size()[2]),
+ mode="replicate",
+ )
+ return mask * mix, aux1 * mix, aux2 * mix
+ else:
+ if aggressiveness:
+ mask[:, :, : aggressiveness["split_bin"]] = torch.pow(
+ mask[:, :, : aggressiveness["split_bin"]],
+ 1 + aggressiveness["value"] / 3,
+ )
+ mask[:, :, aggressiveness["split_bin"] :] = torch.pow(
+ mask[:, :, aggressiveness["split_bin"] :],
+ 1 + aggressiveness["value"],
+ )
+
+ return mask * mix
+
+ def predict(self, x_mag, aggressiveness=None):
+ h = self.forward(x_mag, aggressiveness)
+
+ if self.offset > 0:
+ h = h[:, :, :, self.offset : -self.offset]
+ assert h.size()[3] > 0
+
+ return h
diff --git a/tools/uvr5/lib/lib_v5/nets_new.py b/tools/uvr5/lib/lib_v5/nets_new.py
new file mode 100644
index 0000000..ba1a559
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/nets_new.py
@@ -0,0 +1,125 @@
+import torch
+import torch.nn.functional as F
+from torch import nn
+
+from . import layers_new
+
+
+class BaseNet(nn.Module):
+ def __init__(self, nin, nout, nin_lstm, nout_lstm, dilations=((4, 2), (8, 4), (12, 6))):
+ super(BaseNet, self).__init__()
+ self.enc1 = layers_new.Conv2DBNActiv(nin, nout, 3, 1, 1)
+ self.enc2 = layers_new.Encoder(nout, nout * 2, 3, 2, 1)
+ self.enc3 = layers_new.Encoder(nout * 2, nout * 4, 3, 2, 1)
+ self.enc4 = layers_new.Encoder(nout * 4, nout * 6, 3, 2, 1)
+ self.enc5 = layers_new.Encoder(nout * 6, nout * 8, 3, 2, 1)
+
+ self.aspp = layers_new.ASPPModule(nout * 8, nout * 8, dilations, dropout=True)
+
+ self.dec4 = layers_new.Decoder(nout * (6 + 8), nout * 6, 3, 1, 1)
+ self.dec3 = layers_new.Decoder(nout * (4 + 6), nout * 4, 3, 1, 1)
+ self.dec2 = layers_new.Decoder(nout * (2 + 4), nout * 2, 3, 1, 1)
+ self.lstm_dec2 = layers_new.LSTMModule(nout * 2, nin_lstm, nout_lstm)
+ self.dec1 = layers_new.Decoder(nout * (1 + 2) + 1, nout * 1, 3, 1, 1)
+
+ def __call__(self, x):
+ e1 = self.enc1(x)
+ e2 = self.enc2(e1)
+ e3 = self.enc3(e2)
+ e4 = self.enc4(e3)
+ e5 = self.enc5(e4)
+
+ h = self.aspp(e5)
+
+ h = self.dec4(h, e4)
+ h = self.dec3(h, e3)
+ h = self.dec2(h, e2)
+ h = torch.cat([h, self.lstm_dec2(h)], dim=1)
+ h = self.dec1(h, e1)
+
+ return h
+
+
+class CascadedNet(nn.Module):
+ def __init__(self, n_fft, nout=32, nout_lstm=128):
+ super(CascadedNet, self).__init__()
+
+ self.max_bin = n_fft // 2
+ self.output_bin = n_fft // 2 + 1
+ self.nin_lstm = self.max_bin // 2
+ self.offset = 64
+
+ self.stg1_low_band_net = nn.Sequential(
+ BaseNet(2, nout // 2, self.nin_lstm // 2, nout_lstm),
+ layers_new.Conv2DBNActiv(nout // 2, nout // 4, 1, 1, 0),
+ )
+
+ self.stg1_high_band_net = BaseNet(2, nout // 4, self.nin_lstm // 2, nout_lstm // 2)
+
+ self.stg2_low_band_net = nn.Sequential(
+ BaseNet(nout // 4 + 2, nout, self.nin_lstm // 2, nout_lstm),
+ layers_new.Conv2DBNActiv(nout, nout // 2, 1, 1, 0),
+ )
+ self.stg2_high_band_net = BaseNet(nout // 4 + 2, nout // 2, self.nin_lstm // 2, nout_lstm // 2)
+
+ self.stg3_full_band_net = BaseNet(3 * nout // 4 + 2, nout, self.nin_lstm, nout_lstm)
+
+ self.out = nn.Conv2d(nout, 2, 1, bias=False)
+ self.aux_out = nn.Conv2d(3 * nout // 4, 2, 1, bias=False)
+
+ def forward(self, x):
+ x = x[:, :, : self.max_bin]
+
+ bandw = x.size()[2] // 2
+ l1_in = x[:, :, :bandw]
+ h1_in = x[:, :, bandw:]
+ l1 = self.stg1_low_band_net(l1_in)
+ h1 = self.stg1_high_band_net(h1_in)
+ aux1 = torch.cat([l1, h1], dim=2)
+
+ l2_in = torch.cat([l1_in, l1], dim=1)
+ h2_in = torch.cat([h1_in, h1], dim=1)
+ l2 = self.stg2_low_band_net(l2_in)
+ h2 = self.stg2_high_band_net(h2_in)
+ aux2 = torch.cat([l2, h2], dim=2)
+
+ f3_in = torch.cat([x, aux1, aux2], dim=1)
+ f3 = self.stg3_full_band_net(f3_in)
+
+ mask = torch.sigmoid(self.out(f3))
+ mask = F.pad(
+ input=mask,
+ pad=(0, 0, 0, self.output_bin - mask.size()[2]),
+ mode="replicate",
+ )
+
+ if self.training:
+ aux = torch.cat([aux1, aux2], dim=1)
+ aux = torch.sigmoid(self.aux_out(aux))
+ aux = F.pad(
+ input=aux,
+ pad=(0, 0, 0, self.output_bin - aux.size()[2]),
+ mode="replicate",
+ )
+ return mask, aux
+ else:
+ return mask
+
+ def predict_mask(self, x):
+ mask = self.forward(x)
+
+ if self.offset > 0:
+ mask = mask[:, :, :, self.offset : -self.offset]
+ assert mask.size()[3] > 0
+
+ return mask
+
+ def predict(self, x, aggressiveness=None):
+ mask = self.forward(x)
+ pred_mag = x * mask
+
+ if self.offset > 0:
+ pred_mag = pred_mag[:, :, :, self.offset : -self.offset]
+ assert pred_mag.size()[3] > 0
+
+ return pred_mag
diff --git a/tools/uvr5/lib/lib_v5/spec_utils.py b/tools/uvr5/lib/lib_v5/spec_utils.py
new file mode 100644
index 0000000..d2d2bf3
--- /dev/null
+++ b/tools/uvr5/lib/lib_v5/spec_utils.py
@@ -0,0 +1,637 @@
+import hashlib
+import json
+import math
+import os
+
+import librosa
+import numpy as np
+import soundfile as sf
+from tqdm import tqdm
+
+
+def crop_center(h1, h2):
+ h1_shape = h1.size()
+ h2_shape = h2.size()
+
+ if h1_shape[3] == h2_shape[3]:
+ return h1
+ elif h1_shape[3] < h2_shape[3]:
+ raise ValueError("h1_shape[3] must be greater than h2_shape[3]")
+
+ # s_freq = (h2_shape[2] - h1_shape[2]) // 2
+ # e_freq = s_freq + h1_shape[2]
+ s_time = (h1_shape[3] - h2_shape[3]) // 2
+ e_time = s_time + h2_shape[3]
+ h1 = h1[:, :, :, s_time:e_time]
+
+ return h1
+
+
+def wave_to_spectrogram(wave, hop_length, n_fft, mid_side=False, mid_side_b2=False, reverse=False):
+ if reverse:
+ wave_left = np.flip(np.asfortranarray(wave[0]))
+ wave_right = np.flip(np.asfortranarray(wave[1]))
+ elif mid_side:
+ wave_left = np.asfortranarray(np.add(wave[0], wave[1]) / 2)
+ wave_right = np.asfortranarray(np.subtract(wave[0], wave[1]))
+ elif mid_side_b2:
+ wave_left = np.asfortranarray(np.add(wave[1], wave[0] * 0.5))
+ wave_right = np.asfortranarray(np.subtract(wave[0], wave[1] * 0.5))
+ else:
+ wave_left = np.asfortranarray(wave[0])
+ wave_right = np.asfortranarray(wave[1])
+
+ spec_left = librosa.stft(wave_left, n_fft=n_fft, hop_length=hop_length)
+ spec_right = librosa.stft(wave_right, n_fft=n_fft, hop_length=hop_length)
+
+ spec = np.asfortranarray([spec_left, spec_right])
+
+ return spec
+
+
+def wave_to_spectrogram_mt(wave, hop_length, n_fft, mid_side=False, mid_side_b2=False, reverse=False):
+ import threading
+
+ if reverse:
+ wave_left = np.flip(np.asfortranarray(wave[0]))
+ wave_right = np.flip(np.asfortranarray(wave[1]))
+ elif mid_side:
+ wave_left = np.asfortranarray(np.add(wave[0], wave[1]) / 2)
+ wave_right = np.asfortranarray(np.subtract(wave[0], wave[1]))
+ elif mid_side_b2:
+ wave_left = np.asfortranarray(np.add(wave[1], wave[0] * 0.5))
+ wave_right = np.asfortranarray(np.subtract(wave[0], wave[1] * 0.5))
+ else:
+ wave_left = np.asfortranarray(wave[0])
+ wave_right = np.asfortranarray(wave[1])
+
+ def run_thread(**kwargs):
+ global spec_left
+ spec_left = librosa.stft(**kwargs)
+
+ thread = threading.Thread(
+ target=run_thread,
+ kwargs={"y": wave_left, "n_fft": n_fft, "hop_length": hop_length},
+ )
+ thread.start()
+ spec_right = librosa.stft(wave_right, n_fft=n_fft, hop_length=hop_length)
+ thread.join()
+
+ spec = np.asfortranarray([spec_left, spec_right])
+
+ return spec
+
+
+def combine_spectrograms(specs, mp):
+ l = min([specs[i].shape[2] for i in specs])
+ spec_c = np.zeros(shape=(2, mp.param["bins"] + 1, l), dtype=np.complex64)
+ offset = 0
+ bands_n = len(mp.param["band"])
+
+ for d in range(1, bands_n + 1):
+ h = mp.param["band"][d]["crop_stop"] - mp.param["band"][d]["crop_start"]
+ spec_c[:, offset : offset + h, :l] = specs[d][
+ :, mp.param["band"][d]["crop_start"] : mp.param["band"][d]["crop_stop"], :l
+ ]
+ offset += h
+
+ if offset > mp.param["bins"]:
+ raise ValueError("Too much bins")
+
+ # lowpass fiter
+ if mp.param["pre_filter_start"] > 0: # and mp.param['band'][bands_n]['res_type'] in ['scipy', 'polyphase']:
+ if bands_n == 1:
+ spec_c = fft_lp_filter(spec_c, mp.param["pre_filter_start"], mp.param["pre_filter_stop"])
+ else:
+ gp = 1
+ for b in range(mp.param["pre_filter_start"] + 1, mp.param["pre_filter_stop"]):
+ g = math.pow(10, -(b - mp.param["pre_filter_start"]) * (3.5 - gp) / 20.0)
+ gp = g
+ spec_c[:, b, :] *= g
+
+ return np.asfortranarray(spec_c)
+
+
+def spectrogram_to_image(spec, mode="magnitude"):
+ if mode == "magnitude":
+ if np.iscomplexobj(spec):
+ y = np.abs(spec)
+ else:
+ y = spec
+ y = np.log10(y**2 + 1e-8)
+ elif mode == "phase":
+ if np.iscomplexobj(spec):
+ y = np.angle(spec)
+ else:
+ y = spec
+
+ y -= y.min()
+ y *= 255 / y.max()
+ img = np.uint8(y)
+
+ if y.ndim == 3:
+ img = img.transpose(1, 2, 0)
+ img = np.concatenate([np.max(img, axis=2, keepdims=True), img], axis=2)
+
+ return img
+
+
+def reduce_vocal_aggressively(X, y, softmask):
+ v = X - y
+ y_mag_tmp = np.abs(y)
+ v_mag_tmp = np.abs(v)
+
+ v_mask = v_mag_tmp > y_mag_tmp
+ y_mag = np.clip(y_mag_tmp - v_mag_tmp * v_mask * softmask, 0, np.inf)
+
+ return y_mag * np.exp(1.0j * np.angle(y))
+
+
+def mask_silence(mag, ref, thres=0.2, min_range=64, fade_size=32):
+ if min_range < fade_size * 2:
+ raise ValueError("min_range must be >= fade_area * 2")
+
+ mag = mag.copy()
+
+ idx = np.where(ref.mean(axis=(0, 1)) < thres)[0]
+ starts = np.insert(idx[np.where(np.diff(idx) != 1)[0] + 1], 0, idx[0])
+ ends = np.append(idx[np.where(np.diff(idx) != 1)[0]], idx[-1])
+ uninformative = np.where(ends - starts > min_range)[0]
+ if len(uninformative) > 0:
+ starts = starts[uninformative]
+ ends = ends[uninformative]
+ old_e = None
+ for s, e in zip(starts, ends):
+ if old_e is not None and s - old_e < fade_size:
+ s = old_e - fade_size * 2
+
+ if s != 0:
+ weight = np.linspace(0, 1, fade_size)
+ mag[:, :, s : s + fade_size] += weight * ref[:, :, s : s + fade_size]
+ else:
+ s -= fade_size
+
+ if e != mag.shape[2]:
+ weight = np.linspace(1, 0, fade_size)
+ mag[:, :, e - fade_size : e] += weight * ref[:, :, e - fade_size : e]
+ else:
+ e += fade_size
+
+ mag[:, :, s + fade_size : e - fade_size] += ref[:, :, s + fade_size : e - fade_size]
+ old_e = e
+
+ return mag
+
+
+def align_wave_head_and_tail(a, b):
+ l = min([a[0].size, b[0].size])
+
+ return a[:l, :l], b[:l, :l]
+
+
+def cache_or_load(mix_path, inst_path, mp):
+ mix_basename = os.path.splitext(os.path.basename(mix_path))[0]
+ inst_basename = os.path.splitext(os.path.basename(inst_path))[0]
+
+ cache_dir = "mph{}".format(hashlib.sha1(json.dumps(mp.param, sort_keys=True).encode("utf-8")).hexdigest())
+ mix_cache_dir = os.path.join("cache", cache_dir)
+ inst_cache_dir = os.path.join("cache", cache_dir)
+
+ os.makedirs(mix_cache_dir, exist_ok=True)
+ os.makedirs(inst_cache_dir, exist_ok=True)
+
+ mix_cache_path = os.path.join(mix_cache_dir, mix_basename + ".npy")
+ inst_cache_path = os.path.join(inst_cache_dir, inst_basename + ".npy")
+
+ if os.path.exists(mix_cache_path) and os.path.exists(inst_cache_path):
+ X_spec_m = np.load(mix_cache_path)
+ y_spec_m = np.load(inst_cache_path)
+ else:
+ X_wave, y_wave, X_spec_s, y_spec_s = {}, {}, {}, {}
+
+ for d in range(len(mp.param["band"]), 0, -1):
+ bp = mp.param["band"][d]
+
+ if d == len(mp.param["band"]): # high-end band
+ X_wave[d], _ = librosa.load(
+ mix_path, sr=bp["sr"], mono=False, dtype=np.float32, res_type=bp["res_type"]
+ )
+ y_wave[d], _ = librosa.load(
+ inst_path,
+ sr=bp["sr"],
+ mono=False,
+ dtype=np.float32,
+ res_type=bp["res_type"],
+ )
+ else: # lower bands
+ X_wave[d] = librosa.resample(
+ X_wave[d + 1],
+ orig_sr=mp.param["band"][d + 1]["sr"],
+ target_sr=bp["sr"],
+ res_type=bp["res_type"],
+ )
+ y_wave[d] = librosa.resample(
+ y_wave[d + 1],
+ orig_sr=mp.param["band"][d + 1]["sr"],
+ target_sr=bp["sr"],
+ res_type=bp["res_type"],
+ )
+
+ X_wave[d], y_wave[d] = align_wave_head_and_tail(X_wave[d], y_wave[d])
+
+ X_spec_s[d] = wave_to_spectrogram(
+ X_wave[d],
+ bp["hl"],
+ bp["n_fft"],
+ mp.param["mid_side"],
+ mp.param["mid_side_b2"],
+ mp.param["reverse"],
+ )
+ y_spec_s[d] = wave_to_spectrogram(
+ y_wave[d],
+ bp["hl"],
+ bp["n_fft"],
+ mp.param["mid_side"],
+ mp.param["mid_side_b2"],
+ mp.param["reverse"],
+ )
+
+ del X_wave, y_wave
+
+ X_spec_m = combine_spectrograms(X_spec_s, mp)
+ y_spec_m = combine_spectrograms(y_spec_s, mp)
+
+ if X_spec_m.shape != y_spec_m.shape:
+ raise ValueError("The combined spectrograms are different: " + mix_path)
+
+ _, ext = os.path.splitext(mix_path)
+
+ np.save(mix_cache_path, X_spec_m)
+ np.save(inst_cache_path, y_spec_m)
+
+ return X_spec_m, y_spec_m
+
+
+def spectrogram_to_wave(spec, hop_length, mid_side, mid_side_b2, reverse):
+ spec_left = np.asfortranarray(spec[0])
+ spec_right = np.asfortranarray(spec[1])
+
+ wave_left = librosa.istft(spec_left, hop_length=hop_length)
+ wave_right = librosa.istft(spec_right, hop_length=hop_length)
+
+ if reverse:
+ return np.asfortranarray([np.flip(wave_left), np.flip(wave_right)])
+ elif mid_side:
+ return np.asfortranarray([np.add(wave_left, wave_right / 2), np.subtract(wave_left, wave_right / 2)])
+ elif mid_side_b2:
+ return np.asfortranarray(
+ [
+ np.add(wave_right / 1.25, 0.4 * wave_left),
+ np.subtract(wave_left / 1.25, 0.4 * wave_right),
+ ]
+ )
+ else:
+ return np.asfortranarray([wave_left, wave_right])
+
+
+def spectrogram_to_wave_mt(spec, hop_length, mid_side, reverse, mid_side_b2):
+ import threading
+
+ spec_left = np.asfortranarray(spec[0])
+ spec_right = np.asfortranarray(spec[1])
+
+ def run_thread(**kwargs):
+ global wave_left
+ wave_left = librosa.istft(**kwargs)
+
+ thread = threading.Thread(target=run_thread, kwargs={"stft_matrix": spec_left, "hop_length": hop_length})
+ thread.start()
+ wave_right = librosa.istft(spec_right, hop_length=hop_length)
+ thread.join()
+
+ if reverse:
+ return np.asfortranarray([np.flip(wave_left), np.flip(wave_right)])
+ elif mid_side:
+ return np.asfortranarray([np.add(wave_left, wave_right / 2), np.subtract(wave_left, wave_right / 2)])
+ elif mid_side_b2:
+ return np.asfortranarray(
+ [
+ np.add(wave_right / 1.25, 0.4 * wave_left),
+ np.subtract(wave_left / 1.25, 0.4 * wave_right),
+ ]
+ )
+ else:
+ return np.asfortranarray([wave_left, wave_right])
+
+
+def cmb_spectrogram_to_wave(spec_m, mp, extra_bins_h=None, extra_bins=None):
+ wave_band = {}
+ bands_n = len(mp.param["band"])
+ offset = 0
+
+ for d in range(1, bands_n + 1):
+ bp = mp.param["band"][d]
+ spec_s = np.ndarray(shape=(2, bp["n_fft"] // 2 + 1, spec_m.shape[2]), dtype=complex)
+ h = bp["crop_stop"] - bp["crop_start"]
+ spec_s[:, bp["crop_start"] : bp["crop_stop"], :] = spec_m[:, offset : offset + h, :]
+
+ offset += h
+ if d == bands_n: # higher
+ if extra_bins_h: # if --high_end_process bypass
+ max_bin = bp["n_fft"] // 2
+ spec_s[:, max_bin - extra_bins_h : max_bin, :] = extra_bins[:, :extra_bins_h, :]
+ if bp["hpf_start"] > 0:
+ spec_s = fft_hp_filter(spec_s, bp["hpf_start"], bp["hpf_stop"] - 1)
+ if bands_n == 1:
+ wave = spectrogram_to_wave(
+ spec_s,
+ bp["hl"],
+ mp.param["mid_side"],
+ mp.param["mid_side_b2"],
+ mp.param["reverse"],
+ )
+ else:
+ wave = np.add(
+ wave,
+ spectrogram_to_wave(
+ spec_s,
+ bp["hl"],
+ mp.param["mid_side"],
+ mp.param["mid_side_b2"],
+ mp.param["reverse"],
+ ),
+ )
+ else:
+ sr = mp.param["band"][d + 1]["sr"]
+ if d == 1: # lower
+ spec_s = fft_lp_filter(spec_s, bp["lpf_start"], bp["lpf_stop"])
+ wave = librosa.resample(
+ spectrogram_to_wave(
+ spec_s,
+ bp["hl"],
+ mp.param["mid_side"],
+ mp.param["mid_side_b2"],
+ mp.param["reverse"],
+ ),
+ orig_sr=bp["sr"],
+ target_sr=sr,
+ res_type="sinc_fastest",
+ )
+ else: # mid
+ spec_s = fft_hp_filter(spec_s, bp["hpf_start"], bp["hpf_stop"] - 1)
+ spec_s = fft_lp_filter(spec_s, bp["lpf_start"], bp["lpf_stop"])
+ wave2 = np.add(
+ wave,
+ spectrogram_to_wave(
+ spec_s,
+ bp["hl"],
+ mp.param["mid_side"],
+ mp.param["mid_side_b2"],
+ mp.param["reverse"],
+ ),
+ )
+ # wave = librosa.core.resample(wave2, orig_sr=bp['sr'], target_sr=sr, res_type="sinc_fastest")
+ wave = librosa.core.resample(wave2, orig_sr=bp["sr"], target_sr=sr, res_type="scipy")
+
+ return wave.T
+
+
+def fft_lp_filter(spec, bin_start, bin_stop):
+ g = 1.0
+ for b in range(bin_start, bin_stop):
+ g -= 1 / (bin_stop - bin_start)
+ spec[:, b, :] = g * spec[:, b, :]
+
+ spec[:, bin_stop:, :] *= 0
+
+ return spec
+
+
+def fft_hp_filter(spec, bin_start, bin_stop):
+ g = 1.0
+ for b in range(bin_start, bin_stop, -1):
+ g -= 1 / (bin_start - bin_stop)
+ spec[:, b, :] = g * spec[:, b, :]
+
+ spec[:, 0 : bin_stop + 1, :] *= 0
+
+ return spec
+
+
+def mirroring(a, spec_m, input_high_end, mp):
+ if "mirroring" == a:
+ mirror = np.flip(
+ np.abs(
+ spec_m[
+ :,
+ mp.param["pre_filter_start"] - 10 - input_high_end.shape[1] : mp.param["pre_filter_start"] - 10,
+ :,
+ ]
+ ),
+ 1,
+ )
+ mirror = mirror * np.exp(1.0j * np.angle(input_high_end))
+
+ return np.where(np.abs(input_high_end) <= np.abs(mirror), input_high_end, mirror)
+
+ if "mirroring2" == a:
+ mirror = np.flip(
+ np.abs(
+ spec_m[
+ :,
+ mp.param["pre_filter_start"] - 10 - input_high_end.shape[1] : mp.param["pre_filter_start"] - 10,
+ :,
+ ]
+ ),
+ 1,
+ )
+ mi = np.multiply(mirror, input_high_end * 1.7)
+
+ return np.where(np.abs(input_high_end) <= np.abs(mi), input_high_end, mi)
+
+
+def ensembling(a, specs):
+ for i in range(1, len(specs)):
+ if i == 1:
+ spec = specs[0]
+
+ ln = min([spec.shape[2], specs[i].shape[2]])
+ spec = spec[:, :, :ln]
+ specs[i] = specs[i][:, :, :ln]
+
+ if "min_mag" == a:
+ spec = np.where(np.abs(specs[i]) <= np.abs(spec), specs[i], spec)
+ if "max_mag" == a:
+ spec = np.where(np.abs(specs[i]) >= np.abs(spec), specs[i], spec)
+
+ return spec
+
+
+def stft(wave, nfft, hl):
+ wave_left = np.asfortranarray(wave[0])
+ wave_right = np.asfortranarray(wave[1])
+ spec_left = librosa.stft(wave_left, n_fft=nfft, hop_length=hl)
+ spec_right = librosa.stft(wave_right, n_fft=nfft, hop_length=hl)
+ spec = np.asfortranarray([spec_left, spec_right])
+
+ return spec
+
+
+def istft(spec, hl):
+ spec_left = np.asfortranarray(spec[0])
+ spec_right = np.asfortranarray(spec[1])
+
+ wave_left = librosa.istft(spec_left, hop_length=hl)
+ wave_right = librosa.istft(spec_right, hop_length=hl)
+ wave = np.asfortranarray([wave_left, wave_right])
+
+ return wave
+
+
+if __name__ == "__main__":
+ import argparse
+ import time
+
+ import cv2
+ from model_param_init import ModelParameters
+
+ p = argparse.ArgumentParser()
+ p.add_argument(
+ "--algorithm",
+ "-a",
+ type=str,
+ choices=["invert", "invert_p", "min_mag", "max_mag", "deep", "align"],
+ default="min_mag",
+ )
+ p.add_argument(
+ "--model_params",
+ "-m",
+ type=str,
+ default=os.path.join("modelparams", "1band_sr44100_hl512.json"),
+ )
+ p.add_argument("--output_name", "-o", type=str, default="output")
+ p.add_argument("--vocals_only", "-v", action="store_true")
+ p.add_argument("input", nargs="+")
+ args = p.parse_args()
+
+ start_time = time.time()
+
+ if args.algorithm.startswith("invert") and len(args.input) != 2:
+ raise ValueError("There should be two input files.")
+
+ if not args.algorithm.startswith("invert") and len(args.input) < 2:
+ raise ValueError("There must be at least two input files.")
+
+ wave, specs = {}, {}
+ mp = ModelParameters(args.model_params)
+
+ for i in range(len(args.input)):
+ spec = {}
+
+ for d in range(len(mp.param["band"]), 0, -1):
+ bp = mp.param["band"][d]
+
+ if d == len(mp.param["band"]): # high-end band
+ wave[d], _ = librosa.load(
+ args.input[i],
+ sr=bp["sr"],
+ mono=False,
+ dtype=np.float32,
+ res_type=bp["res_type"],
+ )
+
+ if len(wave[d].shape) == 1: # mono to stereo
+ wave[d] = np.array([wave[d], wave[d]])
+ else: # lower bands
+ wave[d] = librosa.resample(
+ wave[d + 1],
+ orig_sr=mp.param["band"][d + 1]["sr"],
+ target_sr=bp["sr"],
+ res_type=bp["res_type"],
+ )
+
+ spec[d] = wave_to_spectrogram(
+ wave[d],
+ bp["hl"],
+ bp["n_fft"],
+ mp.param["mid_side"],
+ mp.param["mid_side_b2"],
+ mp.param["reverse"],
+ )
+
+ specs[i] = combine_spectrograms(spec, mp)
+
+ del wave
+
+ if args.algorithm == "deep":
+ d_spec = np.where(np.abs(specs[0]) <= np.abs(spec[1]), specs[0], spec[1])
+ v_spec = d_spec - specs[1]
+ sf.write(
+ os.path.join("{}.wav".format(args.output_name)),
+ cmb_spectrogram_to_wave(v_spec, mp),
+ mp.param["sr"],
+ )
+
+ if args.algorithm.startswith("invert"):
+ ln = min([specs[0].shape[2], specs[1].shape[2]])
+ specs[0] = specs[0][:, :, :ln]
+ specs[1] = specs[1][:, :, :ln]
+
+ if "invert_p" == args.algorithm:
+ X_mag = np.abs(specs[0])
+ y_mag = np.abs(specs[1])
+ max_mag = np.where(X_mag >= y_mag, X_mag, y_mag)
+ v_spec = specs[1] - max_mag * np.exp(1.0j * np.angle(specs[0]))
+ else:
+ specs[1] = reduce_vocal_aggressively(specs[0], specs[1], 0.2)
+ v_spec = specs[0] - specs[1]
+
+ if not args.vocals_only:
+ X_mag = np.abs(specs[0])
+ y_mag = np.abs(specs[1])
+ v_mag = np.abs(v_spec)
+
+ X_image = spectrogram_to_image(X_mag)
+ y_image = spectrogram_to_image(y_mag)
+ v_image = spectrogram_to_image(v_mag)
+
+ cv2.imwrite("{}_X.png".format(args.output_name), X_image)
+ cv2.imwrite("{}_y.png".format(args.output_name), y_image)
+ cv2.imwrite("{}_v.png".format(args.output_name), v_image)
+
+ sf.write(
+ "{}_X.wav".format(args.output_name),
+ cmb_spectrogram_to_wave(specs[0], mp),
+ mp.param["sr"],
+ )
+ sf.write(
+ "{}_y.wav".format(args.output_name),
+ cmb_spectrogram_to_wave(specs[1], mp),
+ mp.param["sr"],
+ )
+
+ sf.write(
+ "{}_v.wav".format(args.output_name),
+ cmb_spectrogram_to_wave(v_spec, mp),
+ mp.param["sr"],
+ )
+ else:
+ if not args.algorithm == "deep":
+ sf.write(
+ os.path.join("ensembled", "{}.wav".format(args.output_name)),
+ cmb_spectrogram_to_wave(ensembling(args.algorithm, specs), mp),
+ mp.param["sr"],
+ )
+
+ if args.algorithm == "align":
+ trackalignment = [
+ {
+ "file1": '"{}"'.format(args.input[0]),
+ "file2": '"{}"'.format(args.input[1]),
+ }
+ ]
+
+ for i, e in tqdm(enumerate(trackalignment), desc="Performing Alignment..."):
+ os.system(f"python lib/align_tracks.py {e['file1']} {e['file2']}")
+
+ # print('Total time: {0:.{1}f}s'.format(time.time() - start_time, 1))
diff --git a/tools/uvr5/lib/utils.py b/tools/uvr5/lib/utils.py
new file mode 100644
index 0000000..826b76d
--- /dev/null
+++ b/tools/uvr5/lib/utils.py
@@ -0,0 +1,82 @@
+import numpy as np
+import torch
+from tqdm import tqdm
+
+
+def make_padding(width, cropsize, offset):
+ left = offset
+ roi_size = cropsize - left * 2
+ if roi_size == 0:
+ roi_size = cropsize
+ right = roi_size - (width % roi_size) + left
+
+ return left, right, roi_size
+
+
+def inference(X_spec, device, model, aggressiveness, data):
+ """
+ data : dic configs
+ """
+
+ def _execute(X_mag_pad, roi_size, n_window, device, model, aggressiveness, is_half=True):
+ model.eval()
+ with torch.no_grad():
+ preds = []
+
+ iterations = [n_window]
+
+ total_iterations = sum(iterations)
+ for i in tqdm(range(n_window)):
+ start = i * roi_size
+ X_mag_window = X_mag_pad[None, :, :, start : start + data["window_size"]]
+ X_mag_window = torch.from_numpy(X_mag_window)
+ if is_half:
+ X_mag_window = X_mag_window.half()
+ X_mag_window = X_mag_window.to(device)
+
+ pred = model.predict(X_mag_window, aggressiveness)
+
+ pred = pred.detach().cpu().numpy()
+ preds.append(pred[0])
+
+ pred = np.concatenate(preds, axis=2)
+ return pred
+
+ def preprocess(X_spec):
+ X_mag = np.abs(X_spec)
+ X_phase = np.angle(X_spec)
+
+ return X_mag, X_phase
+
+ X_mag, X_phase = preprocess(X_spec)
+
+ coef = X_mag.max()
+ X_mag_pre = X_mag / coef
+
+ n_frame = X_mag_pre.shape[2]
+ pad_l, pad_r, roi_size = make_padding(n_frame, data["window_size"], model.offset)
+ n_window = int(np.ceil(n_frame / roi_size))
+
+ X_mag_pad = np.pad(X_mag_pre, ((0, 0), (0, 0), (pad_l, pad_r)), mode="constant")
+
+ if list(model.state_dict().values())[0].dtype == torch.float16:
+ is_half = True
+ else:
+ is_half = False
+ pred = _execute(X_mag_pad, roi_size, n_window, device, model, aggressiveness, is_half)
+ pred = pred[:, :, :n_frame]
+
+ if data["tta"]:
+ pad_l += roi_size // 2
+ pad_r += roi_size // 2
+ n_window += 1
+
+ X_mag_pad = np.pad(X_mag_pre, ((0, 0), (0, 0), (pad_l, pad_r)), mode="constant")
+
+ pred_tta = _execute(X_mag_pad, roi_size, n_window, device, model, aggressiveness, is_half)
+ pred_tta = pred_tta[:, :, roi_size // 2 :]
+ pred_tta = pred_tta[:, :, :n_frame]
+
+ return (pred + pred_tta) * 0.5 * coef, X_mag, np.exp(1.0j * X_phase)
+ else:
+ return pred * coef, X_mag, np.exp(1.0j * X_phase)
diff --git a/tools/uvr5/mdxnet.py b/tools/uvr5/mdxnet.py
new file mode 100644
index 0000000..4ae8b4a
--- /dev/null
+++ b/tools/uvr5/mdxnet.py
@@ -0,0 +1,315 @@
+import os
+import logging
+import sysconfig
+
+logger = logging.getLogger(__name__)
+
+import librosa
+import numpy as np
+import soundfile as sf
+import torch
+from tqdm import tqdm
+
+
+_ORT_CUDA_DLL_HANDLES = []
+
+
+def _configure_ort_cuda_dll_paths():
+ """Expose pip-installed CUDA 11/cuDNN 8 DLLs to ONNX Runtime on Windows."""
+ if os.name != "nt":
+ return
+
+ site_packages = os.path.normpath(sysconfig.get_paths()["purelib"])
+ nvidia_root = os.path.join(site_packages, "nvidia")
+ dll_dirs = [
+ os.path.join(nvidia_root, "cuda_runtime", "bin"),
+ os.path.join(nvidia_root, "cublas", "bin"),
+ os.path.join(nvidia_root, "cufft", "bin"),
+ os.path.join(nvidia_root, "cudnn", "bin"),
+ os.path.join(nvidia_root, "cuda_nvrtc", "bin"),
+ os.path.join(os.path.dirname(torch.__file__), "lib"),
+ ]
+ dll_dirs = [path for path in dll_dirs if os.path.isdir(path)]
+ if not dll_dirs:
+ return
+
+ current_path = os.environ.get("PATH", "")
+ current_dirs = [path for path in current_path.split(os.pathsep) if path]
+ known_dirs = {os.path.normcase(os.path.normpath(path)) for path in current_dirs}
+ prepend_dirs = []
+ for path in dll_dirs:
+ normalized = os.path.normcase(os.path.normpath(path))
+ if normalized not in known_dirs:
+ prepend_dirs.append(path)
+ known_dirs.add(normalized)
+ if prepend_dirs:
+ os.environ["PATH"] = os.pathsep.join(prepend_dirs + current_dirs)
+
+ # Python 3.8+ restricts DLL lookup for extension modules. Keep the handles
+ # alive for the process lifetime in addition to updating PATH.
+ if hasattr(os, "add_dll_directory"):
+ for path in dll_dirs:
+ try:
+ _ORT_CUDA_DLL_HANDLES.append(os.add_dll_directory(path))
+ except OSError:
+ logger.warning("Unable to add ONNX Runtime DLL directory: %s", path)
+
+
+_configure_ort_cuda_dll_paths()
+
+cpu = torch.device("cpu")
+
+
+class ConvTDFNetTrim:
+ def __init__(self, device, model_name, target_name, L, dim_f, dim_t, n_fft, hop=1024):
+ super(ConvTDFNetTrim, self).__init__()
+
+ self.dim_f = dim_f
+ self.dim_t = 2**dim_t
+ self.n_fft = n_fft
+ self.hop = hop
+ self.n_bins = self.n_fft // 2 + 1
+ self.chunk_size = hop * (self.dim_t - 1)
+ self.window = torch.hann_window(window_length=self.n_fft, periodic=True).to(device)
+ self.target_name = target_name
+ self.blender = "blender" in model_name
+
+ self.dim_c = 4
+ out_c = self.dim_c * 4 if target_name == "*" else self.dim_c
+ self.freq_pad = torch.zeros([1, out_c, self.n_bins - self.dim_f, self.dim_t]).to(device)
+
+ self.n = L // 2
+
+ def stft(self, x):
+ x = x.reshape([-1, self.chunk_size])
+ x = torch.stft(
+ x,
+ n_fft=self.n_fft,
+ hop_length=self.hop,
+ window=self.window,
+ center=True,
+ return_complex=True,
+ )
+ x = torch.view_as_real(x)
+ x = x.permute([0, 3, 1, 2])
+ x = x.reshape([-1, 2, 2, self.n_bins, self.dim_t]).reshape([-1, self.dim_c, self.n_bins, self.dim_t])
+ return x[:, :, : self.dim_f]
+
+ def istft(self, x, freq_pad=None):
+ freq_pad = self.freq_pad.repeat([x.shape[0], 1, 1, 1]) if freq_pad is None else freq_pad
+ x = torch.cat([x, freq_pad], -2)
+ c = 4 * 2 if self.target_name == "*" else 2
+ x = x.reshape([-1, c, 2, self.n_bins, self.dim_t]).reshape([-1, 2, self.n_bins, self.dim_t])
+ x = x.permute([0, 2, 3, 1])
+ x = x.contiguous()
+ x = torch.view_as_complex(x)
+ x = torch.istft(x, n_fft=self.n_fft, hop_length=self.hop, window=self.window, center=True)
+ return x.reshape([-1, c, self.chunk_size])
+
+
+def get_models(device, dim_f, dim_t, n_fft):
+ return ConvTDFNetTrim(
+ device=device,
+ model_name="Conv-TDF",
+ target_name="vocals",
+ L=11,
+ dim_f=dim_f,
+ dim_t=dim_t,
+ n_fft=n_fft,
+ )
+
+
+class Predictor:
+ def __init__(self, args):
+ import onnxruntime as ort
+
+ available_providers = ort.get_available_providers()
+ logger.info("ONNX Runtime available providers: %s", available_providers)
+ if (
+ "CUDAExecutionProvider" in args.providers
+ and "CUDAExecutionProvider" not in available_providers
+ ):
+ raise RuntimeError(
+ "CUDAExecutionProvider is required for the FoxJoy ONNX model, "
+ "but the installed ONNX Runtime does not provide it. Install "
+ "the matching CUDA ONNX Runtime dependencies with this "
+ "project's runtime Python."
+ )
+ if (
+ "DmlExecutionProvider" in args.providers
+ and "DmlExecutionProvider" not in available_providers
+ ):
+ raise RuntimeError(
+ "DmlExecutionProvider is required for the FoxJoy ONNX model, "
+ "but the installed ONNX Runtime does not provide it. Install "
+ "requirments_cpu_py312.txt with this project's runtime Python."
+ )
+ self.args = args
+ self.model_ = get_models(device=cpu, dim_f=args.dim_f, dim_t=args.dim_t, n_fft=args.n_fft)
+ self.model = ort.InferenceSession(
+ os.path.join(args.onnx, self.model_.target_name + ".onnx"),
+ providers=args.providers,
+ )
+ active_providers = self.model.get_providers()
+ logger.info("ONNX Runtime active providers: %s", active_providers)
+ if (
+ "CUDAExecutionProvider" in args.providers
+ and (
+ not active_providers
+ or active_providers[0] != "CUDAExecutionProvider"
+ )
+ ):
+ raise RuntimeError(
+ "The FoxJoy ONNX model did not activate CUDAExecutionProvider; "
+ "check the CUDA 11/cuDNN 8 DLL installation."
+ )
+ if (
+ "DmlExecutionProvider" in args.providers
+ and (
+ not active_providers
+ or active_providers[0] != "DmlExecutionProvider"
+ )
+ ):
+ raise RuntimeError(
+ "The FoxJoy ONNX model did not activate DmlExecutionProvider; "
+ "check the ONNX Runtime DirectML installation."
+ )
+ logger.info("ONNX load done")
+
+ def demix(self, mix):
+ samples = mix.shape[-1]
+ margin = self.args.margin
+ chunk_size = self.args.chunks * 44100
+ assert not margin == 0, "margin cannot be zero!"
+ if margin > chunk_size:
+ margin = chunk_size
+
+ segmented_mix = {}
+
+ if self.args.chunks == 0 or samples < chunk_size:
+ chunk_size = samples
+
+ counter = -1
+ for skip in range(0, samples, chunk_size):
+ counter += 1
+
+ s_margin = 0 if counter == 0 else margin
+ end = min(skip + chunk_size + margin, samples)
+
+ start = skip - s_margin
+
+ segmented_mix[skip] = mix[:, start:end].copy()
+ if end == samples:
+ break
+
+ sources = self.demix_base(segmented_mix, margin_size=margin)
+ """
+ mix:(2,big_sample)
+ segmented_mix:offset->(2,small_sample)
+ sources:(1,2,big_sample)
+ """
+ return sources
+
+ def demix_base(self, mixes, margin_size):
+ chunked_sources = []
+ progress_bar = tqdm(total=len(mixes))
+ progress_bar.set_description("Processing")
+ for mix in mixes:
+ cmix = mixes[mix]
+ sources = []
+ n_sample = cmix.shape[1]
+ model = self.model_
+ trim = model.n_fft // 2
+ gen_size = model.chunk_size - 2 * trim
+ pad = gen_size - n_sample % gen_size
+ mix_p = np.concatenate((np.zeros((2, trim)), cmix, np.zeros((2, pad)), np.zeros((2, trim))), 1)
+ mix_waves = []
+ i = 0
+ while i < n_sample + pad:
+ waves = np.array(mix_p[:, i : i + model.chunk_size])
+ mix_waves.append(waves)
+ i += gen_size
+ mix_waves = torch.tensor(mix_waves, dtype=torch.float32).to(cpu)
+ with torch.no_grad():
+ _ort = self.model
+ spek = model.stft(mix_waves)
+ if self.args.denoise:
+ spec_pred = (
+ -_ort.run(None, {"input": -spek.cpu().numpy()})[0] * 0.5
+ + _ort.run(None, {"input": spek.cpu().numpy()})[0] * 0.5
+ )
+ tar_waves = model.istft(torch.tensor(spec_pred))
+ else:
+ tar_waves = model.istft(torch.tensor(_ort.run(None, {"input": spek.cpu().numpy()})[0]))
+ tar_signal = tar_waves[:, :, trim:-trim].transpose(0, 1).reshape(2, -1).numpy()[:, :-pad]
+
+ start = 0 if mix == 0 else margin_size
+ end = None if mix == list(mixes.keys())[::-1][0] else -margin_size
+ if margin_size == 0:
+ end = None
+ sources.append(tar_signal[:, start:end])
+
+ progress_bar.update(1)
+
+ chunked_sources.append(sources)
+ _sources = np.concatenate(chunked_sources, axis=-1)
+ # del self.model
+ progress_bar.close()
+ return _sources
+
+ def prediction(self, m, vocal_root, others_root, format):
+ os.makedirs(vocal_root, exist_ok=True)
+ os.makedirs(others_root, exist_ok=True)
+ basename = os.path.basename(m)
+ mix, rate = librosa.load(m, mono=False, sr=44100)
+ if mix.ndim == 1:
+ mix = np.asfortranarray([mix, mix])
+ mix = mix.T
+ sources = self.demix(mix.T)
+ opt = sources[0].T
+ if format in ["wav", "flac"]:
+ sf.write("%s/%s_main_vocal.%s" % (vocal_root, basename, format), mix - opt, rate)
+ sf.write("%s/%s_others.%s" % (others_root, basename, format), opt, rate)
+ else:
+ path_vocal = "%s/%s_main_vocal.wav" % (vocal_root, basename)
+ path_other = "%s/%s_others.wav" % (others_root, basename)
+ sf.write(path_vocal, mix - opt, rate)
+ sf.write(path_other, opt, rate)
+ opt_path_vocal = path_vocal[:-4] + ".%s" % format
+ opt_path_other = path_other[:-4] + ".%s" % format
+ if os.path.exists(path_vocal):
+ os.system('ffmpeg -i "%s" -vn "%s" -q:a 2 -y' % (path_vocal, opt_path_vocal))
+ if os.path.exists(opt_path_vocal):
+ try:
+ os.remove(path_vocal)
+ except:
+ pass
+ if os.path.exists(path_other):
+ os.system('ffmpeg -i "%s" -vn "%s" -q:a 2 -y' % (path_other, opt_path_other))
+ if os.path.exists(opt_path_other):
+ try:
+ os.remove(path_other)
+ except:
+ pass
+
+
+class MDXNetDereverb:
+ def __init__(self, chunks, providers):
+ self.onnx = os.path.join(
+ os.getenv("weight_uvr5_root", "assets/uvr5_weights"),
+ "onnx_dereverb_By_FoxJoy",
+ )
+ self.shifts = 10 # 'Predict with randomised equivariant stabilisation'
+ self.mixing = "min_mag" # ['default','min_mag','max_mag']
+ self.chunks = chunks
+ self.providers = providers
+ self.margin = 44100
+ self.dim_t = 9
+ self.dim_f = 3072
+ self.n_fft = 6144
+ self.denoise = True
+ self.pred = Predictor(self)
+ self.device = cpu
+
+ def _path_audio_(self, input, others_root, vocal_root, format, is_hp3=False):
+ self.pred.prediction(input, vocal_root, others_root, format)
diff --git a/tools/uvr5/rotary_embedding_torch/__init__.py b/tools/uvr5/rotary_embedding_torch/__init__.py
new file mode 100644
index 0000000..b1fbdc2
--- /dev/null
+++ b/tools/uvr5/rotary_embedding_torch/__init__.py
@@ -0,0 +1,6 @@
+from .rotary_embedding_torch import (
+ apply_rotary_emb,
+ RotaryEmbedding,
+ apply_learned_rotations,
+ broadcat
+)
diff --git a/tools/uvr5/rotary_embedding_torch/rotary_embedding_torch.py b/tools/uvr5/rotary_embedding_torch/rotary_embedding_torch.py
new file mode 100644
index 0000000..ec67505
--- /dev/null
+++ b/tools/uvr5/rotary_embedding_torch/rotary_embedding_torch.py
@@ -0,0 +1,186 @@
+from __future__ import annotations
+from math import pi, log
+import warnings
+
+warnings.filterwarnings(
+ "ignore",
+ message="`torch.cuda.amp.autocast.*is deprecated.*",
+ category=FutureWarning,
+)
+
+import torch
+from torch.nn import Module, ModuleList
+from torch.cuda.amp import autocast
+from torch import nn, einsum, broadcast_tensors, Tensor
+from einops import rearrange, repeat
+from typing import Literal
+
+def exists(val):
+ return val is not None
+
+def default(val, d):
+ return val if exists(val) else d
+
+def broadcat(tensors, dim=-1):
+ broadcasted_tensors = broadcast_tensors(*tensors)
+ return torch.cat(broadcasted_tensors, dim=dim)
+
+def rotate_half(x):
+ x = rearrange(x, '... (d r) -> ... d r', r=2)
+ (x1, x2) = x.unbind(dim=-1)
+ x = torch.stack((-x2, x1), dim=-1)
+ return rearrange(x, '... d r -> ... (d r)')
+
+@autocast(enabled=False)
+def apply_rotary_emb(freqs, t, start_index=0, scale=1.0, seq_dim=-2):
+ dtype = t.dtype
+ if t.ndim == 3:
+ seq_len = t.shape[seq_dim]
+ freqs = freqs[-seq_len:]
+ rot_dim = freqs.shape[-1]
+ end_index = start_index + rot_dim
+ assert rot_dim <= t.shape[-1], f'feature dimension {t.shape[-1]} is not of sufficient size to rotate in all the positions {rot_dim}'
+ (t_left, t, t_right) = (t[..., :start_index], t[..., start_index:end_index], t[..., end_index:])
+ t = t * freqs.cos() * scale + rotate_half(t) * freqs.sin() * scale
+ if t.device.type == 'privateuseone':
+ # DirectML rejects concatenation when one of the slices has a zero
+ # length. Rotary embeddings normally cover the complete head, so both
+ # edge slices are empty; omitting them is mathematically identical.
+ parts = tuple(part for part in (t_left, t, t_right) if part.shape[-1] > 0)
+ out = parts[0] if len(parts) == 1 else torch.cat(parts, dim=-1)
+ else:
+ out = torch.cat((t_left, t, t_right), dim=-1)
+ return out.type(dtype)
+
+def apply_learned_rotations(rotations, t, start_index=0, freq_ranges=None):
+ if exists(freq_ranges):
+ rotations = einsum('..., f -> ... f', rotations, freq_ranges)
+ rotations = rearrange(rotations, '... r f -> ... (r f)')
+ rotations = repeat(rotations, '... n -> ... (n r)', r=2)
+ return apply_rotary_emb(rotations, t, start_index=start_index)
+
+class RotaryEmbedding(Module):
+
+ def __init__(self, dim, custom_freqs=None, freqs_for='lang', theta=10000, max_freq=10, num_freqs=1, learned_freq=False, use_xpos=False, xpos_scale_base=512, interpolate_factor=1.0, theta_rescale_factor=1.0, seq_before_head_dim=False, cache_if_possible=True):
+ super().__init__()
+ theta *= theta_rescale_factor ** (dim / (dim - 2))
+ self.freqs_for = freqs_for
+ if exists(custom_freqs):
+ freqs = custom_freqs
+ elif freqs_for == 'lang':
+ freqs = 1.0 / theta ** (torch.arange(0, dim, 2)[:dim // 2].float() / dim)
+ elif freqs_for == 'pixel':
+ freqs = torch.linspace(1.0, max_freq / 2, dim // 2) * pi
+ elif freqs_for == 'constant':
+ freqs = torch.ones(num_freqs).float()
+ self.cache_if_possible = cache_if_possible
+ self.tmp_store('cached_freqs', None)
+ self.tmp_store('cached_scales', None)
+ self.freqs = nn.Parameter(freqs, requires_grad=learned_freq)
+ self.learned_freq = learned_freq
+ self.tmp_store('dummy', torch.tensor(0))
+ self.seq_before_head_dim = seq_before_head_dim
+ self.default_seq_dim = -3 if seq_before_head_dim else -2
+ assert interpolate_factor >= 1.0
+ self.interpolate_factor = interpolate_factor
+ self.use_xpos = use_xpos
+ if not use_xpos:
+ self.tmp_store('scale', None)
+ return
+ scale = (torch.arange(0, dim, 2) + 0.4 * dim) / (1.4 * dim)
+ self.scale_base = xpos_scale_base
+ self.tmp_store('scale', scale)
+ self.apply_rotary_emb = staticmethod(apply_rotary_emb)
+
+ @property
+ def device(self):
+ return self.dummy.device
+
+ def tmp_store(self, key, value):
+ self.register_buffer(key, value, persistent=False)
+
+ def get_seq_pos(self, seq_len, device, dtype, offset=0):
+ return (torch.arange(seq_len, device=device, dtype=dtype) + offset) / self.interpolate_factor
+
+ def rotate_queries_or_keys(self, t, seq_dim=None, offset=0, scale=None):
+ seq_dim = default(seq_dim, self.default_seq_dim)
+ assert not self.use_xpos or exists(scale), 'you must use `.rotate_queries_and_keys` method instead and pass in both queries and keys, for length extrapolatable rotary embeddings'
+ (device, dtype, seq_len) = (t.device, t.dtype, t.shape[seq_dim])
+ seq = self.get_seq_pos(seq_len, device=device, dtype=dtype, offset=offset)
+ freqs = self.forward(seq, seq_len=seq_len, offset=offset)
+ if seq_dim == -3:
+ freqs = rearrange(freqs, 'n d -> n 1 d')
+ return apply_rotary_emb(freqs, t, scale=default(scale, 1.0), seq_dim=seq_dim)
+
+ def rotate_queries_with_cached_keys(self, q, k, seq_dim=None, offset=0):
+ (dtype, device, seq_dim) = (q.dtype, q.device, default(seq_dim, self.default_seq_dim))
+ (q_len, k_len) = (q.shape[seq_dim], k.shape[seq_dim])
+ assert q_len <= k_len
+ q_scale = k_scale = 1.0
+ if self.use_xpos:
+ seq = self.get_seq_pos(k_len, dtype=dtype, device=device)
+ q_scale = self.get_scale(seq[-q_len:]).type(dtype)
+ k_scale = self.get_scale(seq).type(dtype)
+ rotated_q = self.rotate_queries_or_keys(q, seq_dim=seq_dim, scale=q_scale, offset=k_len - q_len + offset)
+ rotated_k = self.rotate_queries_or_keys(k, seq_dim=seq_dim, scale=k_scale ** (-1))
+ rotated_q = rotated_q.type(q.dtype)
+ rotated_k = rotated_k.type(k.dtype)
+ return (rotated_q, rotated_k)
+
+ def rotate_queries_and_keys(self, q, k, seq_dim=None):
+ seq_dim = default(seq_dim, self.default_seq_dim)
+ assert self.use_xpos
+ (device, dtype, seq_len) = (q.device, q.dtype, q.shape[seq_dim])
+ seq = self.get_seq_pos(seq_len, dtype=dtype, device=device)
+ freqs = self.forward(seq, seq_len=seq_len)
+ scale = self.get_scale(seq, seq_len=seq_len).to(dtype)
+ if seq_dim == -3:
+ freqs = rearrange(freqs, 'n d -> n 1 d')
+ scale = rearrange(scale, 'n d -> n 1 d')
+ rotated_q = apply_rotary_emb(freqs, q, scale=scale, seq_dim=seq_dim)
+ rotated_k = apply_rotary_emb(freqs, k, scale=scale ** (-1), seq_dim=seq_dim)
+ rotated_q = rotated_q.type(q.dtype)
+ rotated_k = rotated_k.type(k.dtype)
+ return (rotated_q, rotated_k)
+
+ def get_scale(self, t, seq_len=None, offset=0):
+ assert self.use_xpos
+ should_cache = self.cache_if_possible and exists(seq_len)
+ if should_cache and exists(self.cached_scales) and (seq_len + offset <= self.cached_scales.shape[0]):
+ return self.cached_scales[offset:offset + seq_len]
+ scale = 1.0
+ if self.use_xpos:
+ power = (t - len(t) // 2) / self.scale_base
+ scale = self.scale ** rearrange(power, 'n -> n 1')
+ scale = torch.cat((scale, scale), dim=-1)
+ if should_cache:
+ self.tmp_store('cached_scales', scale)
+ return scale
+
+ def get_axial_freqs(self, *dims):
+ Colon = slice(None)
+ all_freqs = []
+ for (ind, dim) in enumerate(dims):
+ if self.freqs_for == 'pixel':
+ pos = torch.linspace(-1, 1, steps=dim, device=self.device)
+ else:
+ pos = torch.arange(dim, device=self.device)
+ freqs = self.forward(pos, seq_len=dim)
+ all_axis = [None] * len(dims)
+ all_axis[ind] = Colon
+ new_axis_slice = (Ellipsis, *all_axis, Colon)
+ all_freqs.append(freqs[new_axis_slice])
+ all_freqs = broadcast_tensors(*all_freqs)
+ return torch.cat(all_freqs, dim=-1)
+
+ @autocast(enabled=False)
+ def forward(self, t, seq_len=None, offset=0):
+ should_cache = self.cache_if_possible and (not self.learned_freq) and exists(seq_len) and (self.freqs_for != 'pixel')
+ if should_cache and exists(self.cached_freqs) and (offset + seq_len <= self.cached_freqs.shape[0]):
+ return self.cached_freqs[offset:offset + seq_len].detach()
+ freqs = self.freqs
+ freqs = einsum('..., f -> ... f', t.type(freqs.dtype), freqs)
+ freqs = repeat(freqs, '... n -> ... (n r)', r=2)
+ if should_cache:
+ self.tmp_store('cached_freqs', freqs.detach())
+ return freqs
diff --git a/tools/uvr5/vr.py b/tools/uvr5/vr.py
new file mode 100644
index 0000000..0d40acb
--- /dev/null
+++ b/tools/uvr5/vr.py
@@ -0,0 +1,350 @@
+import os
+
+parent_directory = os.path.dirname(os.path.abspath(__file__))
+import logging
+
+logger = logging.getLogger(__name__)
+
+import librosa
+import numpy as np
+import soundfile as sf
+import torch
+from tools.uvr5.lib.lib_v5 import nets_61968KB as Nets
+from tools.uvr5.lib.lib_v5 import spec_utils
+from tools.uvr5.lib.lib_v5.model_param_init import ModelParameters
+from tools.uvr5.lib.lib_v5.nets_new import CascadedNet
+from tools.uvr5.lib.utils import inference
+
+
+class AudioPre:
+ def __init__(self, agg, model_path, device, is_half, tta=False):
+ self.model_path = model_path
+ self.device = device
+ self.data = {
+ # Processing Options
+ "postprocess": False,
+ "tta": tta,
+ # Constants
+ "window_size": 512,
+ "agg": agg,
+ "high_end_process": "mirroring",
+ }
+ mp = ModelParameters("%s/lib/lib_v5/modelparams/4band_v2.json" % parent_directory)
+ model = Nets.CascadedASPPNet(mp.param["bins"] * 2)
+ cpk = torch.load(model_path, map_location="cpu")
+ model.load_state_dict(cpk)
+ model.eval()
+ if is_half:
+ model = model.half().to(device)
+ else:
+ model = model.to(device)
+
+ self.mp = mp
+ self.model = model
+
+ def _path_audio_(self, music_file, ins_root=None, vocal_root=None, format="flac", is_hp3=False):
+ if ins_root is None and vocal_root is None:
+ return "No save root."
+ name = os.path.basename(music_file)
+ if ins_root is not None:
+ os.makedirs(ins_root, exist_ok=True)
+ if vocal_root is not None:
+ os.makedirs(vocal_root, exist_ok=True)
+ X_wave, y_wave, X_spec_s, y_spec_s = {}, {}, {}, {}
+ bands_n = len(self.mp.param["band"])
+ # print(bands_n)
+ for d in range(bands_n, 0, -1):
+ bp = self.mp.param["band"][d]
+ if d == bands_n: # high-end band
+ (
+ X_wave[d],
+ _,
+ ) = librosa.core.load( # 理论上librosa读取可能对某些音频有bug,应该上ffmpeg读取,但是太麻烦了弃坑
+ music_file,
+ sr=bp["sr"],
+ mono=False,
+ dtype=np.float32,
+ res_type=bp["res_type"],
+ )
+ if X_wave[d].ndim == 1:
+ X_wave[d] = np.asfortranarray([X_wave[d], X_wave[d]])
+ else: # lower bands
+ X_wave[d] = librosa.core.resample(
+ X_wave[d + 1],
+ orig_sr=self.mp.param["band"][d + 1]["sr"],
+ target_sr=bp["sr"],
+ res_type=bp["res_type"],
+ )
+ # Stft of wave source
+ X_spec_s[d] = spec_utils.wave_to_spectrogram_mt(
+ X_wave[d],
+ bp["hl"],
+ bp["n_fft"],
+ self.mp.param["mid_side"],
+ self.mp.param["mid_side_b2"],
+ self.mp.param["reverse"],
+ )
+ # pdb.set_trace()
+ if d == bands_n and self.data["high_end_process"] != "none":
+ input_high_end_h = (bp["n_fft"] // 2 - bp["crop_stop"]) + (
+ self.mp.param["pre_filter_stop"] - self.mp.param["pre_filter_start"]
+ )
+ input_high_end = X_spec_s[d][:, bp["n_fft"] // 2 - input_high_end_h : bp["n_fft"] // 2, :]
+
+ X_spec_m = spec_utils.combine_spectrograms(X_spec_s, self.mp)
+ aggresive_set = float(self.data["agg"] / 100)
+ aggressiveness = {
+ "value": aggresive_set,
+ "split_bin": self.mp.param["band"][1]["crop_stop"],
+ }
+ with torch.no_grad():
+ pred, X_mag, X_phase = inference(X_spec_m, self.device, self.model, aggressiveness, self.data)
+ # Postprocess
+ if self.data["postprocess"]:
+ pred_inv = np.clip(X_mag - pred, 0, np.inf)
+ pred = spec_utils.mask_silence(pred, pred_inv)
+ y_spec_m = pred * X_phase
+ v_spec_m = X_spec_m - y_spec_m
+
+ if is_hp3 == True:
+ ins_root, vocal_root = vocal_root, ins_root
+
+ if ins_root is not None:
+ if self.data["high_end_process"].startswith("mirroring"):
+ input_high_end_ = spec_utils.mirroring(self.data["high_end_process"], y_spec_m, input_high_end, self.mp)
+ wav_instrument = spec_utils.cmb_spectrogram_to_wave(
+ y_spec_m, self.mp, input_high_end_h, input_high_end_
+ )
+ else:
+ wav_instrument = spec_utils.cmb_spectrogram_to_wave(y_spec_m, self.mp)
+ logger.info("%s instruments done" % name)
+ if is_hp3 == True:
+ head = "vocal_"
+ else:
+ head = "instrument_"
+ if format in ["wav", "flac"]:
+ sf.write(
+ os.path.join(
+ ins_root,
+ head + "{}_{}.{}".format(name, self.data["agg"], format),
+ ),
+ (np.array(wav_instrument) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ ) #
+ else:
+ path = os.path.join(ins_root, head + "{}_{}.wav".format(name, self.data["agg"]))
+ sf.write(
+ path,
+ (np.array(wav_instrument) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ )
+ if os.path.exists(path):
+ opt_format_path = path[:-4] + ".%s" % format
+ cmd = 'ffmpeg -i "%s" -vn "%s" -q:a 2 -y' % (path, opt_format_path)
+ print(cmd)
+ os.system(cmd)
+ if os.path.exists(opt_format_path):
+ try:
+ os.remove(path)
+ except:
+ pass
+ if vocal_root is not None:
+ if is_hp3 == True:
+ head = "instrument_"
+ else:
+ head = "vocal_"
+ if self.data["high_end_process"].startswith("mirroring"):
+ input_high_end_ = spec_utils.mirroring(self.data["high_end_process"], v_spec_m, input_high_end, self.mp)
+ wav_vocals = spec_utils.cmb_spectrogram_to_wave(v_spec_m, self.mp, input_high_end_h, input_high_end_)
+ else:
+ wav_vocals = spec_utils.cmb_spectrogram_to_wave(v_spec_m, self.mp)
+ logger.info("%s vocals done" % name)
+ if format in ["wav", "flac"]:
+ sf.write(
+ os.path.join(
+ vocal_root,
+ head + "{}_{}.{}".format(name, self.data["agg"], format),
+ ),
+ (np.array(wav_vocals) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ )
+ else:
+ path = os.path.join(vocal_root, head + "{}_{}.wav".format(name, self.data["agg"]))
+ sf.write(
+ path,
+ (np.array(wav_vocals) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ )
+ if os.path.exists(path):
+ opt_format_path = path[:-4] + ".%s" % format
+ cmd = 'ffmpeg -i "%s" -vn "%s" -q:a 2 -y' % (path, opt_format_path)
+ print(cmd)
+ os.system(cmd)
+ if os.path.exists(opt_format_path):
+ try:
+ os.remove(path)
+ except:
+ pass
+
+
+class AudioPreDeEcho:
+ def __init__(self, agg, model_path, device, is_half, tta=False):
+ self.model_path = model_path
+ self.device = device
+ self.data = {
+ # Processing Options
+ "postprocess": False,
+ "tta": tta,
+ # Constants
+ "window_size": 512,
+ "agg": agg,
+ "high_end_process": "mirroring",
+ }
+ mp = ModelParameters("%s/lib/lib_v5/modelparams/4band_v3.json" % parent_directory)
+ nout = 64 if "DeReverb" in model_path else 48
+ model = CascadedNet(mp.param["bins"] * 2, nout)
+ cpk = torch.load(model_path, map_location="cpu")
+ model.load_state_dict(cpk)
+ model.eval()
+ if is_half:
+ model = model.half().to(device)
+ else:
+ model = model.to(device)
+
+ self.mp = mp
+ self.model = model
+
+ def _path_audio_(
+ self, music_file, vocal_root=None, ins_root=None, format="flac", is_hp3=False
+ ): # 3个VR模型vocal和ins是反的
+ if ins_root is None and vocal_root is None:
+ return "No save root."
+ name = os.path.basename(music_file)
+ if ins_root is not None:
+ os.makedirs(ins_root, exist_ok=True)
+ if vocal_root is not None:
+ os.makedirs(vocal_root, exist_ok=True)
+ X_wave, y_wave, X_spec_s, y_spec_s = {}, {}, {}, {}
+ bands_n = len(self.mp.param["band"])
+ # print(bands_n)
+ for d in range(bands_n, 0, -1):
+ bp = self.mp.param["band"][d]
+ if d == bands_n: # high-end band
+ (
+ X_wave[d],
+ _,
+ ) = librosa.core.load( # 理论上librosa读取可能对某些音频有bug,应该上ffmpeg读取,但是太麻烦了弃坑
+ music_file,
+ sr=bp["sr"],
+ mono=False,
+ dtype=np.float32,
+ res_type=bp["res_type"],
+ )
+ if X_wave[d].ndim == 1:
+ X_wave[d] = np.asfortranarray([X_wave[d], X_wave[d]])
+ else: # lower bands
+ X_wave[d] = librosa.core.resample(
+ X_wave[d + 1],
+ orig_sr=self.mp.param["band"][d + 1]["sr"],
+ target_sr=bp["sr"],
+ res_type=bp["res_type"],
+ )
+ # Stft of wave source
+ X_spec_s[d] = spec_utils.wave_to_spectrogram_mt(
+ X_wave[d],
+ bp["hl"],
+ bp["n_fft"],
+ self.mp.param["mid_side"],
+ self.mp.param["mid_side_b2"],
+ self.mp.param["reverse"],
+ )
+ # pdb.set_trace()
+ if d == bands_n and self.data["high_end_process"] != "none":
+ input_high_end_h = (bp["n_fft"] // 2 - bp["crop_stop"]) + (
+ self.mp.param["pre_filter_stop"] - self.mp.param["pre_filter_start"]
+ )
+ input_high_end = X_spec_s[d][:, bp["n_fft"] // 2 - input_high_end_h : bp["n_fft"] // 2, :]
+
+ X_spec_m = spec_utils.combine_spectrograms(X_spec_s, self.mp)
+ aggresive_set = float(self.data["agg"] / 100)
+ aggressiveness = {
+ "value": aggresive_set,
+ "split_bin": self.mp.param["band"][1]["crop_stop"],
+ }
+ with torch.no_grad():
+ pred, X_mag, X_phase = inference(X_spec_m, self.device, self.model, aggressiveness, self.data)
+ # Postprocess
+ if self.data["postprocess"]:
+ pred_inv = np.clip(X_mag - pred, 0, np.inf)
+ pred = spec_utils.mask_silence(pred, pred_inv)
+ y_spec_m = pred * X_phase
+ v_spec_m = X_spec_m - y_spec_m
+
+ if ins_root is not None:
+ if self.data["high_end_process"].startswith("mirroring"):
+ input_high_end_ = spec_utils.mirroring(self.data["high_end_process"], y_spec_m, input_high_end, self.mp)
+ wav_instrument = spec_utils.cmb_spectrogram_to_wave(
+ y_spec_m, self.mp, input_high_end_h, input_high_end_
+ )
+ else:
+ wav_instrument = spec_utils.cmb_spectrogram_to_wave(y_spec_m, self.mp)
+ logger.info("%s instruments done" % name)
+ if format in ["wav", "flac"]:
+ sf.write(
+ os.path.join(
+ ins_root,
+ "vocal_{}_{}.{}".format(name, self.data["agg"], format),
+ ),
+ (np.array(wav_instrument) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ ) #
+ else:
+ path = os.path.join(ins_root, "vocal_{}_{}.wav".format(name, self.data["agg"]))
+ sf.write(
+ path,
+ (np.array(wav_instrument) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ )
+ if os.path.exists(path):
+ opt_format_path = path[:-4] + ".%s" % format
+ cmd = 'ffmpeg -i "%s" -vn "%s" -q:a 2 -y' % (path, opt_format_path)
+ print(cmd)
+ os.system(cmd)
+ if os.path.exists(opt_format_path):
+ try:
+ os.remove(path)
+ except:
+ pass
+ if vocal_root is not None:
+ if self.data["high_end_process"].startswith("mirroring"):
+ input_high_end_ = spec_utils.mirroring(self.data["high_end_process"], v_spec_m, input_high_end, self.mp)
+ wav_vocals = spec_utils.cmb_spectrogram_to_wave(v_spec_m, self.mp, input_high_end_h, input_high_end_)
+ else:
+ wav_vocals = spec_utils.cmb_spectrogram_to_wave(v_spec_m, self.mp)
+ logger.info("%s vocals done" % name)
+ if format in ["wav", "flac"]:
+ sf.write(
+ os.path.join(
+ vocal_root,
+ "instrument_{}_{}.{}".format(name, self.data["agg"], format),
+ ),
+ (np.array(wav_vocals) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ )
+ else:
+ path = os.path.join(vocal_root, "instrument_{}_{}.wav".format(name, self.data["agg"]))
+ sf.write(
+ path,
+ (np.array(wav_vocals) * 32768).astype("int16"),
+ self.mp.param["sr"],
+ )
+ if os.path.exists(path):
+ opt_format_path = path[:-4] + ".%s" % format
+ cmd = 'ffmpeg -i "%s" -vn "%s" -q:a 2 -y' % (path, opt_format_path)
+ print(cmd)
+ os.system(cmd)
+ if os.path.exists(opt_format_path):
+ try:
+ os.remove(path)
+ except:
+ pass
diff --git a/tools/uvr5/webui.py b/tools/uvr5/webui.py
new file mode 100644
index 0000000..23e20b4
--- /dev/null
+++ b/tools/uvr5/webui.py
@@ -0,0 +1,133 @@
+import logging
+import os
+import traceback
+
+import ffmpeg
+import torch
+
+from configs.config import Config, IS_GPU
+from tools.uvr5.bsroformer import Roformer_Loader
+from tools.uvr5.mdxnet import MDXNetDereverb
+from tools.uvr5.vr import AudioPre, AudioPreDeEcho
+from i18n.i18n import I18nAuto
+
+
+logger = logging.getLogger(__name__)
+i18n = I18nAuto()
+config = Config()
+weight_uvr5_root = os.getenv("weight_uvr5_root", "assets/uvr5_weights")
+
+
+def clean_path(path):
+ path = path or ""
+ if path.endswith(("\\", "/")):
+ path = path[:-1]
+ return path.replace("/", os.sep).replace("\\", os.sep).strip(" '\n\"\u202a")
+
+
+def uvr(model_name, inp_root, save_root_vocal, paths, save_root_ins, agg, format0):
+ infos = []
+ try:
+ inp_root = clean_path(inp_root)
+ save_root_vocal = clean_path(save_root_vocal)
+ save_root_ins = clean_path(save_root_ins)
+ is_hp3 = "HP3" in model_name
+ if model_name == "onnx_dereverb_By_FoxJoy":
+ if config.dml:
+ providers = ["DmlExecutionProvider", "CPUExecutionProvider"]
+ elif IS_GPU:
+ providers = ["CUDAExecutionProvider", "CPUExecutionProvider"]
+ else:
+ providers = ["CPUExecutionProvider"]
+ pre_fun = MDXNetDereverb(15, providers)
+ elif "roformer" in model_name.lower():
+ pre_fun = Roformer_Loader(
+ model_path=os.path.join(weight_uvr5_root, model_name + ".ckpt"),
+ config_path=os.path.join(weight_uvr5_root, model_name + ".yaml"),
+ device=config.device,
+ is_half=config.is_half,
+ )
+ if not os.path.exists(
+ os.path.join(weight_uvr5_root, model_name + ".yaml")
+ ):
+ infos.append(i18n("未找到Roformer模型配置文件,正在使用内置默认配置"))
+ yield "\n".join(infos)
+ else:
+ func = AudioPre if "DeEcho" not in model_name else AudioPreDeEcho
+ pre_fun = func(
+ agg=int(agg),
+ model_path=os.path.join(weight_uvr5_root, model_name + ".pth"),
+ device=config.device,
+ is_half=config.is_half,
+ )
+ if inp_root:
+ paths = [os.path.join(inp_root, name) for name in os.listdir(inp_root)]
+ else:
+ paths = [path.name for path in (paths or [])]
+ for path in paths:
+ inp_path = os.path.join(inp_root, path)
+ if not os.path.isfile(inp_path):
+ continue
+ need_reformat = True
+ done = False
+ try:
+ info = ffmpeg.probe(inp_path, cmd="ffprobe")
+ if (
+ info["streams"][0]["channels"] == 2
+ and info["streams"][0]["sample_rate"] == "44100"
+ ):
+ need_reformat = False
+ pre_fun._path_audio_(
+ inp_path,
+ save_root_ins,
+ save_root_vocal,
+ format0,
+ is_hp3,
+ )
+ done = True
+ except:
+ traceback.print_exc()
+ if need_reformat:
+ tmp_path = "%s/%s.reformatted.wav" % (
+ os.environ["TEMP"],
+ os.path.basename(inp_path),
+ )
+ os.system(
+ 'ffmpeg -i "%s" -vn -acodec pcm_s16le -ac 2 -ar 44100 "%s" -y'
+ % (inp_path, tmp_path)
+ )
+ inp_path = tmp_path
+ try:
+ if not done:
+ pre_fun._path_audio_(
+ inp_path,
+ save_root_ins,
+ save_root_vocal,
+ format0,
+ is_hp3,
+ )
+ infos.append(i18n("%s → 成功") % os.path.basename(inp_path))
+ yield "\n".join(infos)
+ except Exception:
+ infos.append(
+ "%s → %s\n%s"
+ % (os.path.basename(inp_path), i18n("失败"), traceback.format_exc())
+ )
+ yield "\n".join(infos)
+ except Exception:
+ infos.append("%s\n%s" % (i18n("失败"), traceback.format_exc()))
+ yield "\n".join(infos)
+ finally:
+ try:
+ if model_name == "onnx_dereverb_By_FoxJoy":
+ del pre_fun.pred.model
+ del pre_fun.pred.model_
+ else:
+ del pre_fun.model
+ del pre_fun
+ except:
+ traceback.print_exc()
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ logger.info("Executed torch.cuda.empty_cache()")
+ yield "\n".join(infos)
diff --git a/train/data_utils.py b/train/data_utils.py
new file mode 100644
index 0000000..d03faf2
--- /dev/null
+++ b/train/data_utils.py
@@ -0,0 +1,517 @@
+import os
+import traceback
+import logging
+
+logger = logging.getLogger(__name__)
+
+import numpy as np
+import torch
+import torch.utils.data
+
+from train.mel_processing import spectrogram_torch
+from train.utils import load_filepaths_and_text, load_wav_to_torch
+
+
+class TextAudioLoaderMultiNSFsid(torch.utils.data.Dataset):
+ """
+ 1) loads audio, text pairs
+ 2) normalizes text and converts them to sequences of integers
+ 3) computes spectrograms from audio files.
+ """
+
+ def __init__(self, audiopaths_and_text, hparams):
+ self.audiopaths_and_text = load_filepaths_and_text(audiopaths_and_text)
+ self.max_wav_value = hparams.max_wav_value
+ self.sampling_rate = hparams.sampling_rate
+ self.filter_length = hparams.filter_length
+ self.hop_length = hparams.hop_length
+ self.win_length = hparams.win_length
+ self.sampling_rate = hparams.sampling_rate
+ self.min_text_len = getattr(hparams, "min_text_len", 1)
+ self.max_text_len = getattr(hparams, "max_text_len", 5000)
+ self._filter()
+
+ def _filter(self):
+ """
+ Filter text & store spec lengths
+ """
+ # Store spectrogram lengths for Bucketing
+ # wav_length ~= file_size / (wav_channels * Bytes per dim) = file_size / (1 * 2)
+ # spec_length = wav_length // hop_length
+ audiopaths_and_text_new = []
+ lengths = []
+ for audiopath, text, pitch, pitchf, dv in self.audiopaths_and_text:
+ if self.min_text_len <= len(text) and len(text) <= self.max_text_len:
+ audiopaths_and_text_new.append([audiopath, text, pitch, pitchf, dv])
+ lengths.append(os.path.getsize(audiopath) // (3 * self.hop_length))
+ self.audiopaths_and_text = audiopaths_and_text_new
+ self.lengths = lengths
+
+ def get_sid(self, sid):
+ sid = torch.LongTensor([int(sid)])
+ return sid
+
+ def get_audio_text_pair(self, audiopath_and_text):
+ # separate filename and text
+ file = audiopath_and_text[0]
+ phone = audiopath_and_text[1]
+ pitch = audiopath_and_text[2]
+ pitchf = audiopath_and_text[3]
+ dv = audiopath_and_text[4]
+
+ phone, pitch, pitchf = self.get_labels(phone, pitch, pitchf)
+ spec, wav = self.get_audio(file)
+ dv = self.get_sid(dv)
+
+ len_phone = phone.size()[0]
+ len_spec = spec.size()[-1]
+ # print(123,phone.shape,pitch.shape,spec.shape)
+ if len_phone != len_spec:
+ len_min = min(len_phone, len_spec)
+ # amor
+ len_wav = len_min * self.hop_length
+
+ spec = spec[:, :len_min]
+ wav = wav[:, :len_wav]
+
+ phone = phone[:len_min, :]
+ pitch = pitch[:len_min]
+ pitchf = pitchf[:len_min]
+
+ return (spec, wav, phone, pitch, pitchf, dv)
+
+ def get_labels(self, phone, pitch, pitchf):
+ phone = np.load(phone)
+ phone = np.repeat(phone, 2, axis=0)
+ pitch = np.load(pitch)
+ pitchf = np.load(pitchf)
+ n_num = min(phone.shape[0], 900) # DistributedBucketSampler
+ # print(234,phone.shape,pitch.shape)
+ phone = phone[:n_num, :]
+ pitch = pitch[:n_num]
+ pitchf = pitchf[:n_num]
+ phone = torch.FloatTensor(phone)
+ pitch = torch.LongTensor(pitch)
+ pitchf = torch.FloatTensor(pitchf)
+ return phone, pitch, pitchf
+
+ def get_audio(self, filename):
+ audio, sampling_rate = load_wav_to_torch(filename)
+ if sampling_rate != self.sampling_rate:
+ raise ValueError(
+ "{} SR doesn't match target {} SR".format(
+ sampling_rate, self.sampling_rate
+ )
+ )
+ audio_norm = audio
+ # audio_norm = audio / self.max_wav_value
+ # audio_norm = audio / np.abs(audio).max()
+
+ audio_norm = audio_norm.unsqueeze(0)
+ spec_filename = filename.replace(".wav", ".spec.pt")
+ if os.path.exists(spec_filename):
+ try:
+ spec = torch.load(spec_filename)
+ except:
+ logger.warning("%s %s", spec_filename, traceback.format_exc())
+ spec = spectrogram_torch(
+ audio_norm,
+ self.filter_length,
+ self.sampling_rate,
+ self.hop_length,
+ self.win_length,
+ center=False,
+ )
+ spec = torch.squeeze(spec, 0)
+ torch.save(spec, spec_filename, _use_new_zipfile_serialization=False)
+ else:
+ spec = spectrogram_torch(
+ audio_norm,
+ self.filter_length,
+ self.sampling_rate,
+ self.hop_length,
+ self.win_length,
+ center=False,
+ )
+ spec = torch.squeeze(spec, 0)
+ torch.save(spec, spec_filename, _use_new_zipfile_serialization=False)
+ return spec, audio_norm
+
+ def __getitem__(self, index):
+ return self.get_audio_text_pair(self.audiopaths_and_text[index])
+
+ def __len__(self):
+ return len(self.audiopaths_and_text)
+
+
+class TextAudioCollateMultiNSFsid:
+ """Zero-pads model inputs and targets"""
+
+ def __init__(self, return_ids=False):
+ self.return_ids = return_ids
+
+ def __call__(self, batch):
+ """Collate's training batch from normalized text and aduio
+ PARAMS
+ ------
+ batch: [text_normalized, spec_normalized, wav_normalized]
+ """
+ # Right zero-pad all one-hot text sequences to max input length
+ _, ids_sorted_decreasing = torch.sort(
+ torch.LongTensor([x[0].size(1) for x in batch]), dim=0, descending=True
+ )
+
+ max_spec_len = max([x[0].size(1) for x in batch])
+ max_wave_len = max([x[1].size(1) for x in batch])
+ spec_lengths = torch.LongTensor(len(batch))
+ wave_lengths = torch.LongTensor(len(batch))
+ spec_padded = torch.FloatTensor(len(batch), batch[0][0].size(0), max_spec_len)
+ wave_padded = torch.FloatTensor(len(batch), 1, max_wave_len)
+ spec_padded.zero_()
+ wave_padded.zero_()
+
+ max_phone_len = max([x[2].size(0) for x in batch])
+ phone_lengths = torch.LongTensor(len(batch))
+ phone_padded = torch.FloatTensor(
+ len(batch), max_phone_len, batch[0][2].shape[1]
+ ) # (spec, wav, phone, pitch)
+ pitch_padded = torch.LongTensor(len(batch), max_phone_len)
+ pitchf_padded = torch.FloatTensor(len(batch), max_phone_len)
+ phone_padded.zero_()
+ pitch_padded.zero_()
+ pitchf_padded.zero_()
+ # dv = torch.FloatTensor(len(batch), 256)#gin=256
+ sid = torch.LongTensor(len(batch))
+
+ for i in range(len(ids_sorted_decreasing)):
+ row = batch[ids_sorted_decreasing[i]]
+
+ spec = row[0]
+ spec_padded[i, :, : spec.size(1)] = spec
+ spec_lengths[i] = spec.size(1)
+
+ wave = row[1]
+ wave_padded[i, :, : wave.size(1)] = wave
+ wave_lengths[i] = wave.size(1)
+
+ phone = row[2]
+ phone_padded[i, : phone.size(0), :] = phone
+ phone_lengths[i] = phone.size(0)
+
+ pitch = row[3]
+ pitch_padded[i, : pitch.size(0)] = pitch
+ pitchf = row[4]
+ pitchf_padded[i, : pitchf.size(0)] = pitchf
+
+ # dv[i] = row[5]
+ sid[i] = row[5]
+
+ return (
+ phone_padded,
+ phone_lengths,
+ pitch_padded,
+ pitchf_padded,
+ spec_padded,
+ spec_lengths,
+ wave_padded,
+ wave_lengths,
+ # dv
+ sid,
+ )
+
+
+class TextAudioLoader(torch.utils.data.Dataset):
+ """
+ 1) loads audio, text pairs
+ 2) normalizes text and converts them to sequences of integers
+ 3) computes spectrograms from audio files.
+ """
+
+ def __init__(self, audiopaths_and_text, hparams):
+ self.audiopaths_and_text = load_filepaths_and_text(audiopaths_and_text)
+ self.max_wav_value = hparams.max_wav_value
+ self.sampling_rate = hparams.sampling_rate
+ self.filter_length = hparams.filter_length
+ self.hop_length = hparams.hop_length
+ self.win_length = hparams.win_length
+ self.sampling_rate = hparams.sampling_rate
+ self.min_text_len = getattr(hparams, "min_text_len", 1)
+ self.max_text_len = getattr(hparams, "max_text_len", 5000)
+ self._filter()
+
+ def _filter(self):
+ """
+ Filter text & store spec lengths
+ """
+ # Store spectrogram lengths for Bucketing
+ # wav_length ~= file_size / (wav_channels * Bytes per dim) = file_size / (1 * 2)
+ # spec_length = wav_length // hop_length
+ audiopaths_and_text_new = []
+ lengths = []
+ for audiopath, text, dv in self.audiopaths_and_text:
+ if self.min_text_len <= len(text) and len(text) <= self.max_text_len:
+ audiopaths_and_text_new.append([audiopath, text, dv])
+ lengths.append(os.path.getsize(audiopath) // (3 * self.hop_length))
+ self.audiopaths_and_text = audiopaths_and_text_new
+ self.lengths = lengths
+
+ def get_sid(self, sid):
+ sid = torch.LongTensor([int(sid)])
+ return sid
+
+ def get_audio_text_pair(self, audiopath_and_text):
+ # separate filename and text
+ file = audiopath_and_text[0]
+ phone = audiopath_and_text[1]
+ dv = audiopath_and_text[2]
+
+ phone = self.get_labels(phone)
+ spec, wav = self.get_audio(file)
+ dv = self.get_sid(dv)
+
+ len_phone = phone.size()[0]
+ len_spec = spec.size()[-1]
+ if len_phone != len_spec:
+ len_min = min(len_phone, len_spec)
+ len_wav = len_min * self.hop_length
+ spec = spec[:, :len_min]
+ wav = wav[:, :len_wav]
+ phone = phone[:len_min, :]
+ return (spec, wav, phone, dv)
+
+ def get_labels(self, phone):
+ phone = np.load(phone)
+ phone = np.repeat(phone, 2, axis=0)
+ n_num = min(phone.shape[0], 900) # DistributedBucketSampler
+ phone = phone[:n_num, :]
+ phone = torch.FloatTensor(phone)
+ return phone
+
+ def get_audio(self, filename):
+ audio, sampling_rate = load_wav_to_torch(filename)
+ if sampling_rate != self.sampling_rate:
+ raise ValueError(
+ "{} SR doesn't match target {} SR".format(
+ sampling_rate, self.sampling_rate
+ )
+ )
+ audio_norm = audio
+ # audio_norm = audio / self.max_wav_value
+ # audio_norm = audio / np.abs(audio).max()
+
+ audio_norm = audio_norm.unsqueeze(0)
+ spec_filename = filename.replace(".wav", ".spec.pt")
+ if os.path.exists(spec_filename):
+ try:
+ spec = torch.load(spec_filename)
+ except:
+ logger.warning("%s %s", spec_filename, traceback.format_exc())
+ spec = spectrogram_torch(
+ audio_norm,
+ self.filter_length,
+ self.sampling_rate,
+ self.hop_length,
+ self.win_length,
+ center=False,
+ )
+ spec = torch.squeeze(spec, 0)
+ torch.save(spec, spec_filename, _use_new_zipfile_serialization=False)
+ else:
+ spec = spectrogram_torch(
+ audio_norm,
+ self.filter_length,
+ self.sampling_rate,
+ self.hop_length,
+ self.win_length,
+ center=False,
+ )
+ spec = torch.squeeze(spec, 0)
+ torch.save(spec, spec_filename, _use_new_zipfile_serialization=False)
+ return spec, audio_norm
+
+ def __getitem__(self, index):
+ return self.get_audio_text_pair(self.audiopaths_and_text[index])
+
+ def __len__(self):
+ return len(self.audiopaths_and_text)
+
+
+class TextAudioCollate:
+ """Zero-pads model inputs and targets"""
+
+ def __init__(self, return_ids=False):
+ self.return_ids = return_ids
+
+ def __call__(self, batch):
+ """Collate's training batch from normalized text and aduio
+ PARAMS
+ ------
+ batch: [text_normalized, spec_normalized, wav_normalized]
+ """
+ # Right zero-pad all one-hot text sequences to max input length
+ _, ids_sorted_decreasing = torch.sort(
+ torch.LongTensor([x[0].size(1) for x in batch]), dim=0, descending=True
+ )
+
+ max_spec_len = max([x[0].size(1) for x in batch])
+ max_wave_len = max([x[1].size(1) for x in batch])
+ spec_lengths = torch.LongTensor(len(batch))
+ wave_lengths = torch.LongTensor(len(batch))
+ spec_padded = torch.FloatTensor(len(batch), batch[0][0].size(0), max_spec_len)
+ wave_padded = torch.FloatTensor(len(batch), 1, max_wave_len)
+ spec_padded.zero_()
+ wave_padded.zero_()
+
+ max_phone_len = max([x[2].size(0) for x in batch])
+ phone_lengths = torch.LongTensor(len(batch))
+ phone_padded = torch.FloatTensor(
+ len(batch), max_phone_len, batch[0][2].shape[1]
+ )
+ phone_padded.zero_()
+ sid = torch.LongTensor(len(batch))
+
+ for i in range(len(ids_sorted_decreasing)):
+ row = batch[ids_sorted_decreasing[i]]
+
+ spec = row[0]
+ spec_padded[i, :, : spec.size(1)] = spec
+ spec_lengths[i] = spec.size(1)
+
+ wave = row[1]
+ wave_padded[i, :, : wave.size(1)] = wave
+ wave_lengths[i] = wave.size(1)
+
+ phone = row[2]
+ phone_padded[i, : phone.size(0), :] = phone
+ phone_lengths[i] = phone.size(0)
+
+ sid[i] = row[3]
+
+ return (
+ phone_padded,
+ phone_lengths,
+ spec_padded,
+ spec_lengths,
+ wave_padded,
+ wave_lengths,
+ sid,
+ )
+
+
+class DistributedBucketSampler(torch.utils.data.distributed.DistributedSampler):
+ """
+ Maintain similar input lengths in a batch.
+ Length groups are specified by boundaries.
+ Ex) boundaries = [b1, b2, b3] -> any batch is included either {x | b1 < length(x) <=b2} or {x | b2 < length(x) <= b3}.
+
+ It removes samples which are not included in the boundaries.
+ Ex) boundaries = [b1, b2, b3] -> any x s.t. length(x) <= b1 or length(x) > b3 are discarded.
+ """
+
+ def __init__(
+ self,
+ dataset,
+ batch_size,
+ boundaries,
+ num_replicas=None,
+ rank=None,
+ shuffle=True,
+ ):
+ super().__init__(dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle)
+ self.lengths = dataset.lengths
+ self.batch_size = batch_size
+ self.boundaries = boundaries
+
+ self.buckets, self.num_samples_per_bucket = self._create_buckets()
+ self.total_size = sum(self.num_samples_per_bucket)
+ self.num_samples = self.total_size // self.num_replicas
+
+ def _create_buckets(self):
+ buckets = [[] for _ in range(len(self.boundaries) - 1)]
+ for i in range(len(self.lengths)):
+ length = self.lengths[i]
+ idx_bucket = self._bisect(length)
+ if idx_bucket != -1:
+ buckets[idx_bucket].append(i)
+
+ for i in range(len(buckets) - 1, -1, -1): #
+ if len(buckets[i]) == 0:
+ buckets.pop(i)
+ self.boundaries.pop(i + 1)
+
+ num_samples_per_bucket = []
+ for i in range(len(buckets)):
+ len_bucket = len(buckets[i])
+ total_batch_size = self.num_replicas * self.batch_size
+ rem = (
+ total_batch_size - (len_bucket % total_batch_size)
+ ) % total_batch_size
+ num_samples_per_bucket.append(len_bucket + rem)
+ return buckets, num_samples_per_bucket
+
+ def __iter__(self):
+ # deterministically shuffle based on epoch
+ g = torch.Generator()
+ g.manual_seed(self.epoch)
+
+ indices = []
+ if self.shuffle:
+ for bucket in self.buckets:
+ indices.append(torch.randperm(len(bucket), generator=g).tolist())
+ else:
+ for bucket in self.buckets:
+ indices.append(list(range(len(bucket))))
+
+ batches = []
+ for i in range(len(self.buckets)):
+ bucket = self.buckets[i]
+ len_bucket = len(bucket)
+ ids_bucket = indices[i]
+ num_samples_bucket = self.num_samples_per_bucket[i]
+
+ # add extra samples to make it evenly divisible
+ rem = num_samples_bucket - len_bucket
+ ids_bucket = (
+ ids_bucket
+ + ids_bucket * (rem // len_bucket)
+ + ids_bucket[: (rem % len_bucket)]
+ )
+
+ # subsample
+ ids_bucket = ids_bucket[self.rank :: self.num_replicas]
+
+ # batching
+ for j in range(len(ids_bucket) // self.batch_size):
+ batch = [
+ bucket[idx]
+ for idx in ids_bucket[
+ j * self.batch_size : (j + 1) * self.batch_size
+ ]
+ ]
+ batches.append(batch)
+
+ if self.shuffle:
+ batch_ids = torch.randperm(len(batches), generator=g).tolist()
+ batches = [batches[i] for i in batch_ids]
+ self.batches = batches
+
+ assert len(self.batches) * self.batch_size == self.num_samples
+ return iter(self.batches)
+
+ def _bisect(self, x, lo=0, hi=None):
+ if hi is None:
+ hi = len(self.boundaries) - 1
+
+ if hi > lo:
+ mid = (hi + lo) // 2
+ if self.boundaries[mid] < x and x <= self.boundaries[mid + 1]:
+ return mid
+ elif x <= self.boundaries[mid]:
+ return self._bisect(x, lo, mid)
+ else:
+ return self._bisect(x, mid + 1, hi)
+ else:
+ return -1
+
+ def __len__(self):
+ return self.num_samples // self.batch_size
diff --git a/train/dataset/extract_f0.py b/train/dataset/extract_f0.py
new file mode 100644
index 0000000..cdf0a36
--- /dev/null
+++ b/train/dataset/extract_f0.py
@@ -0,0 +1,228 @@
+import os
+import sys
+import traceback
+
+import parselmouth
+
+import logging
+
+import numpy as np
+
+from infer.audio import load_audio
+from i18n.i18n import I18nAuto
+from tools.progress import should_report
+
+
+i18n = I18nAuto()
+
+logging.getLogger("numba").setLevel(logging.WARNING)
+from multiprocessing import Process
+
+mode = sys.argv[1].lower()
+if mode == "cpu":
+ exp_dir = sys.argv[2]
+ n_p = int(sys.argv[3])
+ f0method = sys.argv[4]
+ device = "cpu"
+ is_half = False
+elif mode == "cuda":
+ n_part = int(sys.argv[2])
+ i_part = int(sys.argv[3])
+ i_gpu = sys.argv[4]
+ os.environ["CUDA_VISIBLE_DEVICES"] = str(i_gpu)
+ exp_dir = sys.argv[5]
+ is_half = sys.argv[6].lower() == "true"
+ f0method = "rmvpe"
+ device = "cuda"
+elif mode in ("dml", "directml"):
+ exp_dir = sys.argv[2]
+ f0method = "rmvpe"
+ is_half = False
+ import torch_directml
+
+ device = torch_directml.device(torch_directml.default_device())
+else:
+ raise ValueError("Unsupported F0 extraction mode: %s" % mode)
+
+f = open("%s/extract_f0_feature.log" % exp_dir, "a", encoding="utf8")
+
+
+def printt(strr):
+ print(strr)
+ f.write("%s\n" % strr)
+ f.flush()
+class FeatureInput(object):
+ def __init__(self, samplerate=16000, hop_size=160):
+ self.fs = samplerate
+ self.hop = hop_size
+
+ self.f0_bin = 256
+ self.f0_max = 1100.0
+ self.f0_min = 50.0
+ self.f0_mel_min = 1127 * np.log(1 + self.f0_min / 700)
+ self.f0_mel_max = 1127 * np.log(1 + self.f0_max / 700)
+
+ def compute_f0(self, path, f0_method):
+ if f0_method not in ("pm", "rmvpe"):
+ raise ValueError(i18n("仅支持pm和rmvpe音高提取算法"))
+ x = load_audio(path, self.fs)
+ p_len = x.shape[0] // self.hop
+ if f0_method == "pm":
+ time_step = 160 / 16000 * 1000
+ f0_min = 50
+ f0_max = 1100
+ f0 = (
+ parselmouth.Sound(x, self.fs)
+ .to_pitch_ac(
+ time_step=time_step / 1000,
+ voicing_threshold=0.6,
+ pitch_floor=f0_min,
+ pitch_ceiling=f0_max,
+ )
+ .selected_array["frequency"]
+ )
+ pad_size = (p_len - len(f0) + 1) // 2
+ if pad_size > 0 or p_len - len(f0) - pad_size > 0:
+ f0 = np.pad(
+ f0, [[pad_size, p_len - len(f0) - pad_size]], mode="constant"
+ )
+ elif f0_method == "rmvpe":
+ if hasattr(self, "model_rmvpe") == False:
+ from infer.rmvpe import RMVPE
+
+ printt(i18n("正在加载RMVPE模型"))
+ self.model_rmvpe = RMVPE(
+ "assets/rmvpe/rmvpe.pt", is_half=is_half, device=device
+ )
+ f0 = self.model_rmvpe.infer_from_audio(x, thred=0.03)
+ f0 = np.asarray(f0)
+ try:
+ uv = f0 == 0
+ f0[uv] = np.interp(np.where(uv)[0], np.where(~uv)[0], f0[~uv])
+ except Exception:
+ traceback.print_exc()
+ return None
+ return f0
+
+ def coarse_f0(self, f0):
+ f0_mel = 1127 * np.log(1 + f0 / 700)
+ f0_mel[f0_mel > 0] = (f0_mel[f0_mel > 0] - self.f0_mel_min) * (
+ self.f0_bin - 2
+ ) / (self.f0_mel_max - self.f0_mel_min) + 1
+
+ # use 0 or 1
+ f0_mel[f0_mel <= 1] = 1
+ f0_mel[f0_mel > self.f0_bin - 1] = self.f0_bin - 1
+ f0_coarse = np.rint(f0_mel).astype(int)
+ assert f0_coarse.max() <= 255 and f0_coarse.min() >= 1, (
+ f0_coarse.max(),
+ f0_coarse.min(),
+ )
+ return f0_coarse
+
+ def go(self, paths, f0_method, max_updates=5):
+ success = 0
+ skipped = 0
+ failed = 0
+ if len(paths) == 0:
+ printt(i18n("[F0提取] 无待处理音频,已全部跳过"))
+ else:
+ printt(i18n("[F0提取] 待处理:%s") % len(paths))
+ for idx, (inp_path, opt_path1, opt_path2) in enumerate(paths):
+ try:
+ if (
+ os.path.exists(opt_path1 + ".npy") == True
+ and os.path.exists(opt_path2 + ".npy") == True
+ ):
+ skipped += 1
+ continue
+ featur_pit = self.compute_f0(inp_path, f0_method)
+ if featur_pit is None:
+ skipped += 1
+ printt(i18n("音高全部为0,该音频无意义,跳过:%s") % inp_path)
+ continue
+ np.save(
+ opt_path2,
+ featur_pit,
+ allow_pickle=False,
+ ) # nsf
+ coarse_pit = self.coarse_f0(featur_pit)
+ np.save(
+ opt_path1,
+ coarse_pit,
+ allow_pickle=False,
+ ) # ori
+ success += 1
+ if should_report(idx, len(paths), max_updates):
+ printt(
+ i18n("[F0提取] 进度:%s/%s | 成功:%s | 跳过:%s | %s")
+ % (idx + 1, len(paths), success, skipped, os.path.basename(inp_path))
+ )
+ except Exception:
+ failed += 1
+ printt(
+ i18n("[F0提取][失败] %s\n%s")
+ % (inp_path, traceback.format_exc())
+ )
+ printt(
+ i18n("[F0提取] 完成 | 成功:%s | 跳过:%s | 失败:%s")
+ % (success, skipped, failed)
+ )
+
+
+if __name__ == "__main__":
+ # exp_dir=r"E:\codes\py39\dataset\mi-test"
+ # n_p=16
+ featureInput = FeatureInput()
+ paths = []
+ inp_root = "%s/1_16k_wavs" % (exp_dir)
+ opt_root1 = "%s/2a_f0" % (exp_dir)
+ opt_root2 = "%s/2b-f0nsf" % (exp_dir)
+
+ os.makedirs(opt_root1, exist_ok=True)
+ os.makedirs(opt_root2, exist_ok=True)
+ for name in sorted(list(os.listdir(inp_root))):
+ inp_path = "%s/%s" % (inp_root, name)
+ if "spec" in inp_path:
+ continue
+ opt_path1 = "%s/%s" % (opt_root1, name)
+ opt_path2 = "%s/%s" % (opt_root2, name)
+ if os.path.exists(opt_path1 + ".npy") and os.path.exists(
+ opt_path2 + ".npy"
+ ):
+ continue
+ paths.append([inp_path, opt_path1, opt_path2])
+
+ if mode == "cpu":
+ if not paths:
+ featureInput.go([], f0method, 1)
+ else:
+ worker_count = min(max(1, n_p), len(paths))
+ ps = []
+ for i in range(worker_count):
+ p = Process(
+ target=featureInput.go,
+ args=(
+ paths[i::worker_count],
+ f0method,
+ max(1, (12 + worker_count - 1) // worker_count),
+ ),
+ )
+ ps.append(p)
+ p.start()
+ for p in ps:
+ p.join()
+ elif mode == "cuda":
+ try:
+ featureInput.go(
+ paths[i_part::n_part],
+ "rmvpe",
+ max(1, (12 + n_part - 1) // n_part),
+ )
+ except Exception:
+ printt(i18n("[F0提取][失败] %s") % traceback.format_exc())
+ else:
+ try:
+ featureInput.go(paths, "rmvpe", 5)
+ except Exception:
+ printt(i18n("[F0提取][失败] %s") % traceback.format_exc())
diff --git a/train/dataset/extract_hubert_feature.py b/train/dataset/extract_hubert_feature.py
new file mode 100644
index 0000000..9fd2437
--- /dev/null
+++ b/train/dataset/extract_hubert_feature.py
@@ -0,0 +1,154 @@
+import os
+import sys
+import traceback
+
+device = sys.argv[1]
+n_part = int(sys.argv[2])
+i_part = int(sys.argv[3])
+if len(sys.argv) == 7:
+ exp_dir = sys.argv[4]
+ version = sys.argv[5]
+ is_half = sys.argv[6].lower() == "true"
+else:
+ i_gpu = sys.argv[4]
+ exp_dir = sys.argv[5]
+ os.environ["CUDA_VISIBLE_DEVICES"] = str(i_gpu)
+ version = sys.argv[6]
+ is_half = sys.argv[7].lower() == "true"
+import numpy as np
+import soundfile as sf
+import torch
+import torch.nn.functional as F
+
+from configs.config import get_device_dtype_sm
+from infer.hubert import (
+ HUBERT_MODEL_PATH,
+ extract_hubert_features,
+ hubert_audio_requires_normalization,
+ load_hubert_model,
+)
+from i18n.i18n import I18nAuto
+from tools.progress import should_report
+
+i18n = I18nAuto()
+
+if "privateuseone" not in device:
+ device = "cpu"
+ if torch.cuda.is_available():
+ selected_device, selected_dtype, _, _ = get_device_dtype_sm(0)
+ device = str(selected_device)
+ is_half = is_half and selected_dtype == torch.float16
+else:
+ import torch_directml
+
+ device = torch_directml.device(torch_directml.default_device())
+
+
+f = open("%s/extract_f0_feature.log" % exp_dir, "a", encoding="utf8")
+
+
+def printt(strr):
+ print(strr)
+ f.write("%s\n" % strr)
+ f.flush()
+
+
+model_path = str(HUBERT_MODEL_PATH)
+wavPath = "%s/1_16k_wavs" % exp_dir
+outPath = (
+ "%s/3_feature256" % exp_dir if version == "v1" else "%s/3_feature768" % exp_dir
+)
+os.makedirs(outPath, exist_ok=True)
+
+
+# wave must be 16k, hop_size=320
+def readwave(wav_path, normalize=False):
+ wav, sr = sf.read(wav_path)
+ assert sr == 16000
+ feats = torch.from_numpy(wav).float()
+ if feats.dim() == 2: # double channels
+ feats = feats.mean(-1)
+ assert feats.dim() == 1, feats.dim()
+ if normalize:
+ with torch.no_grad():
+ feats = F.layer_norm(feats, feats.shape)
+ feats = feats.view(1, -1)
+ return feats
+
+
+assigned_files = [
+ file
+ for file in sorted(os.listdir(wavPath))[i_part::n_part]
+ if file.endswith(".wav")
+]
+todo = [
+ file
+ for file in assigned_files
+ if not os.path.exists(
+ "%s/%s.npy" % (outPath, os.path.splitext(file)[0])
+ )
+]
+skipped = len(assigned_files) - len(todo)
+if len(todo) == 0:
+ printt(i18n("[HuBERT特征] 无待处理音频,已全部跳过:%s") % skipped)
+ raise SystemExit(0)
+
+
+printt(i18n("[HuBERT特征] 正在加载模型:%s") % model_path)
+if os.access(model_path, os.F_OK) == False:
+ printt(
+ i18n("[HuBERT特征][失败] 模型不存在:%s")
+ % model_path
+ )
+ raise SystemExit(1)
+model = load_hubert_model(device, is_half and device != "cpu")
+normalize_audio = hubert_audio_requires_normalization()
+printt(
+ i18n("[HuBERT特征] 设备:%s | 待处理:%s | 已跳过:%s")
+ % (device, len(todo), skipped)
+)
+
+success = 0
+failed = 0
+for idx, file in enumerate(todo):
+ try:
+ wav_path = "%s/%s" % (wavPath, file)
+ out_path = "%s/%s.npy" % (outPath, os.path.splitext(file)[0])
+ if os.path.exists(out_path):
+ skipped += 1
+ continue
+
+ feats = readwave(wav_path, normalize=normalize_audio)
+ padding_mask = torch.BoolTensor(feats.shape).fill_(False)
+ source = (
+ feats.half().to(device)
+ if is_half and device != "cpu"
+ else feats.to(device)
+ )
+ with torch.no_grad():
+ feats = extract_hubert_features(
+ model,
+ source,
+ version,
+ padding_mask=padding_mask.to(device),
+ )
+
+ feats = feats.squeeze(0).float().cpu().numpy()
+ if np.isnan(feats).sum() == 0:
+ np.save(out_path, feats, allow_pickle=False)
+ success += 1
+ if should_report(idx, len(todo), max(1, (12 + n_part - 1) // n_part)):
+ printt(
+ i18n("[HuBERT特征] 进度:%s/%s | 成功:%s | 失败:%s | %s | %s")
+ % (idx + 1, len(todo), success, failed, file, feats.shape)
+ )
+ else:
+ failed += 1
+ printt(i18n("[HuBERT特征][失败] %s 包含NaN") % file)
+ except Exception:
+ failed += 1
+ printt(i18n("[HuBERT特征][失败] %s\n%s") % (file, traceback.format_exc()))
+printt(
+ i18n("[HuBERT特征] 完成 | 成功:%s | 跳过:%s | 失败:%s")
+ % (success, skipped, failed)
+)
diff --git a/train/dataset/slicer2.py b/train/dataset/slicer2.py
new file mode 100644
index 0000000..42d6618
--- /dev/null
+++ b/train/dataset/slicer2.py
@@ -0,0 +1,260 @@
+import numpy as np
+
+
+# This function is obtained from librosa.
+def get_rms(
+ y,
+ frame_length=2048,
+ hop_length=512,
+ pad_mode="constant",
+):
+ padding = (int(frame_length // 2), int(frame_length // 2))
+ y = np.pad(y, padding, mode=pad_mode)
+
+ axis = -1
+ # put our new within-frame axis at the end for now
+ out_strides = y.strides + tuple([y.strides[axis]])
+ # Reduce the shape on the framing axis
+ x_shape_trimmed = list(y.shape)
+ x_shape_trimmed[axis] -= frame_length - 1
+ out_shape = tuple(x_shape_trimmed) + tuple([frame_length])
+ xw = np.lib.stride_tricks.as_strided(y, shape=out_shape, strides=out_strides)
+ if axis < 0:
+ target_axis = axis - 1
+ else:
+ target_axis = axis + 1
+ xw = np.moveaxis(xw, -1, target_axis)
+ # Downsample along the target axis
+ slices = [slice(None)] * xw.ndim
+ slices[axis] = slice(0, None, hop_length)
+ x = xw[tuple(slices)]
+
+ # Calculate power
+ power = np.mean(np.abs(x) ** 2, axis=-2, keepdims=True)
+
+ return np.sqrt(power)
+
+
+class Slicer:
+ def __init__(
+ self,
+ sr,
+ threshold = -40.0,
+ min_length = 5000,
+ min_interval = 300,
+ hop_size = 20,
+ max_sil_kept = 5000,
+ ):
+ if not min_length >= min_interval >= hop_size:
+ raise ValueError(
+ "The following condition must be satisfied: min_length >= min_interval >= hop_size"
+ )
+ if not max_sil_kept >= hop_size:
+ raise ValueError(
+ "The following condition must be satisfied: max_sil_kept >= hop_size"
+ )
+ min_interval = sr * min_interval / 1000
+ self.threshold = 10 ** (threshold / 20.0)
+ self.hop_size = round(sr * hop_size / 1000)
+ self.win_size = min(round(min_interval), 4 * self.hop_size)
+ self.min_length = round(sr * min_length / 1000 / self.hop_size)
+ self.min_interval = round(min_interval / self.hop_size)
+ self.max_sil_kept = round(sr * max_sil_kept / 1000 / self.hop_size)
+
+ def _apply_slice(self, waveform, begin, end):
+ if len(waveform.shape) > 1:
+ return waveform[
+ :, begin * self.hop_size : min(waveform.shape[1], end * self.hop_size)
+ ]
+ else:
+ return waveform[
+ begin * self.hop_size : min(waveform.shape[0], end * self.hop_size)
+ ]
+
+ # @timeit
+ def slice(self, waveform):
+ if len(waveform.shape) > 1:
+ samples = waveform.mean(axis=0)
+ else:
+ samples = waveform
+ if samples.shape[0] <= self.min_length:
+ return [waveform]
+ rms_list = get_rms(
+ y=samples, frame_length=self.win_size, hop_length=self.hop_size
+ ).squeeze(0)
+ sil_tags = []
+ silence_start = None
+ clip_start = 0
+ for i, rms in enumerate(rms_list):
+ # Keep looping while frame is silent.
+ if rms < self.threshold:
+ # Record start of silent frames.
+ if silence_start is None:
+ silence_start = i
+ continue
+ # Keep looping while frame is not silent and silence start has not been recorded.
+ if silence_start is None:
+ continue
+ # Clear recorded silence start if interval is not enough or clip is too short
+ is_leading_silence = silence_start == 0 and i > self.max_sil_kept
+ need_slice_middle = (
+ i - silence_start >= self.min_interval
+ and i - clip_start >= self.min_length
+ )
+ if not is_leading_silence and not need_slice_middle:
+ silence_start = None
+ continue
+ # Need slicing. Record the range of silent frames to be removed.
+ if i - silence_start <= self.max_sil_kept:
+ pos = rms_list[silence_start : i + 1].argmin() + silence_start
+ if silence_start == 0:
+ sil_tags.append((0, pos))
+ else:
+ sil_tags.append((pos, pos))
+ clip_start = pos
+ elif i - silence_start <= self.max_sil_kept * 2:
+ pos = rms_list[
+ i - self.max_sil_kept : silence_start + self.max_sil_kept + 1
+ ].argmin()
+ pos += i - self.max_sil_kept
+ pos_l = (
+ rms_list[
+ silence_start : silence_start + self.max_sil_kept + 1
+ ].argmin()
+ + silence_start
+ )
+ pos_r = (
+ rms_list[i - self.max_sil_kept : i + 1].argmin()
+ + i
+ - self.max_sil_kept
+ )
+ if silence_start == 0:
+ sil_tags.append((0, pos_r))
+ clip_start = pos_r
+ else:
+ sil_tags.append((min(pos_l, pos), max(pos_r, pos)))
+ clip_start = max(pos_r, pos)
+ else:
+ pos_l = (
+ rms_list[
+ silence_start : silence_start + self.max_sil_kept + 1
+ ].argmin()
+ + silence_start
+ )
+ pos_r = (
+ rms_list[i - self.max_sil_kept : i + 1].argmin()
+ + i
+ - self.max_sil_kept
+ )
+ if silence_start == 0:
+ sil_tags.append((0, pos_r))
+ else:
+ sil_tags.append((pos_l, pos_r))
+ clip_start = pos_r
+ silence_start = None
+ # Deal with trailing silence.
+ total_frames = rms_list.shape[0]
+ if (
+ silence_start is not None
+ and total_frames - silence_start >= self.min_interval
+ ):
+ silence_end = min(total_frames, silence_start + self.max_sil_kept)
+ pos = rms_list[silence_start : silence_end + 1].argmin() + silence_start
+ sil_tags.append((pos, total_frames + 1))
+ # Apply and return slices.
+ if len(sil_tags) == 0:
+ return [waveform]
+ else:
+ chunks = []
+ if sil_tags[0][0] > 0:
+ chunks.append(self._apply_slice(waveform, 0, sil_tags[0][0]))
+ for i in range(len(sil_tags) - 1):
+ chunks.append(
+ self._apply_slice(waveform, sil_tags[i][1], sil_tags[i + 1][0])
+ )
+ if sil_tags[-1][1] < total_frames:
+ chunks.append(
+ self._apply_slice(waveform, sil_tags[-1][1], total_frames)
+ )
+ return chunks
+
+
+def main():
+ import os.path
+ from argparse import ArgumentParser
+
+ import librosa
+ import soundfile
+
+ parser = ArgumentParser()
+ parser.add_argument("audio", type=str, help="The audio to be sliced")
+ parser.add_argument(
+ "--out", type=str, help="Output directory of the sliced audio clips"
+ )
+ parser.add_argument(
+ "--db_thresh",
+ type=float,
+ required=False,
+ default=-40,
+ help="The dB threshold for silence detection",
+ )
+ parser.add_argument(
+ "--min_length",
+ type=int,
+ required=False,
+ default=5000,
+ help="The minimum milliseconds required for each sliced audio clip",
+ )
+ parser.add_argument(
+ "--min_interval",
+ type=int,
+ required=False,
+ default=300,
+ help="The minimum milliseconds for a silence part to be sliced",
+ )
+ parser.add_argument(
+ "--hop_size",
+ type=int,
+ required=False,
+ default=10,
+ help="Frame length in milliseconds",
+ )
+ parser.add_argument(
+ "--max_sil_kept",
+ type=int,
+ required=False,
+ default=500,
+ help="The maximum silence length kept around the sliced clip, presented in milliseconds",
+ )
+ args = parser.parse_args()
+ out = args.out
+ if out is None:
+ out = os.path.dirname(os.path.abspath(args.audio))
+ audio, sr = librosa.load(args.audio, sr=None, mono=False)
+ slicer = Slicer(
+ sr=sr,
+ threshold=args.db_thresh,
+ min_length=args.min_length,
+ min_interval=args.min_interval,
+ hop_size=args.hop_size,
+ max_sil_kept=args.max_sil_kept,
+ )
+ chunks = slicer.slice(audio)
+ if not os.path.exists(out):
+ os.makedirs(out)
+ for i, chunk in enumerate(chunks):
+ if len(chunk.shape) > 1:
+ chunk = chunk.T
+ soundfile.write(
+ os.path.join(
+ out,
+ f"%s_%d.wav"
+ % (os.path.basename(args.audio).rsplit(".", maxsplit=1)[0], i),
+ ),
+ chunk,
+ sr,
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/train/losses.py b/train/losses.py
new file mode 100644
index 0000000..aa7bd81
--- /dev/null
+++ b/train/losses.py
@@ -0,0 +1,58 @@
+import torch
+
+
+def feature_loss(fmap_r, fmap_g):
+ loss = 0
+ for dr, dg in zip(fmap_r, fmap_g):
+ for rl, gl in zip(dr, dg):
+ rl = rl.float().detach()
+ gl = gl.float()
+ loss += torch.mean(torch.abs(rl - gl))
+
+ return loss * 2
+
+
+def discriminator_loss(disc_real_outputs, disc_generated_outputs):
+ loss = 0
+ r_losses = []
+ g_losses = []
+ for dr, dg in zip(disc_real_outputs, disc_generated_outputs):
+ dr = dr.float()
+ dg = dg.float()
+ r_loss = torch.mean((1 - dr) ** 2)
+ g_loss = torch.mean(dg**2)
+ loss += r_loss + g_loss
+ r_losses.append(r_loss.item())
+ g_losses.append(g_loss.item())
+
+ return loss, r_losses, g_losses
+
+
+def generator_loss(disc_outputs):
+ loss = 0
+ gen_losses = []
+ for dg in disc_outputs:
+ dg = dg.float()
+ l = torch.mean((1 - dg) ** 2)
+ gen_losses.append(l)
+ loss += l
+
+ return loss, gen_losses
+
+
+def kl_loss(z_p, logs_q, m_p, logs_p, z_mask):
+ """
+ z_p, logs_q: [b, h, t_t]
+ m_p, logs_p: [b, h, t_t]
+ """
+ z_p = z_p.float()
+ logs_q = logs_q.float()
+ m_p = m_p.float()
+ logs_p = logs_p.float()
+ z_mask = z_mask.float()
+
+ kl = logs_p - logs_q - 0.5
+ kl += 0.5 * ((z_p - m_p) ** 2) * torch.exp(-2.0 * logs_p)
+ kl = torch.sum(kl * z_mask)
+ l = kl / torch.sum(z_mask)
+ return l
diff --git a/train/mel_processing.py b/train/mel_processing.py
new file mode 100644
index 0000000..e8b438f
--- /dev/null
+++ b/train/mel_processing.py
@@ -0,0 +1,127 @@
+import torch
+import torch.utils.data
+from librosa.filters import mel as librosa_mel_fn
+import logging
+
+logger = logging.getLogger(__name__)
+
+MAX_WAV_VALUE = 32768.0
+
+
+def dynamic_range_compression_torch(x, C=1, clip_val=2e-6):
+ """
+ PARAMS
+ ------
+ C: compression factor
+ """
+ return torch.log(torch.clamp(x, min=clip_val) * C)
+
+
+def dynamic_range_decompression_torch(x, C=1):
+ """
+ PARAMS
+ ------
+ C: compression factor used to compress
+ """
+ return torch.exp(x) / C
+
+
+def spectral_normalize_torch(magnitudes):
+ return dynamic_range_compression_torch(magnitudes)
+
+
+def spectral_de_normalize_torch(magnitudes):
+ return dynamic_range_decompression_torch(magnitudes)
+
+
+# Reusable banks
+mel_basis = {}
+hann_window = {}
+
+
+def spectrogram_torch(y, n_fft, sampling_rate, hop_size, win_size, center=False):
+ """Convert waveform into Linear-frequency Linear-amplitude spectrogram.
+
+ Args:
+ y :: (B, T) - Audio waveforms
+ n_fft
+ sampling_rate
+ hop_size
+ win_size
+ center
+ Returns:
+ :: (B, Freq, Frame) - Linear-frequency Linear-amplitude spectrogram
+ """
+
+ # Window - Cache if needed
+ global hann_window
+ dtype_device = str(y.dtype) + "_" + str(y.device)
+ wnsize_dtype_device = str(win_size) + "_" + dtype_device
+ if wnsize_dtype_device not in hann_window:
+ hann_window[wnsize_dtype_device] = torch.hann_window(win_size).to(
+ dtype=y.dtype, device=y.device
+ )
+
+ # Padding
+ y = torch.nn.functional.pad(
+ y.unsqueeze(1),
+ (int((n_fft - hop_size) / 2), int((n_fft - hop_size) / 2)),
+ mode="reflect",
+ )
+ y = y.squeeze(1)
+
+ # Complex Spectrogram :: (B, T) -> (B, Freq, Frame, RealComplex=2)
+ spec = torch.stft(
+ y,
+ n_fft,
+ hop_length=hop_size,
+ win_length=win_size,
+ window=hann_window[wnsize_dtype_device],
+ center=center,
+ pad_mode="reflect",
+ normalized=False,
+ onesided=True,
+ return_complex=True,
+ )
+
+ # Linear-frequency Linear-amplitude spectrogram :: (B, Freq, Frame, RealComplex=2) -> (B, Freq, Frame)
+ spec = torch.sqrt(spec.real.pow(2) + spec.imag.pow(2) + 2e-7)
+ return spec
+
+
+def spec_to_mel_torch(spec, n_fft, num_mels, sampling_rate, fmin, fmax):
+ # MelBasis - Cache if needed
+ global mel_basis
+ dtype_device = str(spec.dtype) + "_" + str(spec.device)
+ fmax_dtype_device = str(fmax) + "_" + dtype_device
+ if fmax_dtype_device not in mel_basis:
+ mel = librosa_mel_fn(
+ sr=sampling_rate, n_fft=n_fft, n_mels=num_mels, fmin=fmin, fmax=fmax
+ )
+ mel_basis[fmax_dtype_device] = torch.from_numpy(mel).to(
+ dtype=spec.dtype, device=spec.device
+ )
+
+ # Mel-frequency Log-amplitude spectrogram :: (B, Freq=num_mels, Frame)
+ melspec = torch.matmul(mel_basis[fmax_dtype_device], spec)
+ melspec = spectral_normalize_torch(melspec)
+ return melspec
+
+
+def mel_spectrogram_torch(
+ y, n_fft, num_mels, sampling_rate, hop_size, win_size, fmin, fmax, center=False
+):
+ """Convert waveform into Mel-frequency Log-amplitude spectrogram.
+
+ Args:
+ y :: (B, T) - Waveforms
+ Returns:
+ melspec :: (B, Freq, Frame) - Mel-frequency Log-amplitude spectrogram
+ """
+ # Linear-frequency Linear-amplitude spectrogram :: (B, T) -> (B, Freq, Frame)
+ spec = spectrogram_torch(y, n_fft, sampling_rate, hop_size, win_size, center)
+
+ # Mel-frequency Log-amplitude spectrogram :: (B, Freq, Frame) -> (B, Freq=num_mels, Frame)
+ melspec = spec_to_mel_torch(spec, n_fft, num_mels, sampling_rate, fmin, fmax)
+
+ return melspec
diff --git a/train/preprocess.py b/train/preprocess.py
new file mode 100644
index 0000000..4cc3605
--- /dev/null
+++ b/train/preprocess.py
@@ -0,0 +1,169 @@
+import multiprocessing
+import os
+import sys
+
+from scipy import signal
+
+inp_root = sys.argv[1]
+sr = int(sys.argv[2])
+n_p = int(sys.argv[3])
+exp_dir = sys.argv[4]
+noparallel = sys.argv[5] == "True"
+per = float(sys.argv[6])
+import os
+import traceback
+
+import librosa
+import numpy as np
+from scipy.io import wavfile
+
+from infer.audio import load_audio
+from train.dataset.slicer2 import Slicer
+from i18n.i18n import I18nAuto
+from tools.progress import should_report
+
+i18n = I18nAuto()
+
+f = open("%s/preprocess.log" % exp_dir, "a", encoding="utf8")
+
+
+def println(strr):
+ print(strr)
+ f.write("%s\n" % strr)
+ f.flush()
+
+
+class PreProcess:
+ def __init__(self, sr, exp_dir, per=3.7):
+ self.slicer = Slicer(
+ sr=sr,
+ threshold=-42,
+ min_length=1500,
+ min_interval=400,
+ hop_size=15,
+ max_sil_kept=500,
+ )
+ self.sr = sr
+ self.bh, self.ah = signal.butter(N=5, Wn=48, btype="high", fs=self.sr)
+ self.per = per
+ self.overlap = 0.3
+ self.tail = self.per + self.overlap
+ self.max = 0.9
+ self.alpha = 0.75
+ self.exp_dir = exp_dir
+ self.gt_wavs_dir = "%s/0_gt_wavs" % exp_dir
+ self.wavs16k_dir = "%s/1_16k_wavs" % exp_dir
+ os.makedirs(self.exp_dir, exist_ok=True)
+ os.makedirs(self.gt_wavs_dir, exist_ok=True)
+ os.makedirs(self.wavs16k_dir, exist_ok=True)
+
+ def norm_write(self, tmp_audio, idx0, idx1):
+ tmp_max = np.abs(tmp_audio).max()
+ if not np.isfinite(tmp_max) or tmp_max <= 0 or tmp_max > 2.5:
+ println(
+ i18n("[数据切分][跳过] 无效或异常音频片段:%s_%s | 峰值:%s")
+ % (idx0, idx1, tmp_max)
+ )
+ return False
+ tmp_audio = (tmp_audio / tmp_max * (self.max * self.alpha)) + (
+ 1 - self.alpha
+ ) * tmp_audio
+ wavfile.write(
+ "%s/%s_%s.wav" % (self.gt_wavs_dir, idx0, idx1),
+ self.sr,
+ tmp_audio.astype(np.float32),
+ )
+ tmp_audio = librosa.resample(
+ tmp_audio, orig_sr=self.sr, target_sr=16000
+ ) # , res_type="soxr_vhq"
+ wavfile.write(
+ "%s/%s_%s.wav" % (self.wavs16k_dir, idx0, idx1),
+ 16000,
+ tmp_audio.astype(np.float32),
+ )
+ return True
+
+ def pipeline(self, path, idx0, total):
+ try:
+ audio = load_audio(path, self.sr)
+ # zero phased digital filter cause pre-ringing noise...
+ # audio = signal.filtfilt(self.bh, self.ah, audio)
+ audio = signal.lfilter(self.bh, self.ah, audio)
+
+ idx1 = 0
+ for audio in self.slicer.slice(audio):
+ i = 0
+ while 1:
+ start = int(self.sr * (self.per - self.overlap) * i)
+ i += 1
+ if len(audio[start:]) > self.tail * self.sr:
+ tmp_audio = audio[start : start + int(self.per * self.sr)]
+ self.norm_write(tmp_audio, idx0, idx1)
+ idx1 += 1
+ else:
+ tmp_audio = audio[start:]
+ idx1 += 1
+ break
+ self.norm_write(tmp_audio, idx0, idx1)
+ if should_report(idx0, total):
+ println(
+ i18n("[数据切分] 进度:%s/%s | %s")
+ % (idx0 + 1, total, os.path.basename(path))
+ )
+ return True
+ except Exception:
+ println(
+ i18n("[数据切分][失败] %s\n%s")
+ % (path, traceback.format_exc())
+ )
+ return False
+
+ def pipeline_mp(self, infos):
+ success = 0
+ failed = 0
+ for path, idx0, total in infos:
+ if self.pipeline(path, idx0, total):
+ success += 1
+ else:
+ failed += 1
+ if infos:
+ println(
+ i18n("[数据切分] 子任务完成 | 成功:%s | 失败:%s")
+ % (success, failed)
+ )
+
+ def pipeline_mp_inp_dir(self, inp_root, n_p):
+ try:
+ names = sorted(os.listdir(inp_root))
+ total = len(names)
+ infos = [
+ ("%s/%s" % (inp_root, name), idx, total)
+ for idx, name in enumerate(names)
+ ]
+ println(i18n("[数据切分] 待处理:%s | 进程数:%s") % (total, n_p))
+ if noparallel:
+ for i in range(n_p):
+ self.pipeline_mp(infos[i::n_p])
+ else:
+ ps = []
+ for i in range(n_p):
+ p = multiprocessing.Process(
+ target=self.pipeline_mp, args=(infos[i::n_p],)
+ )
+ ps.append(p)
+ p.start()
+ for i in range(n_p):
+ ps[i].join()
+ except Exception:
+ println(i18n("[数据切分][失败] %s") % traceback.format_exc())
+
+
+def preprocess_trainset(inp_root, sr, n_p, exp_dir, per):
+ pp = PreProcess(sr, exp_dir, per)
+ println(i18n("[数据切分] 开始"))
+ pp.pipeline_mp_inp_dir(inp_root, n_p)
+ println(i18n("[数据切分] 完成"))
+
+
+if __name__ == "__main__":
+ preprocess_trainset(inp_root, sr, n_p, exp_dir, per)
diff --git a/train/process_ckpt.py b/train/process_ckpt.py
new file mode 100644
index 0000000..3fa3698
--- /dev/null
+++ b/train/process_ckpt.py
@@ -0,0 +1,261 @@
+import os
+import sys
+import traceback
+from collections import OrderedDict
+
+import torch
+
+from i18n.i18n import I18nAuto
+
+i18n = I18nAuto()
+
+
+def savee(ckpt, sr, if_f0, name, epoch, version, hps):
+ try:
+ opt = OrderedDict()
+ opt["weight"] = {}
+ for key in ckpt.keys():
+ if "enc_q" in key:
+ continue
+ opt["weight"][key] = ckpt[key].half()
+ opt["config"] = [
+ hps.data.filter_length // 2 + 1,
+ 32,
+ hps.model.inter_channels,
+ hps.model.hidden_channels,
+ hps.model.filter_channels,
+ hps.model.n_heads,
+ hps.model.n_layers,
+ hps.model.kernel_size,
+ hps.model.p_dropout,
+ hps.model.resblock,
+ hps.model.resblock_kernel_sizes,
+ hps.model.resblock_dilation_sizes,
+ hps.model.upsample_rates,
+ hps.model.upsample_initial_channel,
+ hps.model.upsample_kernel_sizes,
+ hps.model.spk_embed_dim,
+ hps.model.gin_channels,
+ hps.data.sampling_rate,
+ ]
+ opt["info"] = "%sepoch" % epoch
+ opt["sr"] = sr
+ opt["f0"] = if_f0
+ opt["version"] = version
+ torch.save(opt, "assets/weights/%s.pth" % name)
+ return i18n("成功")
+ except:
+ return traceback.format_exc()
+
+
+def show_info(path):
+ try:
+ a = torch.load(path, map_location="cpu")
+ return i18n("模型信息:%s\n采样率:%s\n是否使用音高引导:%s\n版本:%s") % (
+ a.get("info", "None"),
+ a.get("sr", "None"),
+ a.get("f0", "None"),
+ a.get("version", "None"),
+ )
+ except:
+ return traceback.format_exc()
+
+
+def extract_small_model(path, name, sr, if_f0, info, version):
+ try:
+ ckpt = torch.load(path, map_location="cpu")
+ if "model" in ckpt:
+ ckpt = ckpt["model"]
+ opt = OrderedDict()
+ opt["weight"] = {}
+ for key in ckpt.keys():
+ if "enc_q" in key:
+ continue
+ opt["weight"][key] = ckpt[key].half()
+ if sr == "40k":
+ opt["config"] = [
+ 1025,
+ 32,
+ 192,
+ 192,
+ 768,
+ 2,
+ 6,
+ 3,
+ 0,
+ "1",
+ [3, 7, 11],
+ [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
+ [10, 10, 2, 2],
+ 512,
+ [16, 16, 4, 4],
+ 109,
+ 256,
+ 40000,
+ ]
+ elif sr == "48k":
+ if version == "v1":
+ opt["config"] = [
+ 1025,
+ 32,
+ 192,
+ 192,
+ 768,
+ 2,
+ 6,
+ 3,
+ 0,
+ "1",
+ [3, 7, 11],
+ [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
+ [10, 6, 2, 2, 2],
+ 512,
+ [16, 16, 4, 4, 4],
+ 109,
+ 256,
+ 48000,
+ ]
+ else:
+ opt["config"] = [
+ 1025,
+ 32,
+ 192,
+ 192,
+ 768,
+ 2,
+ 6,
+ 3,
+ 0,
+ "1",
+ [3, 7, 11],
+ [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
+ [12, 10, 2, 2],
+ 512,
+ [24, 20, 4, 4],
+ 109,
+ 256,
+ 48000,
+ ]
+ elif sr == "32k":
+ if version == "v1":
+ opt["config"] = [
+ 513,
+ 32,
+ 192,
+ 192,
+ 768,
+ 2,
+ 6,
+ 3,
+ 0,
+ "1",
+ [3, 7, 11],
+ [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
+ [10, 4, 2, 2, 2],
+ 512,
+ [16, 16, 4, 4, 4],
+ 109,
+ 256,
+ 32000,
+ ]
+ else:
+ opt["config"] = [
+ 513,
+ 32,
+ 192,
+ 192,
+ 768,
+ 2,
+ 6,
+ 3,
+ 0,
+ "1",
+ [3, 7, 11],
+ [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
+ [10, 8, 2, 2],
+ 512,
+ [20, 16, 4, 4],
+ 109,
+ 256,
+ 32000,
+ ]
+ if info == "":
+ info = i18n("从训练检查点提取的模型")
+ opt["info"] = info
+ opt["version"] = version
+ opt["sr"] = sr
+ opt["f0"] = int(if_f0)
+ torch.save(opt, "assets/weights/%s.pth" % name)
+ return i18n("成功")
+ except:
+ return traceback.format_exc()
+
+
+def change_info(path, info, name):
+ try:
+ ckpt = torch.load(path, map_location="cpu")
+ ckpt["info"] = info
+ if name == "":
+ name = os.path.basename(path)
+ torch.save(ckpt, "assets/weights/%s" % name)
+ return i18n("成功")
+ except:
+ return traceback.format_exc()
+
+
+def merge(path1, path2, alpha1, sr, f0, info, name, version):
+ try:
+
+ def extract(ckpt):
+ a = ckpt["model"]
+ opt = OrderedDict()
+ opt["weight"] = {}
+ for key in a.keys():
+ if "enc_q" in key:
+ continue
+ opt["weight"][key] = a[key]
+ return opt
+
+ ckpt1 = torch.load(path1, map_location="cpu")
+ ckpt2 = torch.load(path2, map_location="cpu")
+ cfg = ckpt1["config"]
+ if "model" in ckpt1:
+ ckpt1 = extract(ckpt1)
+ else:
+ ckpt1 = ckpt1["weight"]
+ if "model" in ckpt2:
+ ckpt2 = extract(ckpt2)
+ else:
+ ckpt2 = ckpt2["weight"]
+ if sorted(list(ckpt1.keys())) != sorted(list(ckpt2.keys())):
+ return i18n("模型融合失败:两个模型的结构不一致")
+ opt = OrderedDict()
+ opt["weight"] = {}
+ for key in ckpt1.keys():
+ # try:
+ if key == "emb_g.weight" and ckpt1[key].shape != ckpt2[key].shape:
+ min_shape0 = min(ckpt1[key].shape[0], ckpt2[key].shape[0])
+ opt["weight"][key] = (
+ alpha1 * (ckpt1[key][:min_shape0].float())
+ + (1 - alpha1) * (ckpt2[key][:min_shape0].float())
+ ).half()
+ else:
+ opt["weight"][key] = (
+ alpha1 * (ckpt1[key].float()) + (1 - alpha1) * (ckpt2[key].float())
+ ).half()
+ # except:
+ # pdb.set_trace()
+ opt["config"] = cfg
+ """
+ if(sr=="40k"):opt["config"] = [1025, 32, 192, 192, 768, 2, 6, 3, 0, "1", [3, 7, 11], [[1, 3, 5], [1, 3, 5], [1, 3, 5]], [10, 10, 2, 2], 512, [16, 16, 4, 4,4], 109, 256, 40000]
+ elif(sr=="48k"):opt["config"] = [1025, 32, 192, 192, 768, 2, 6, 3, 0, "1", [3, 7, 11], [[1, 3, 5], [1, 3, 5], [1, 3, 5]], [10,6,2,2,2], 512, [16, 16, 4, 4], 109, 256, 48000]
+ elif(sr=="32k"):opt["config"] = [513, 32, 192, 192, 768, 2, 6, 3, 0, "1", [3, 7, 11], [[1, 3, 5], [1, 3, 5], [1, 3, 5]], [10, 4, 2, 2, 2], 512, [16, 16, 4, 4,4], 109, 256, 32000]
+ """
+ opt["sr"] = sr
+ opt["f0"] = 1 if f0 == i18n("是") else 0
+ opt["version"] = version
+ opt["info"] = info
+ torch.save(opt, "assets/weights/%s.pth" % name)
+ return i18n("成功")
+ except:
+ return traceback.format_exc()
diff --git a/train/train.py b/train/train.py
new file mode 100644
index 0000000..38139b2
--- /dev/null
+++ b/train/train.py
@@ -0,0 +1,657 @@
+import os
+import logging
+import warnings
+
+warnings.filterwarnings(
+ "ignore",
+ message="`torch.nn.utils.weight_norm` is deprecated.*",
+ category=FutureWarning,
+)
+warnings.filterwarnings(
+ "ignore",
+ message="`torch.cuda.amp.GradScaler.*is deprecated.*",
+ category=FutureWarning,
+)
+warnings.filterwarnings(
+ "ignore",
+ message="`torch.cuda.amp.autocast.*is deprecated.*",
+ category=FutureWarning,
+)
+warnings.filterwarnings(
+ "ignore",
+ message="Grad strides do not match bucket view strides.*",
+ category=UserWarning,
+)
+logging.getLogger("matplotlib").setLevel(logging.WARNING)
+
+logger = logging.getLogger(__name__)
+
+import datetime
+
+from train import utils
+
+hps = utils.get_hparams()
+os.environ["CUDA_VISIBLE_DEVICES"] = hps.gpus.replace("-", ",")
+n_gpus = len(hps.gpus.split("-"))
+from random import randint, shuffle
+
+import torch
+
+from configs.config import get_training_dtype
+from i18n.i18n import I18nAuto
+
+i18n = I18nAuto()
+
+training_dtype = get_training_dtype()
+training_is_half = training_dtype == torch.float16
+
+from torch.cuda.amp import GradScaler, autocast
+
+torch.backends.cudnn.deterministic = False
+torch.backends.cudnn.benchmark = False
+from time import sleep
+from time import time as ttime
+
+import torch.distributed as dist
+import torch.multiprocessing as mp
+from torch.nn import functional as F
+from torch.nn.parallel import DistributedDataParallel as DDP
+from torch.utils.data import DataLoader
+from torch.utils.tensorboard import SummaryWriter
+
+from infer.module import commons
+from train.data_utils import (
+ DistributedBucketSampler,
+ TextAudioCollate,
+ TextAudioCollateMultiNSFsid,
+ TextAudioLoader,
+ TextAudioLoaderMultiNSFsid,
+)
+
+if hps.version == "v1":
+ from infer.module.models import MultiPeriodDiscriminator
+ from infer.module.models import SynthesizerTrnMs256NSFsid as RVC_Model_f0
+ from infer.module.models import (
+ SynthesizerTrnMs256NSFsid_nono as RVC_Model_nof0,
+ )
+else:
+ from infer.module.models import (
+ SynthesizerTrnMs768NSFsid as RVC_Model_f0,
+ SynthesizerTrnMs768NSFsid_nono as RVC_Model_nof0,
+ MultiPeriodDiscriminatorV2 as MultiPeriodDiscriminator,
+ )
+
+from train.losses import (
+ discriminator_loss,
+ feature_loss,
+ generator_loss,
+ kl_loss,
+)
+from train.mel_processing import mel_spectrogram_torch, spec_to_mel_torch
+from train.process_ckpt import savee
+
+global_step = 0
+
+
+class EpochRecorder:
+ def __init__(self):
+ self.last_time = ttime()
+
+ def record(self):
+ now_time = ttime()
+ elapsed_time = now_time - self.last_time
+ self.last_time = now_time
+ elapsed_time_str = str(datetime.timedelta(seconds=elapsed_time))
+ current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ return f"[{current_time}] | ({elapsed_time_str})"
+
+
+def main():
+ n_gpus = torch.cuda.device_count()
+ single_cuda = torch.cuda.is_available() and n_gpus == 1
+
+ if n_gpus < 1:
+ # patch to unblock people without gpus. there is probably a better way.
+ print(i18n("未检测到可用显卡,将使用CPU训练,耗时可能较长"))
+ n_gpus = 1
+ logger = utils.get_logger(hps.model_dir)
+ logger.info(i18n("训练设备规则选择的精度:%s"), training_dtype)
+ if single_cuda:
+ run(0, 1, hps, logger, False)
+ return
+ os.environ["MASTER_ADDR"] = "localhost"
+ os.environ["MASTER_PORT"] = str(randint(20000, 55555))
+ children = []
+ for i in range(n_gpus):
+ subproc = mp.Process(
+ target=run,
+ args=(i, n_gpus, hps, logger, True),
+ )
+ children.append(subproc)
+ subproc.start()
+
+ for i in range(n_gpus):
+ children[i].join()
+
+
+def run(rank, n_gpus, hps, logger, use_ddp):
+ global global_step
+ if rank == 0:
+ # logger = utils.get_logger(hps.model_dir)
+ logger.info(hps)
+ # utils.check_git_hash(hps.model_dir)
+ writer = SummaryWriter(log_dir=hps.model_dir)
+ writer_eval = SummaryWriter(log_dir=os.path.join(hps.model_dir, "eval"))
+
+ if use_ddp:
+ dist.init_process_group(
+ backend="gloo", init_method="env://", world_size=n_gpus, rank=rank
+ )
+ torch.manual_seed(hps.train.seed)
+ if torch.cuda.is_available():
+ torch.cuda.set_device(rank)
+
+ if hps.if_f0 == 1:
+ train_dataset = TextAudioLoaderMultiNSFsid(hps.data.training_files, hps.data)
+ else:
+ train_dataset = TextAudioLoader(hps.data.training_files, hps.data)
+ train_sampler = DistributedBucketSampler(
+ train_dataset,
+ hps.train.batch_size * n_gpus,
+ # [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200,1400], # 16s
+ [100, 200, 300, 400, 500, 600, 700, 800, 900], # 16s
+ num_replicas=n_gpus,
+ rank=rank,
+ shuffle=True,
+ )
+ # It is possible that dataloader's workers are out of shared memory. Please try to raise your shared memory limit.
+ # num_workers=8 -> num_workers=4
+ if hps.if_f0 == 1:
+ collate_fn = TextAudioCollateMultiNSFsid()
+ else:
+ collate_fn = TextAudioCollate()
+ train_loader = DataLoader(
+ train_dataset,
+ num_workers=4,
+ shuffle=False,
+ pin_memory=True,
+ collate_fn=collate_fn,
+ batch_sampler=train_sampler,
+ persistent_workers=True,
+ prefetch_factor=8,
+ )
+ if hps.if_f0 == 1:
+ net_g = RVC_Model_f0(
+ hps.data.filter_length // 2 + 1,
+ hps.train.segment_size // hps.data.hop_length,
+ **hps.model,
+ is_half=training_is_half,
+ sr=hps.sample_rate,
+ )
+ else:
+ net_g = RVC_Model_nof0(
+ hps.data.filter_length // 2 + 1,
+ hps.train.segment_size // hps.data.hop_length,
+ **hps.model,
+ is_half=training_is_half,
+ )
+ if torch.cuda.is_available():
+ net_g = net_g.cuda(rank)
+ net_d = MultiPeriodDiscriminator(hps.model.use_spectral_norm)
+ if torch.cuda.is_available():
+ net_d = net_d.cuda(rank)
+ optim_g = torch.optim.AdamW(
+ net_g.parameters(),
+ hps.train.learning_rate,
+ betas=hps.train.betas,
+ eps=hps.train.eps,
+ )
+ optim_d = torch.optim.AdamW(
+ net_d.parameters(),
+ hps.train.learning_rate,
+ betas=hps.train.betas,
+ eps=hps.train.eps,
+ )
+ # net_g = DDP(net_g, device_ids=[rank], find_unused_parameters=True)
+ # net_d = DDP(net_d, device_ids=[rank], find_unused_parameters=True)
+ if use_ddp:
+ if torch.cuda.is_available():
+ net_g = DDP(net_g, device_ids=[rank])
+ net_d = DDP(net_d, device_ids=[rank])
+ else:
+ net_g = DDP(net_g)
+ net_d = DDP(net_d)
+
+ try: # 如果能加载自动resume
+ _, _, _, epoch_str = utils.load_checkpoint(
+ utils.latest_checkpoint_path(hps.model_dir, "D_*.pth"), net_d, optim_d
+ ) # D多半加载没事
+ if rank == 0:
+ logger.info(i18n("已恢复判别器检查点"))
+ # _, _, _, epoch_str = utils.load_checkpoint(utils.latest_checkpoint_path(hps.model_dir, "G_*.pth"), net_g, optim_g,load_opt=0)
+ _, _, _, epoch_str = utils.load_checkpoint(
+ utils.latest_checkpoint_path(hps.model_dir, "G_*.pth"), net_g, optim_g
+ )
+ global_step = (epoch_str - 1) * len(train_loader)
+ # epoch_str = 1
+ # global_step = 0
+ except Exception: # 如果首次不能加载,加载pretrain
+ # traceback.print_exc()
+ epoch_str = 1
+ global_step = 0
+ if hps.pretrainG != "":
+ if rank == 0:
+ logger.info(i18n("已加载生成器预训练模型:%s") % hps.pretrainG)
+ if hasattr(net_g, "module"):
+ logger.info(
+ net_g.module.load_state_dict(
+ torch.load(hps.pretrainG, map_location="cpu")["model"]
+ )
+ ) ##测试不加载优化器
+ else:
+ logger.info(
+ net_g.load_state_dict(
+ torch.load(hps.pretrainG, map_location="cpu")["model"]
+ )
+ ) ##测试不加载优化器
+ if hps.pretrainD != "":
+ if rank == 0:
+ logger.info(i18n("已加载判别器预训练模型:%s") % hps.pretrainD)
+ if hasattr(net_d, "module"):
+ logger.info(
+ net_d.module.load_state_dict(
+ torch.load(hps.pretrainD, map_location="cpu")["model"]
+ )
+ )
+ else:
+ logger.info(
+ net_d.load_state_dict(
+ torch.load(hps.pretrainD, map_location="cpu")["model"]
+ )
+ )
+
+ scheduler_g = torch.optim.lr_scheduler.ExponentialLR(
+ optim_g, gamma=hps.train.lr_decay, last_epoch=epoch_str - 2
+ )
+ scheduler_d = torch.optim.lr_scheduler.ExponentialLR(
+ optim_d, gamma=hps.train.lr_decay, last_epoch=epoch_str - 2
+ )
+
+ scaler = GradScaler(enabled=training_is_half)
+
+ cache = []
+ for epoch in range(epoch_str, hps.train.epochs + 1):
+ if rank == 0:
+ train_and_evaluate(
+ rank,
+ epoch,
+ hps,
+ [net_g, net_d],
+ [optim_g, optim_d],
+ [scheduler_g, scheduler_d],
+ scaler,
+ [train_loader, None],
+ logger,
+ [writer, writer_eval],
+ cache,
+ )
+ else:
+ train_and_evaluate(
+ rank,
+ epoch,
+ hps,
+ [net_g, net_d],
+ [optim_g, optim_d],
+ [scheduler_g, scheduler_d],
+ scaler,
+ [train_loader, None],
+ None,
+ None,
+ cache,
+ )
+ scheduler_g.step()
+ scheduler_d.step()
+
+
+def train_and_evaluate(
+ rank, epoch, hps, nets, optims, schedulers, scaler, loaders, logger, writers, cache
+):
+ net_g, net_d = nets
+ optim_g, optim_d = optims
+ train_loader, eval_loader = loaders
+ if writers is not None:
+ writer, writer_eval = writers
+
+ train_loader.batch_sampler.set_epoch(epoch)
+ global global_step
+
+ net_g.train()
+ net_d.train()
+
+ # Prepare data iterator
+ if hps.if_cache_data_in_gpu == True:
+ # Use Cache
+ data_iterator = cache
+ if cache == []:
+ # Make new cache
+ for batch_idx, info in enumerate(train_loader):
+ # Unpack
+ if hps.if_f0 == 1:
+ (
+ phone,
+ phone_lengths,
+ pitch,
+ pitchf,
+ spec,
+ spec_lengths,
+ wave,
+ wave_lengths,
+ sid,
+ ) = info
+ else:
+ (
+ phone,
+ phone_lengths,
+ spec,
+ spec_lengths,
+ wave,
+ wave_lengths,
+ sid,
+ ) = info
+ # Load on CUDA
+ if torch.cuda.is_available():
+ phone = phone.cuda(rank, non_blocking=True)
+ phone_lengths = phone_lengths.cuda(rank, non_blocking=True)
+ if hps.if_f0 == 1:
+ pitch = pitch.cuda(rank, non_blocking=True)
+ pitchf = pitchf.cuda(rank, non_blocking=True)
+ sid = sid.cuda(rank, non_blocking=True)
+ spec = spec.cuda(rank, non_blocking=True)
+ spec_lengths = spec_lengths.cuda(rank, non_blocking=True)
+ wave = wave.cuda(rank, non_blocking=True)
+ wave_lengths = wave_lengths.cuda(rank, non_blocking=True)
+ # Cache on list
+ if hps.if_f0 == 1:
+ cache.append(
+ (
+ batch_idx,
+ (
+ phone,
+ phone_lengths,
+ pitch,
+ pitchf,
+ spec,
+ spec_lengths,
+ wave,
+ wave_lengths,
+ sid,
+ ),
+ )
+ )
+ else:
+ cache.append(
+ (
+ batch_idx,
+ (
+ phone,
+ phone_lengths,
+ spec,
+ spec_lengths,
+ wave,
+ wave_lengths,
+ sid,
+ ),
+ )
+ )
+ else:
+ # Load shuffled cache
+ shuffle(cache)
+ else:
+ # Loader
+ data_iterator = enumerate(train_loader)
+
+ # Run steps
+ epoch_recorder = EpochRecorder()
+ for batch_idx, info in data_iterator:
+ # Data
+ ## Unpack
+ if hps.if_f0 == 1:
+ (
+ phone,
+ phone_lengths,
+ pitch,
+ pitchf,
+ spec,
+ spec_lengths,
+ wave,
+ wave_lengths,
+ sid,
+ ) = info
+ else:
+ phone, phone_lengths, spec, spec_lengths, wave, wave_lengths, sid = info
+ ## Load on CUDA
+ if (hps.if_cache_data_in_gpu == False) and torch.cuda.is_available():
+ phone = phone.cuda(rank, non_blocking=True)
+ phone_lengths = phone_lengths.cuda(rank, non_blocking=True)
+ if hps.if_f0 == 1:
+ pitch = pitch.cuda(rank, non_blocking=True)
+ pitchf = pitchf.cuda(rank, non_blocking=True)
+ sid = sid.cuda(rank, non_blocking=True)
+ spec = spec.cuda(rank, non_blocking=True)
+ spec_lengths = spec_lengths.cuda(rank, non_blocking=True)
+ wave = wave.cuda(rank, non_blocking=True)
+ # wave_lengths = wave_lengths.cuda(rank, non_blocking=True)
+
+ # Calculate
+ with autocast(enabled=training_is_half):
+ if hps.if_f0 == 1:
+ (
+ y_hat,
+ ids_slice,
+ x_mask,
+ z_mask,
+ (z, z_p, m_p, logs_p, m_q, logs_q),
+ ) = net_g(phone, phone_lengths, pitch, pitchf, spec, spec_lengths, sid)
+ else:
+ (
+ y_hat,
+ ids_slice,
+ x_mask,
+ z_mask,
+ (z, z_p, m_p, logs_p, m_q, logs_q),
+ ) = net_g(phone, phone_lengths, spec, spec_lengths, sid)
+ mel = spec_to_mel_torch(
+ spec,
+ hps.data.filter_length,
+ hps.data.n_mel_channels,
+ hps.data.sampling_rate,
+ hps.data.mel_fmin,
+ hps.data.mel_fmax,
+ )
+ y_mel = commons.slice_segments(
+ mel, ids_slice, hps.train.segment_size // hps.data.hop_length
+ )
+ with autocast(enabled=False):
+ y_hat_mel = mel_spectrogram_torch(
+ y_hat.float().squeeze(1),
+ hps.data.filter_length,
+ hps.data.n_mel_channels,
+ hps.data.sampling_rate,
+ hps.data.hop_length,
+ hps.data.win_length,
+ hps.data.mel_fmin,
+ hps.data.mel_fmax,
+ )
+ if training_is_half:
+ y_hat_mel = y_hat_mel.half()
+ wave = commons.slice_segments(
+ wave, ids_slice * hps.data.hop_length, hps.train.segment_size
+ ) # slice
+
+ # Discriminator
+ y_d_hat_r, y_d_hat_g, _, _ = net_d(wave, y_hat.detach())
+ with autocast(enabled=False):
+ loss_disc, losses_disc_r, losses_disc_g = discriminator_loss(
+ y_d_hat_r, y_d_hat_g
+ )
+ optim_d.zero_grad()
+ scaler.scale(loss_disc).backward()
+ scaler.unscale_(optim_d)
+ grad_norm_d = commons.clip_grad_value_(net_d.parameters(), None)
+ scaler.step(optim_d)
+
+ with autocast(enabled=training_is_half):
+ # Generator
+ y_d_hat_r, y_d_hat_g, fmap_r, fmap_g = net_d(wave, y_hat)
+ with autocast(enabled=False):
+ loss_mel = F.l1_loss(y_mel, y_hat_mel) * hps.train.c_mel
+ loss_kl = kl_loss(z_p, logs_q, m_p, logs_p, z_mask) * hps.train.c_kl
+ loss_fm = feature_loss(fmap_r, fmap_g)
+ loss_gen, losses_gen = generator_loss(y_d_hat_g)
+ loss_gen_all = loss_gen + loss_fm + loss_mel + loss_kl
+ optim_g.zero_grad()
+ scaler.scale(loss_gen_all).backward()
+ scaler.unscale_(optim_g)
+ grad_norm_g = commons.clip_grad_value_(net_g.parameters(), None)
+ scaler.step(optim_g)
+ scaler.update()
+
+ if rank == 0:
+ if global_step % hps.train.log_interval == 0:
+ lr = optim_g.param_groups[0]["lr"]
+ logger.info(
+ i18n("训练轮次:{} [{:.0f}%]").format(
+ epoch, 100.0 * batch_idx / len(train_loader)
+ )
+ )
+ # Amor For Tensorboard display
+ if loss_mel > 75:
+ loss_mel = 75
+ if loss_kl > 9:
+ loss_kl = 9
+
+ logger.info([global_step, lr])
+ logger.info(
+ f"loss_disc={loss_disc:.3f}, loss_gen={loss_gen:.3f}, loss_fm={loss_fm:.3f},loss_mel={loss_mel:.3f}, loss_kl={loss_kl:.3f}"
+ )
+ scalar_dict = {
+ "loss/g/total": loss_gen_all,
+ "loss/d/total": loss_disc,
+ "learning_rate": lr,
+ "grad_norm_d": grad_norm_d,
+ "grad_norm_g": grad_norm_g,
+ }
+ scalar_dict.update(
+ {
+ "loss/g/fm": loss_fm,
+ "loss/g/mel": loss_mel,
+ "loss/g/kl": loss_kl,
+ }
+ )
+
+ scalar_dict.update(
+ {"loss/g/{}".format(i): v for i, v in enumerate(losses_gen)}
+ )
+ scalar_dict.update(
+ {"loss/d_r/{}".format(i): v for i, v in enumerate(losses_disc_r)}
+ )
+ scalar_dict.update(
+ {"loss/d_g/{}".format(i): v for i, v in enumerate(losses_disc_g)}
+ )
+ image_dict = {
+ "slice/mel_org": utils.plot_spectrogram_to_numpy(
+ y_mel[0].data.cpu().numpy()
+ ),
+ "slice/mel_gen": utils.plot_spectrogram_to_numpy(
+ y_hat_mel[0].data.cpu().numpy()
+ ),
+ "all/mel": utils.plot_spectrogram_to_numpy(
+ mel[0].data.cpu().numpy()
+ ),
+ }
+ utils.summarize(
+ writer=writer,
+ global_step=global_step,
+ images=image_dict,
+ scalars=scalar_dict,
+ )
+ global_step += 1
+ # /Run steps
+
+ if epoch % hps.save_every_epoch == 0 and rank == 0:
+ if hps.if_latest == 0:
+ utils.save_checkpoint(
+ net_g,
+ optim_g,
+ hps.train.learning_rate,
+ epoch,
+ os.path.join(hps.model_dir, "G_{}.pth".format(global_step)),
+ )
+ utils.save_checkpoint(
+ net_d,
+ optim_d,
+ hps.train.learning_rate,
+ epoch,
+ os.path.join(hps.model_dir, "D_{}.pth".format(global_step)),
+ )
+ else:
+ utils.save_checkpoint(
+ net_g,
+ optim_g,
+ hps.train.learning_rate,
+ epoch,
+ os.path.join(hps.model_dir, "G_{}.pth".format(2333333)),
+ )
+ utils.save_checkpoint(
+ net_d,
+ optim_d,
+ hps.train.learning_rate,
+ epoch,
+ os.path.join(hps.model_dir, "D_{}.pth".format(2333333)),
+ )
+ if rank == 0 and hps.save_every_weights == "1":
+ if hasattr(net_g, "module"):
+ ckpt = net_g.module.state_dict()
+ else:
+ ckpt = net_g.state_dict()
+ logger.info(
+ i18n("正在保存检查点 %s_e%s:%s")
+ % (
+ hps.name,
+ epoch,
+ savee(
+ ckpt,
+ hps.sample_rate,
+ hps.if_f0,
+ hps.name + "_e%s_s%s" % (epoch, global_step),
+ epoch,
+ hps.version,
+ hps,
+ ),
+ )
+ )
+
+ if rank == 0:
+ logger.info(i18n("====> 轮次:{} {}").format(epoch, epoch_recorder.record()))
+ if epoch >= hps.total_epoch and rank == 0:
+ logger.info(i18n("训练已完成,正在保存最终模型"))
+
+ if hasattr(net_g, "module"):
+ ckpt = net_g.module.state_dict()
+ else:
+ ckpt = net_g.state_dict()
+ logger.info(
+ i18n("正在保存最终检查点:%s")
+ % (
+ savee(
+ ckpt, hps.sample_rate, hps.if_f0, hps.name, epoch, hps.version, hps
+ )
+ )
+ )
+ sleep(1)
+ os._exit(0)
+
+
+if __name__ == "__main__":
+ torch.multiprocessing.set_start_method("spawn")
+ main()
diff --git a/train/train_index.py b/train/train_index.py
new file mode 100644
index 0000000..f8d07ec
--- /dev/null
+++ b/train/train_index.py
@@ -0,0 +1,174 @@
+import os
+import platform
+import sys
+import traceback
+import glob
+
+import faiss
+import numpy as np
+from sklearn.cluster import MiniBatchKMeans
+from i18n.i18n import I18nAuto
+from tools.progress import should_report
+
+
+i18n = I18nAuto()
+
+
+exp_name = sys.argv[1]
+version = sys.argv[2]
+outside_index_root = sys.argv[3]
+n_cpu = int(sys.argv[4])
+exp_dir = os.path.join("logs", exp_name)
+feature_dir = os.path.join(
+ exp_dir, "3_feature256" if version == "v1" else "3_feature768"
+)
+log_path = os.path.join(exp_dir, "train_index.log")
+os.makedirs(exp_dir, exist_ok=True)
+
+
+def log(message):
+ print(message, flush=True)
+ with open(log_path, "a", encoding="utf8") as f:
+ f.write(str(message) + "\n")
+
+
+with open(log_path, "w", encoding="utf8"):
+ pass
+
+
+def newest_index(pattern):
+ paths = [path for path in glob.glob(pattern) if os.path.isfile(path)]
+ return max(paths, key=os.path.getmtime) if paths else ""
+
+
+def link_added_index(added_path):
+ added_name = os.path.basename(added_path)
+ try:
+ os.makedirs(outside_index_root, exist_ok=True)
+ source = os.path.abspath(added_path)
+ outside_root = os.path.abspath(outside_index_root)
+ if os.path.commonpath([source, outside_root]) == outside_root:
+ log(i18n("[索引训练] 外部索引链接已存在:%s") % source)
+ return
+ target = os.path.abspath(
+ os.path.join(outside_index_root, "%s_%s" % (exp_name, added_name))
+ )
+ if os.path.lexists(target):
+ try:
+ if os.path.samefile(source, target):
+ log(i18n("[索引训练] 外部索引链接已存在:%s") % target)
+ return
+ except (FileNotFoundError, OSError):
+ pass
+ os.unlink(target)
+ if platform.system() == "Windows":
+ os.link(source, target)
+ else:
+ os.symlink(source, target)
+ log(i18n("[索引训练] 已链接索引到外部目录:%s") % outside_index_root)
+ except Exception:
+ log(
+ i18n("[索引训练][失败] 无法链接索引到外部目录:%s\n%s")
+ % (outside_index_root, traceback.format_exc())
+ )
+
+
+existing_trained_path = newest_index(
+ os.path.join(
+ exp_dir,
+ "trained_IVF*_Flat_nprobe_*_%s_%s.index" % (exp_name, version),
+ )
+)
+existing_added_path = newest_index(
+ os.path.join(exp_dir, "added_IVF*_Flat_nprobe_*_%s_%s.index" % (exp_name, version))
+)
+if not existing_added_path:
+ existing_added_path = newest_index(
+ os.path.join(
+ outside_index_root,
+ "%s_*added_IVF*_Flat_nprobe_*_%s_%s.index"
+ % (exp_name, exp_name, version),
+ )
+ )
+if existing_added_path:
+ if existing_trained_path:
+ log(
+ i18n("[索引训练][跳过] trained索引已存在:%s")
+ % os.path.basename(existing_trained_path)
+ )
+ log(
+ i18n("[索引训练][跳过] added索引已存在:%s")
+ % os.path.basename(existing_added_path)
+ )
+ link_added_index(existing_added_path)
+ raise SystemExit(0)
+
+if not os.path.isdir(feature_dir) or not os.listdir(feature_dir):
+ log(i18n("[索引训练][失败] 请先进行特征提取"))
+ raise SystemExit(1)
+
+features = []
+for name in sorted(os.listdir(feature_dir)):
+ features.append(np.load(os.path.join(feature_dir, name)))
+
+big_npy = np.concatenate(features, 0)
+big_npy = big_npy[np.random.permutation(big_npy.shape[0])]
+if big_npy.shape[0] > 200000:
+ log(i18n("[索引训练] 正在将%s条特征聚类为10000个中心") % big_npy.shape[0])
+ try:
+ big_npy = MiniBatchKMeans(
+ n_clusters=10000,
+ verbose=False,
+ batch_size=256 * n_cpu,
+ compute_labels=False,
+ init="random",
+ ).fit(big_npy).cluster_centers_
+ except Exception:
+ log(i18n("[索引训练][失败] 聚类失败,将使用原始特征继续\n%s") % traceback.format_exc())
+
+n_ivf = max(1, min(int(16 * np.sqrt(big_npy.shape[0])), big_npy.shape[0] // 39))
+log(i18n("[索引训练] 特征形状:%s | IVF数量:%s") % (big_npy.shape, n_ivf))
+if existing_trained_path:
+ trained_path = existing_trained_path
+ index = faiss.read_index(trained_path)
+ index_ivf = faiss.extract_index_ivf(index)
+ index_ivf.nprobe = 1
+ log(
+ i18n("[索引训练][跳过] trained索引已存在:%s")
+ % os.path.basename(trained_path)
+ )
+else:
+ index = faiss.index_factory(
+ 256 if version == "v1" else 768, "IVF%s,Flat" % n_ivf
+ )
+ index_ivf = faiss.extract_index_ivf(index)
+ index_ivf.nprobe = 1
+ trained_path = os.path.join(
+ exp_dir,
+ "trained_IVF%s_Flat_nprobe_%s_%s_%s.index"
+ % (n_ivf, index_ivf.nprobe, exp_name, version),
+ )
+ log(i18n("[索引训练] 正在训练索引"))
+ index.train(big_npy)
+ faiss.write_index(index, trained_path)
+
+log(i18n("[索引训练] 正在写入特征向量"))
+starts = list(range(0, big_npy.shape[0], 8192))
+for batch_index, start in enumerate(starts):
+ index.add(big_npy[start : start + 8192])
+ if should_report(batch_index, len(starts), 10):
+ log(
+ i18n("[索引训练] 写入进度:%s/%s")
+ % (batch_index + 1, len(starts))
+ )
+
+added_name = "added_IVF%s_Flat_nprobe_%s_%s_%s.index" % (
+ n_ivf,
+ index_ivf.nprobe,
+ exp_name,
+ version,
+)
+added_path = os.path.join(exp_dir, added_name)
+faiss.write_index(index, added_path)
+log(i18n("[索引训练] 成功构建索引:%s") % added_name)
+link_added_index(added_path)
diff --git a/train/utils.py b/train/utils.py
new file mode 100644
index 0000000..c24d303
--- /dev/null
+++ b/train/utils.py
@@ -0,0 +1,479 @@
+import argparse
+import glob
+import json
+import logging
+import os
+import subprocess
+import sys
+import shutil
+
+import numpy as np
+import torch
+from scipy.io.wavfile import read
+from tools.file_io import read_text
+
+MATPLOTLIB_FLAG = False
+
+logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
+logger = logging
+
+
+def load_checkpoint_d(checkpoint_path, combd, sbd, optimizer=None, load_opt=1):
+ assert os.path.isfile(checkpoint_path)
+ checkpoint_dict = torch.load(checkpoint_path, map_location="cpu")
+
+ ##################
+ def go(model, bkey):
+ saved_state_dict = checkpoint_dict[bkey]
+ if hasattr(model, "module"):
+ state_dict = model.module.state_dict()
+ else:
+ state_dict = model.state_dict()
+ new_state_dict = {}
+ for k, v in state_dict.items(): # 模型需要的shape
+ try:
+ new_state_dict[k] = saved_state_dict[k]
+ if saved_state_dict[k].shape != state_dict[k].shape:
+ logger.warning(
+ "shape-%s-mismatch. need: %s, get: %s",
+ k,
+ state_dict[k].shape,
+ saved_state_dict[k].shape,
+ ) #
+ raise KeyError
+ except:
+ # logger.info(traceback.format_exc())
+ logger.info("%s is not in the checkpoint", k) # pretrain缺失的
+ new_state_dict[k] = v # 模型自带的随机值
+ if hasattr(model, "module"):
+ model.module.load_state_dict(new_state_dict, strict=False)
+ else:
+ model.load_state_dict(new_state_dict, strict=False)
+ return model
+
+ go(combd, "combd")
+ model = go(sbd, "sbd")
+ #############
+ logger.info("Loaded model weights")
+
+ iteration = checkpoint_dict["iteration"]
+ learning_rate = checkpoint_dict["learning_rate"]
+ if (
+ optimizer is not None and load_opt == 1
+ ): ###加载不了,如果是空的的话,重新初始化,可能还会影响lr时间表的更新,因此在train文件最外围catch
+ # try:
+ optimizer.load_state_dict(checkpoint_dict["optimizer"])
+ # except:
+ # traceback.print_exc()
+ logger.info("Loaded checkpoint '{}' (epoch {})".format(checkpoint_path, iteration))
+ return model, optimizer, learning_rate, iteration
+
+
+# def load_checkpoint(checkpoint_path, model, optimizer=None):
+# assert os.path.isfile(checkpoint_path)
+# checkpoint_dict = torch.load(checkpoint_path, map_location='cpu')
+# iteration = checkpoint_dict['iteration']
+# learning_rate = checkpoint_dict['learning_rate']
+# if optimizer is not None:
+# optimizer.load_state_dict(checkpoint_dict['optimizer'])
+# # print(1111)
+# saved_state_dict = checkpoint_dict['model']
+# # print(1111)
+#
+# if hasattr(model, 'module'):
+# state_dict = model.module.state_dict()
+# else:
+# state_dict = model.state_dict()
+# new_state_dict= {}
+# for k, v in state_dict.items():
+# try:
+# new_state_dict[k] = saved_state_dict[k]
+# except:
+# logger.info("%s is not in the checkpoint" % k)
+# new_state_dict[k] = v
+# if hasattr(model, 'module'):
+# model.module.load_state_dict(new_state_dict)
+# else:
+# model.load_state_dict(new_state_dict)
+# logger.info("Loaded checkpoint '{}' (epoch {})" .format(
+# checkpoint_path, iteration))
+# return model, optimizer, learning_rate, iteration
+def load_checkpoint(checkpoint_path, model, optimizer=None, load_opt=1):
+ assert os.path.isfile(checkpoint_path)
+ checkpoint_dict = torch.load(checkpoint_path, map_location="cpu")
+
+ saved_state_dict = checkpoint_dict["model"]
+ if hasattr(model, "module"):
+ state_dict = model.module.state_dict()
+ else:
+ state_dict = model.state_dict()
+ new_state_dict = {}
+ for k, v in state_dict.items(): # 模型需要的shape
+ try:
+ new_state_dict[k] = saved_state_dict[k]
+ if saved_state_dict[k].shape != state_dict[k].shape:
+ logger.warning(
+ "shape-%s-mismatch|need-%s|get-%s",
+ k,
+ state_dict[k].shape,
+ saved_state_dict[k].shape,
+ ) #
+ raise KeyError
+ except:
+ # logger.info(traceback.format_exc())
+ logger.info("%s is not in the checkpoint", k) # pretrain缺失的
+ new_state_dict[k] = v # 模型自带的随机值
+ if hasattr(model, "module"):
+ model.module.load_state_dict(new_state_dict, strict=False)
+ else:
+ model.load_state_dict(new_state_dict, strict=False)
+ logger.info("Loaded model weights")
+
+ iteration = checkpoint_dict["iteration"]
+ learning_rate = checkpoint_dict["learning_rate"]
+ if (
+ optimizer is not None and load_opt == 1
+ ): ###加载不了,如果是空的的话,重新初始化,可能还会影响lr时间表的更新,因此在train文件最外围catch
+ # try:
+ optimizer.load_state_dict(checkpoint_dict["optimizer"])
+ # except:
+ # traceback.print_exc()
+ logger.info("Loaded checkpoint '{}' (epoch {})".format(checkpoint_path, iteration))
+ return model, optimizer, learning_rate, iteration
+
+
+def save_checkpoint(model, optimizer, learning_rate, iteration, checkpoint_path):
+ logger.info(
+ "Saving model and optimizer state at epoch {} to {}".format(
+ iteration, checkpoint_path
+ )
+ )
+ if hasattr(model, "module"):
+ state_dict = model.module.state_dict()
+ else:
+ state_dict = model.state_dict()
+ torch.save(
+ {
+ "model": state_dict,
+ "iteration": iteration,
+ "optimizer": optimizer.state_dict(),
+ "learning_rate": learning_rate,
+ },
+ checkpoint_path,
+ )
+
+
+def save_checkpoint_d(combd, sbd, optimizer, learning_rate, iteration, checkpoint_path):
+ logger.info(
+ "Saving model and optimizer state at epoch {} to {}".format(
+ iteration, checkpoint_path
+ )
+ )
+ if hasattr(combd, "module"):
+ state_dict_combd = combd.module.state_dict()
+ else:
+ state_dict_combd = combd.state_dict()
+ if hasattr(sbd, "module"):
+ state_dict_sbd = sbd.module.state_dict()
+ else:
+ state_dict_sbd = sbd.state_dict()
+ torch.save(
+ {
+ "combd": state_dict_combd,
+ "sbd": state_dict_sbd,
+ "iteration": iteration,
+ "optimizer": optimizer.state_dict(),
+ "learning_rate": learning_rate,
+ },
+ checkpoint_path,
+ )
+
+
+def summarize(
+ writer,
+ global_step,
+ scalars={},
+ histograms={},
+ images={},
+ audios={},
+ audio_sampling_rate=22050,
+):
+ for k, v in scalars.items():
+ writer.add_scalar(k, v, global_step)
+ for k, v in histograms.items():
+ writer.add_histogram(k, v, global_step)
+ for k, v in images.items():
+ writer.add_image(k, v, global_step, dataformats="HWC")
+ for k, v in audios.items():
+ writer.add_audio(k, v, global_step, audio_sampling_rate)
+
+
+def latest_checkpoint_path(dir_path, regex="G_*.pth"):
+ f_list = glob.glob(os.path.join(dir_path, regex))
+ f_list.sort(key=lambda f: int("".join(filter(str.isdigit, f))))
+ x = f_list[-1]
+ logger.debug(x)
+ return x
+
+
+def figure_to_rgb_array(fig):
+ import numpy as np
+
+ fig.canvas.draw()
+ if hasattr(fig.canvas, "buffer_rgba"):
+ return np.asarray(fig.canvas.buffer_rgba())[..., :3].copy()
+ width, height = fig.canvas.get_width_height()
+ return np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8).reshape(
+ height, width, 3
+ )
+
+
+def plot_spectrogram_to_numpy(spectrogram):
+ global MATPLOTLIB_FLAG
+ if not MATPLOTLIB_FLAG:
+ import matplotlib
+
+ matplotlib.use("Agg")
+ MATPLOTLIB_FLAG = True
+ mpl_logger = logging.getLogger("matplotlib")
+ mpl_logger.setLevel(logging.WARNING)
+ import matplotlib.pylab as plt
+
+ fig, ax = plt.subplots(figsize=(10, 2))
+ im = ax.imshow(spectrogram, aspect="auto", origin="lower", interpolation="none")
+ plt.colorbar(im, ax=ax)
+ plt.xlabel("Frames")
+ plt.ylabel("Channels")
+ plt.tight_layout()
+
+ data = figure_to_rgb_array(fig)
+ plt.close()
+ return data
+
+
+def plot_alignment_to_numpy(alignment, info=None):
+ global MATPLOTLIB_FLAG
+ if not MATPLOTLIB_FLAG:
+ import matplotlib
+
+ matplotlib.use("Agg")
+ MATPLOTLIB_FLAG = True
+ mpl_logger = logging.getLogger("matplotlib")
+ mpl_logger.setLevel(logging.WARNING)
+ import matplotlib.pylab as plt
+
+ fig, ax = plt.subplots(figsize=(6, 4))
+ im = ax.imshow(
+ alignment.transpose(), aspect="auto", origin="lower", interpolation="none"
+ )
+ fig.colorbar(im, ax=ax)
+ xlabel = "Decoder timestep"
+ if info is not None:
+ xlabel += "\n\n" + info
+ plt.xlabel(xlabel)
+ plt.ylabel("Encoder timestep")
+ plt.tight_layout()
+
+ data = figure_to_rgb_array(fig)
+ plt.close()
+ return data
+
+
+def load_wav_to_torch(full_path):
+ sampling_rate, data = read(full_path)
+ return torch.FloatTensor(data.astype(np.float32)), sampling_rate
+
+
+def load_filepaths_and_text(filename, split="|"):
+ return [line.strip().split(split) for line in read_text(filename).splitlines()]
+
+
+def get_hparams(init=True):
+ """
+ todo:
+ 结尾七人组:
+ 保存频率、总epoch done
+ bs done
+ pretrainG、pretrainD done
+ 卡号:os.en["CUDA_VISIBLE_DEVICES"] done
+ if_latest done
+ 模型:if_f0 done
+ 采样率:自动选择config done
+ 是否缓存数据集进GPU:if_cache_data_in_gpu done
+
+ -m:
+ 自动决定training_files路径,改掉train_nsf_load_pretrain.py里的hps.data.training_files done
+ -c不要了
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-se",
+ "--save_every_epoch",
+ type=int,
+ required=True,
+ help="checkpoint save frequency (epoch)",
+ )
+ parser.add_argument(
+ "-te", "--total_epoch", type=int, required=True, help="total_epoch"
+ )
+ parser.add_argument(
+ "-pg", "--pretrainG", type=str, default="", help="Pretrained Generator path"
+ )
+ parser.add_argument(
+ "-pd", "--pretrainD", type=str, default="", help="Pretrained Discriminator path"
+ )
+ parser.add_argument("-g", "--gpus", type=str, default="0", help="split by -")
+ parser.add_argument(
+ "-bs", "--batch_size", type=int, required=True, help="batch size"
+ )
+ parser.add_argument(
+ "-e", "--experiment_dir", type=str, required=True, help="experiment dir"
+ ) # -m
+ parser.add_argument(
+ "-sr", "--sample_rate", type=str, required=True, help="sample rate, 32k/40k/48k"
+ )
+ parser.add_argument(
+ "-sw",
+ "--save_every_weights",
+ type=str,
+ default="0",
+ help="save the extracted model in weights directory when saving checkpoints",
+ )
+ parser.add_argument(
+ "-v", "--version", type=str, required=True, help="model version"
+ )
+ parser.add_argument(
+ "-f0",
+ "--if_f0",
+ type=int,
+ required=True,
+ help="use f0 as one of the inputs of the model, 1 or 0",
+ )
+ parser.add_argument(
+ "-l",
+ "--if_latest",
+ type=int,
+ required=True,
+ help="if only save the latest G/D pth file, 1 or 0",
+ )
+ parser.add_argument(
+ "-c",
+ "--if_cache_data_in_gpu",
+ type=int,
+ required=True,
+ help="if caching the dataset in GPU memory, 1 or 0",
+ )
+
+ args = parser.parse_args()
+ name = args.experiment_dir
+ experiment_dir = os.path.join("./logs", args.experiment_dir)
+
+ config_save_path = os.path.join(experiment_dir, "config.json")
+ config = json.loads(read_text(config_save_path))
+
+ hparams = HParams(**config)
+ hparams.model_dir = hparams.experiment_dir = experiment_dir
+ hparams.save_every_epoch = args.save_every_epoch
+ hparams.name = name
+ hparams.total_epoch = args.total_epoch
+ hparams.pretrainG = args.pretrainG
+ hparams.pretrainD = args.pretrainD
+ hparams.version = args.version
+ hparams.gpus = args.gpus
+ hparams.train.batch_size = args.batch_size
+ hparams.sample_rate = args.sample_rate
+ hparams.if_f0 = args.if_f0
+ hparams.if_latest = args.if_latest
+ hparams.save_every_weights = args.save_every_weights
+ hparams.if_cache_data_in_gpu = args.if_cache_data_in_gpu
+ hparams.data.training_files = "%s/filelist.txt" % experiment_dir
+ return hparams
+
+
+def get_hparams_from_dir(model_dir):
+ config_save_path = os.path.join(model_dir, "config.json")
+ config = json.loads(read_text(config_save_path))
+
+ hparams = HParams(**config)
+ hparams.model_dir = model_dir
+ return hparams
+
+
+def get_hparams_from_file(config_path):
+ config = json.loads(read_text(config_path))
+
+ hparams = HParams(**config)
+ return hparams
+
+
+def check_git_hash(model_dir):
+ source_dir = os.path.dirname(os.path.realpath(__file__))
+ if not os.path.exists(os.path.join(source_dir, ".git")):
+ logger.warning(
+ "{} is not a git repository, therefore hash value comparison will be ignored.".format(
+ source_dir
+ )
+ )
+ return
+
+ cur_hash = subprocess.getoutput("git rev-parse HEAD")
+
+ path = os.path.join(model_dir, "githash")
+ if os.path.exists(path):
+ saved_hash = read_text(path)
+ if saved_hash != cur_hash:
+ logger.warning(
+ "git hash values are different. {}(saved) != {}(current)".format(
+ saved_hash[:8], cur_hash[:8]
+ )
+ )
+ else:
+ with open(path, "w", encoding="utf8") as f:
+ f.write(cur_hash)
+
+
+def get_logger(model_dir, filename="train.log"):
+ global logger
+ logger = logging.getLogger(os.path.basename(model_dir))
+ logger.setLevel(logging.DEBUG)
+
+ formatter = logging.Formatter("%(asctime)s\t%(name)s\t%(levelname)s\t%(message)s")
+ if not os.path.exists(model_dir):
+ os.makedirs(model_dir)
+ h = logging.FileHandler(os.path.join(model_dir, filename), encoding="utf8")
+ h.setLevel(logging.DEBUG)
+ h.setFormatter(formatter)
+ logger.addHandler(h)
+ return logger
+
+
+class HParams:
+ def __init__(self, **kwargs):
+ for k, v in kwargs.items():
+ if type(v) == dict:
+ v = HParams(**v)
+ self[k] = v
+
+ def keys(self):
+ return self.__dict__.keys()
+
+ def items(self):
+ return self.__dict__.items()
+
+ def values(self):
+ return self.__dict__.values()
+
+ def __len__(self):
+ return len(self.__dict__)
+
+ def __getitem__(self, key):
+ return getattr(self, key)
+
+ def __setitem__(self, key, value):
+ return setattr(self, key, value)
+
+ def __contains__(self, key):
+ return key in self.__dict__
+
+ def __repr__(self):
+ return self.__dict__.__repr__()
diff --git a/webui.py b/webui.py
new file mode 100644
index 0000000..eee285a
--- /dev/null
+++ b/webui.py
@@ -0,0 +1,2008 @@
+import os
+import shutil
+
+os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
+os.environ.setdefault("no_proxy", "localhost, 127.0.0.1, ::1")
+os.environ.setdefault("weight_root", "assets/weights")
+os.environ.setdefault("weight_uvr5_root", "assets/uvr5_weights")
+os.environ.setdefault("index_root", "logs")
+os.environ.setdefault("outside_index_root", "assets/indices")
+os.environ.setdefault("rmvpe_root", "assets/rmvpe")
+
+now_dir = os.getcwd()
+tmp = os.path.join(now_dir, "TEMP")
+os.makedirs(tmp, exist_ok=True)
+os.environ["TEMP"] = tmp
+for name in os.listdir(tmp):
+ if name == "jieba.cache":
+ continue
+ path = os.path.join(tmp, name)
+ delete = (
+ os.remove if os.path.isfile(path) or os.path.islink(path) else shutil.rmtree
+ )
+ try:
+ delete(path)
+ except Exception as error:
+ print(str(error))
+
+from infer.vc.modules import VC
+from tools.uvr5.webui import uvr
+from tools.file_io import read_text
+from train.process_ckpt import (
+ change_info,
+ extract_small_model,
+ merge,
+ show_info,
+)
+from i18n.i18n import I18nAuto
+from configs.config import Config, GPU_INDEX, GPU_INFOS, GPU_MEMORY, IS_GPU
+import torch, platform
+import numpy as np
+import gradio as gr
+import pathlib
+import json
+from time import sleep
+from subprocess import Popen
+from random import shuffle
+import warnings
+import traceback
+import threading
+import logging
+import signal
+import socket
+import subprocess
+import time
+
+
+logging.getLogger("numba").setLevel(logging.WARNING)
+logging.getLogger("httpx").setLevel(logging.WARNING)
+
+logger = logging.getLogger(__name__)
+
+
+def find_available_port(start_port, host="0.0.0.0"):
+ """Return the first bindable TCP port at or above ``start_port``."""
+ if not 1 <= start_port <= 65535:
+ raise ValueError(f"Port must be between 1 and 65535, got {start_port}.")
+
+ for port in range(start_port, 65536):
+ try:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ sock.bind((host, port))
+ return port
+ except OSError:
+ continue
+
+ raise OSError(
+ f"No available TCP port from {start_port} through 65535; WebUI was not started."
+ )
+
+
+def is_gradio_port_in_use_error(error, port):
+ """Recognize Gradio's explicit-port conflict without hiding other launch errors."""
+ return str(error).startswith(f"Port {port} is in use.")
+
+
+def launch_webui_with_port_fallback(app, config):
+ """Launch Gradio, increasing the requested port until startup succeeds."""
+ next_port = config.listen_port
+ queued_app = app.queue(concurrency_count=511, max_size=1022)
+ while True:
+ config.listen_port = find_available_port(next_port)
+ if config.listen_port != next_port:
+ logger.warning(
+ "Port %s is occupied; trying port %s instead.",
+ next_port,
+ config.listen_port,
+ )
+ try:
+ queued_app.launch(
+ server_name="0.0.0.0",
+ inbrowser=not config.noautoopen,
+ server_port=config.listen_port,
+ quiet=True,
+ )
+ return config.listen_port
+ except OSError as error:
+ if not is_gradio_port_in_use_error(error, config.listen_port):
+ raise
+ if config.listen_port == 65535:
+ raise OSError(
+ "No available TCP port through 65535; WebUI was not started."
+ ) from error
+ logger.warning(
+ "Port %s became occupied while Gradio was starting; trying the next port.",
+ config.listen_port,
+ )
+ next_port = config.listen_port + 1
+
+os.makedirs(os.path.join(now_dir, "logs"), exist_ok=True)
+os.makedirs(os.path.join(now_dir, "assets/weights"), exist_ok=True)
+warnings.filterwarnings("ignore")
+torch.manual_seed(114514)
+
+
+config = Config()
+vc = VC(config)
+
+
+i18n = I18nAuto()
+logger.info(i18n)
+print(
+ i18n("当前设备:%s | 推理精度:%s") % (config.device, config.dtype),
+ flush=True,
+)
+# GPU filtering and precision rules are shared with inference/extraction/training.
+gpu_infos = list(GPU_INFOS)
+gpu_indices = sorted(GPU_INDEX)
+if_gpu_ok = IS_GPU
+if if_gpu_ok:
+ gpu_info = "\n".join(gpu_infos)
+ default_batch_size = max(1, int(min(GPU_MEMORY[i] for i in gpu_indices)) // 2)
+else:
+ gpu_info = i18n("很遗憾您这没有能用的显卡来支持您训练")
+ default_batch_size = 1
+gpus = "-".join(str(i) for i in gpu_indices)
+
+
+class ToolButton(gr.Button, gr.components.FormComponent):
+ """Small button with single emoji as text, fits inside gradio forms"""
+
+ def __init__(self, **kwargs):
+ super().__init__(variant="tool", **kwargs)
+
+ def get_block_name(self):
+ return "button"
+
+
+weight_root = os.getenv("weight_root")
+weight_uvr5_root = os.getenv("weight_uvr5_root")
+outside_index_root = os.getenv("outside_index_root")
+
+def weight_names():
+ return sorted(
+ name for name in os.listdir(weight_root) if name.endswith(".pth")
+ )
+
+
+def refresh_weight_choices(previous_names=None, force=False):
+ current_names = tuple(weight_names())
+ if force or current_names != previous_names:
+ return current_names, change_choices()
+ return current_names, {"__type__": "update"}
+
+
+names = weight_names()
+uvr5_names = []
+for name in os.listdir(weight_uvr5_root):
+ if name.endswith((".pth", ".ckpt")) or "onnx" in name:
+ uvr5_names.append(name.replace(".pth", "").replace(".ckpt", ""))
+uvr5_names.sort()
+
+
+def change_choices():
+ return {"choices": weight_names(), "__type__": "update"}
+
+
+def clean():
+ return {"value": "", "__type__": "update"}
+
+
+sr_dict = {
+ "32k": 32000,
+ "40k": 40000,
+ "48k": 48000,
+}
+
+
+TRAIN_TASK_LOCK = threading.Lock()
+TRAIN_TASK = None
+
+
+def button_update(value=None, variant=None, visible=None):
+ update = {"__type__": "update"}
+ if value is not None:
+ update["value"] = value
+ if variant is not None:
+ update["variant"] = variant
+ if visible is not None:
+ update["visible"] = visible
+ return update
+
+
+def format_status(title, state, detail=""):
+ lines = ["【%s】" % i18n(title), "%s:%s" % (i18n("状态"), i18n(state))]
+ if detail:
+ lines.extend(["", detail.strip()])
+ return "\n".join(lines)
+
+
+def format_workflow_status(step, detail="", completed_steps=None, state="运行中"):
+ completed_steps = completed_steps or []
+ detail = str(detail).strip()
+ lines = []
+ if completed_steps:
+ lines.append("%s:" % i18n("已完成阶段"))
+ lines.extend(
+ "✓ %s:%s" % (i18n(completed_step), i18n("已成功"))
+ for completed_step in completed_steps
+ )
+ if step:
+ if lines:
+ lines.append("")
+ lines.append("%s:%s" % (i18n("当前阶段"), i18n(step)))
+ if detail:
+ lines.extend(["", detail])
+ return format_status(
+ "一键训练",
+ state,
+ "\n".join(lines),
+ )
+
+
+def read_log(path, max_lines=40):
+ try:
+ lines = [line.rstrip() for line in read_text(path, errors="ignore").splitlines()]
+ lines = [line for line in lines if line.strip()]
+ if len(lines) > max_lines:
+ tail_count = max(0, max_lines - 1)
+ omitted = len(lines) - tail_count
+ tail = lines[-tail_count:] if tail_count else []
+ lines = [i18n("……已省略前%s行,仅显示最新状态") % omitted]
+ lines.extend(tail)
+ return "\n".join(lines)
+ except FileNotFoundError:
+ return ""
+
+
+def artifact_names(directory, suffix):
+ if not os.path.isdir(directory):
+ return set()
+ return {
+ name.split(".")[0]
+ for name in os.listdir(directory)
+ if name.lower().endswith(suffix)
+ }
+
+
+def validate_preprocess_outputs(exp_dir):
+ exp_path = os.path.join(now_dir, "logs", exp_dir)
+ gt_names = artifact_names(os.path.join(exp_path, "0_gt_wavs"), ".wav")
+ wav16_names = artifact_names(os.path.join(exp_path, "1_16k_wavs"), ".wav")
+ if not gt_names:
+ raise RuntimeError(i18n("数据切分没有生成有效训练音频,请检查训练集和数据切分日志"))
+ if not wav16_names:
+ raise RuntimeError(i18n("数据切分没有生成16k音频,已停止后续特征提取和训练"))
+ if not gt_names & wav16_names:
+ raise RuntimeError(i18n("数据切分输出文件不匹配,已停止后续特征提取和训练"))
+
+
+def validate_feature_outputs(exp_dir, version, if_f0):
+ exp_path = os.path.join(now_dir, "logs", exp_dir)
+ wav16_names = artifact_names(os.path.join(exp_path, "1_16k_wavs"), ".wav")
+ feature_name = "3_feature256" if version == "v1" else "3_feature768"
+ feature_names = artifact_names(os.path.join(exp_path, feature_name), ".npy")
+ matched = wav16_names & feature_names
+ if not feature_names or not matched:
+ raise RuntimeError(i18n("HuBERT特征提取没有生成有效结果,已停止训练"))
+ if if_f0:
+ f0_names = artifact_names(os.path.join(exp_path, "2a_f0"), ".npy")
+ f0nsf_names = artifact_names(os.path.join(exp_path, "2b-f0nsf"), ".npy")
+ matched &= f0_names & f0nsf_names
+ if not f0_names or not f0nsf_names or not matched:
+ raise RuntimeError(i18n("F0提取没有生成有效结果,已停止训练"))
+ return matched
+
+
+def kill_process(process, process_name=""):
+ if process is None or process.poll() is not None:
+ return
+ pid = process.pid
+ if platform.system() == "Windows":
+ subprocess.run(
+ "taskkill /t /f /pid %s" % pid,
+ shell=True,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+ else:
+ try:
+ os.killpg(os.getpgid(pid), signal.SIGTERM)
+ except (OSError, ProcessLookupError):
+ try:
+ os.kill(pid, signal.SIGTERM)
+ except OSError:
+ pass
+ for _ in range(10):
+ if process.poll() is not None:
+ break
+ time.sleep(0.1)
+ if process.poll() is None:
+ try:
+ os.killpg(os.getpgid(pid), signal.SIGKILL)
+ except (OSError, ProcessLookupError):
+ pass
+ logger.info(i18n("%s进程已终止") % i18n(process_name))
+
+
+def begin_train_task(name):
+ global TRAIN_TASK
+ with TRAIN_TASK_LOCK:
+ if TRAIN_TASK is None:
+ state = {
+ "name": name,
+ "processes": [],
+ "stop_requested": False,
+ }
+ TRAIN_TASK = state
+ return "start", state
+ return "busy", TRAIN_TASK
+
+
+def stop_train_task(name):
+ with TRAIN_TASK_LOCK:
+ if TRAIN_TASK is None:
+ return (
+ format_status(name, "未运行"),
+ button_update(visible=True),
+ button_update(visible=False),
+ )
+ if TRAIN_TASK["name"] != name:
+ return (
+ format_status(
+ name,
+ "无法停止",
+ i18n("%s运行中,请先停止该任务") % i18n(TRAIN_TASK["name"]),
+ ),
+ button_update(),
+ button_update(),
+ )
+ state = TRAIN_TASK
+ state["stop_requested"] = True
+ processes = list(state["processes"])
+ for process in processes:
+ kill_process(process, name)
+ return (
+ format_status(name, "已停止"),
+ button_update(visible=True),
+ button_update(visible=False),
+ )
+
+
+def finish_train_task(state):
+ global TRAIN_TASK
+ with TRAIN_TASK_LOCK:
+ if TRAIN_TASK is state:
+ TRAIN_TASK = None
+
+
+def train_task_stopped(state):
+ with TRAIN_TASK_LOCK:
+ return state["stop_requested"]
+
+
+def start_train_process(state, cmd):
+ kwargs = {"shell": True, "cwd": now_dir}
+ if platform.system() == "Windows":
+ kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP
+ else:
+ kwargs["start_new_session"] = True
+ logger.info("%s: %s", i18n("执行命令"), cmd)
+ process = Popen(cmd, **kwargs)
+ with TRAIN_TASK_LOCK:
+ state["processes"].append(process)
+ stopped = state["stop_requested"]
+ if stopped:
+ kill_process(process, state["name"])
+ return process
+
+
+def wait_train_processes(
+ state,
+ processes,
+ log_path=None,
+ title="任务",
+ format_output=True,
+ watch_weights=False,
+):
+ last_snapshot = None
+ last_emit_time = 0
+ last_weight_names = tuple(weight_names()) if watch_weights else ()
+ while any(process.poll() is None for process in processes):
+ if train_task_stopped(state):
+ for process in processes:
+ kill_process(process, state["name"])
+ break
+ if log_path:
+ snapshot = read_log(log_path)
+ current_time = time.monotonic()
+ current_weight_names = tuple(weight_names()) if watch_weights else ()
+ weights_changed = watch_weights and current_weight_names != last_weight_names
+ if (
+ snapshot != last_snapshot
+ or weights_changed
+ or current_time - last_emit_time >= 5
+ ):
+ yield (
+ format_status(title, "运行中", snapshot)
+ if format_output
+ else snapshot
+ )
+ last_snapshot = snapshot
+ last_emit_time = current_time
+ last_weight_names = current_weight_names
+ sleep(1)
+ with TRAIN_TASK_LOCK:
+ for process in processes:
+ if process in state["processes"]:
+ state["processes"].remove(process)
+ if log_path:
+ final_state = "已停止" if train_task_stopped(state) else "正在收尾"
+ snapshot = read_log(log_path)
+ yield (
+ format_status(title, final_state, snapshot)
+ if format_output
+ else snapshot
+ )
+ if not train_task_stopped(state):
+ failed = [process.returncode for process in processes if process.returncode != 0]
+ if failed:
+ raise RuntimeError(i18n("子进程执行失败,返回码:%s") % failed)
+
+
+def run_preprocess_dataset(trainset_dir, exp_dir, sr, n_p, state, format_output=True):
+ sr = sr_dict[sr]
+ os.makedirs("%s/logs/%s" % (now_dir, exp_dir), exist_ok=True)
+ log_path = "%s/logs/%s/preprocess.log" % (now_dir, exp_dir)
+ with open(log_path, "w", encoding="utf8"):
+ pass
+ cmd = '"%s" train/preprocess.py "%s" %s %s "%s/logs/%s" %s %.1f' % (
+ config.python_cmd,
+ trainset_dir,
+ sr,
+ n_p,
+ now_dir,
+ exp_dir,
+ config.noparallel,
+ config.preprocess_per,
+ )
+ process = start_train_process(state, cmd)
+ yield from wait_train_processes(
+ state, [process], log_path, "数据切分", format_output
+ )
+ if not train_task_stopped(state):
+ validate_preprocess_outputs(exp_dir)
+
+
+def preprocess_dataset(trainset_dir, exp_dir, sr, n_p):
+ action, state = begin_train_task("数据切分")
+ if action == "busy":
+ yield (
+ format_status(
+ "数据切分",
+ "等待中",
+ i18n("%s运行中,请先停止该任务") % i18n(state["name"]),
+ ),
+ button_update(),
+ button_update(),
+ )
+ return
+ final_info = None
+ try:
+ yield (
+ format_status("数据切分", "正在启动"),
+ button_update(visible=False),
+ button_update(visible=True),
+ )
+ for info in run_preprocess_dataset(trainset_dir, exp_dir, sr, n_p, state):
+ yield info, button_update(visible=False), button_update(visible=True)
+ if train_task_stopped(state):
+ final_info = format_status("数据切分", "已停止")
+ except Exception:
+ final_info = format_status("数据切分", "失败", traceback.format_exc())
+ finally:
+ finish_train_task(state)
+ if final_info is None:
+ final_info = format_status("数据切分", "已完成")
+ yield final_info, button_update(visible=True), button_update(visible=False)
+
+
+def stop_preprocess_dataset():
+ return stop_train_task("数据切分")
+
+
+# but2.click(extract_f0,[gpus6,np7,f0method8,if_f0_3,trainset_dir4],[info2])
+def run_extract_f0_feature(
+ gpus,
+ n_p,
+ f0method,
+ if_f0,
+ exp_dir,
+ version19,
+ gpus_rmvpe,
+ state,
+ format_output=True,
+):
+ if f0method not in ("pm", "rmvpe"):
+ raise ValueError(i18n("仅支持pm和rmvpe音高提取算法"))
+ log_path = "%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir)
+ os.makedirs("%s/logs/%s" % (now_dir, exp_dir), exist_ok=True)
+ validate_preprocess_outputs(exp_dir)
+ with open(log_path, "w", encoding="utf8"):
+ pass
+
+ if if_f0:
+ processes = []
+ rmvpe_devices = [gpu for gpu in gpus_rmvpe.split("-") if gpu != ""]
+ if f0method == "pm" or (
+ f0method == "rmvpe" and not rmvpe_devices and not config.dml
+ ):
+ cmd = (
+ '"%s" train/dataset/extract_f0.py cpu "%s/logs/%s" %s %s'
+ % (config.python_cmd, now_dir, exp_dir, n_p, f0method)
+ )
+ processes.append(start_train_process(state, cmd))
+ elif rmvpe_devices:
+ count = len(rmvpe_devices)
+ for index, gpu in enumerate(rmvpe_devices):
+ cmd = (
+ '"%s" train/dataset/extract_f0.py cuda %s %s %s "%s/logs/%s" %s'
+ % (
+ config.python_cmd,
+ count,
+ index,
+ gpu,
+ now_dir,
+ exp_dir,
+ config.is_half,
+ )
+ )
+ processes.append(start_train_process(state, cmd))
+ else:
+ cmd = (
+ '"%s" train/dataset/extract_f0.py dml "%s/logs/%s"'
+ % (config.python_cmd, now_dir, exp_dir)
+ )
+ processes.append(start_train_process(state, cmd))
+ yield from wait_train_processes(
+ state, processes, log_path, "F0提取", format_output
+ )
+ if train_task_stopped(state):
+ return
+
+ with open(log_path, "w", encoding="utf8"):
+ pass
+
+ feature_gpus = [gpu for gpu in gpus.split("-") if gpu != ""]
+ processes = []
+ if feature_gpus:
+ count = len(feature_gpus)
+ for index, gpu in enumerate(feature_gpus):
+ cmd = (
+ '"%s" train/dataset/extract_hubert_feature.py %s %s %s %s "%s/logs/%s" %s %s'
+ % (
+ config.python_cmd,
+ config.device,
+ count,
+ index,
+ gpu,
+ now_dir,
+ exp_dir,
+ version19,
+ config.is_half,
+ )
+ )
+ processes.append(start_train_process(state, cmd))
+ else:
+ cmd = (
+ '"%s" train/dataset/extract_hubert_feature.py %s 1 0 "%s/logs/%s" %s %s'
+ % (
+ config.python_cmd,
+ config.device,
+ now_dir,
+ exp_dir,
+ version19,
+ config.is_half,
+ )
+ )
+ processes.append(start_train_process(state, cmd))
+ yield from wait_train_processes(
+ state, processes, log_path, "HuBERT特征", format_output
+ )
+ if not train_task_stopped(state):
+ validate_feature_outputs(exp_dir, version19, if_f0)
+
+
+def extract_f0_feature(gpus, n_p, f0method, if_f0, exp_dir, version19, gpus_rmvpe):
+ action, state = begin_train_task("特征提取")
+ if action == "busy":
+ yield (
+ format_status(
+ "特征提取",
+ "等待中",
+ i18n("%s运行中,请先停止该任务") % i18n(state["name"]),
+ ),
+ button_update(),
+ button_update(),
+ )
+ return
+ final_info = None
+ try:
+ yield (
+ format_status("特征提取", "正在启动"),
+ button_update(visible=False),
+ button_update(visible=True),
+ )
+ for info in run_extract_f0_feature(
+ gpus, n_p, f0method, if_f0, exp_dir, version19, gpus_rmvpe, state
+ ):
+ yield info, button_update(visible=False), button_update(visible=True)
+ if train_task_stopped(state):
+ final_info = format_status("特征提取", "已停止")
+ except Exception:
+ final_info = format_status("特征提取", "失败", traceback.format_exc())
+ finally:
+ finish_train_task(state)
+ if final_info is None:
+ final_info = format_status("特征提取", "已完成")
+ yield final_info, button_update(visible=True), button_update(visible=False)
+
+
+def stop_extract_f0_feature():
+ return stop_train_task("特征提取")
+
+def get_pretrained_models(path_str, f0_str, sr2):
+ if_pretrained_generator_exist = os.access(
+ "assets/pretrained%s/%sG%s.pth" % (path_str, f0_str, sr2), os.F_OK
+ )
+ if_pretrained_discriminator_exist = os.access(
+ "assets/pretrained%s/%sD%s.pth" % (path_str, f0_str, sr2), os.F_OK
+ )
+ if not if_pretrained_generator_exist:
+ logger.warning(
+ i18n("生成器预训练模型不存在,将不使用:assets/pretrained%s/%sG%s.pth"),
+ path_str,
+ f0_str,
+ sr2,
+ )
+ if not if_pretrained_discriminator_exist:
+ logger.warning(
+ i18n("判别器预训练模型不存在,将不使用:assets/pretrained%s/%sD%s.pth"),
+ path_str,
+ f0_str,
+ sr2,
+ )
+ return (
+ (
+ "assets/pretrained%s/%sG%s.pth" % (path_str, f0_str, sr2)
+ if if_pretrained_generator_exist
+ else ""
+ ),
+ (
+ "assets/pretrained%s/%sD%s.pth" % (path_str, f0_str, sr2)
+ if if_pretrained_discriminator_exist
+ else ""
+ ),
+ )
+
+
+def change_sr2(sr2, if_f0_3, version19):
+ path_str = "" if version19 == "v1" else "_v2"
+ f0_str = "f0" if if_f0_3 else ""
+ return get_pretrained_models(path_str, f0_str, sr2)
+
+
+def change_version19(sr2, if_f0_3, version19):
+ path_str = "" if version19 == "v1" else "_v2"
+ if sr2 == "32k" and version19 == "v1":
+ sr2 = "40k"
+ to_return_sr2 = (
+ {"choices": ["40k", "48k"], "__type__": "update", "value": sr2}
+ if version19 == "v1"
+ else {"choices": ["40k", "48k", "32k"], "__type__": "update", "value": sr2}
+ )
+ f0_str = "f0" if if_f0_3 else ""
+ return (
+ *get_pretrained_models(path_str, f0_str, sr2),
+ to_return_sr2,
+ )
+
+
+def change_f0(if_f0_3, sr2, version19): # f0method8,pretrained_G14,pretrained_D15
+ path_str = "" if version19 == "v1" else "_v2"
+ return (
+ {"visible": if_f0_3, "__type__": "update"},
+ {"visible": if_f0_3, "__type__": "update"},
+ *get_pretrained_models(path_str, "f0" if if_f0_3 == True else "", sr2),
+ )
+
+
+# but3.click(click_train,[exp_dir1,sr2,if_f0_3,save_epoch10,total_epoch11,batch_size12,if_save_latest13,pretrained_G14,pretrained_D15,gpus16])
+def run_train_model(
+ exp_dir1,
+ sr2,
+ if_f0_3,
+ spk_id5,
+ save_epoch10,
+ total_epoch11,
+ batch_size12,
+ if_save_latest13,
+ pretrained_G14,
+ pretrained_D15,
+ gpus16,
+ if_cache_gpu17,
+ if_save_every_weights18,
+ version19,
+ state,
+ format_output=True,
+):
+ # 生成filelist
+ exp_dir = "%s/logs/%s" % (now_dir, exp_dir1)
+ os.makedirs(exp_dir, exist_ok=True)
+ gt_wavs_dir = "%s/0_gt_wavs" % (exp_dir)
+ feature_dir = (
+ "%s/3_feature256" % (exp_dir)
+ if version19 == "v1"
+ else "%s/3_feature768" % (exp_dir)
+ )
+ if if_f0_3:
+ f0_dir = "%s/2a_f0" % (exp_dir)
+ f0nsf_dir = "%s/2b-f0nsf" % (exp_dir)
+ names = (
+ set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)])
+ & set([name.split(".")[0] for name in os.listdir(feature_dir)])
+ & set([name.split(".")[0] for name in os.listdir(f0_dir)])
+ & set([name.split(".")[0] for name in os.listdir(f0nsf_dir)])
+ )
+ else:
+ names = set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)]) & set(
+ [name.split(".")[0] for name in os.listdir(feature_dir)]
+ )
+ if not names:
+ raise RuntimeError(i18n("没有可用于训练的有效音频,请先完成数据切分和特征提取"))
+ opt = []
+ for name in names:
+ if if_f0_3:
+ opt.append(
+ "%s/%s.wav|%s/%s.npy|%s/%s.wav.npy|%s/%s.wav.npy|%s"
+ % (
+ gt_wavs_dir.replace("\\", "\\\\"),
+ name,
+ feature_dir.replace("\\", "\\\\"),
+ name,
+ f0_dir.replace("\\", "\\\\"),
+ name,
+ f0nsf_dir.replace("\\", "\\\\"),
+ name,
+ spk_id5,
+ )
+ )
+ else:
+ opt.append(
+ "%s/%s.wav|%s/%s.npy|%s"
+ % (
+ gt_wavs_dir.replace("\\", "\\\\"),
+ name,
+ feature_dir.replace("\\", "\\\\"),
+ name,
+ spk_id5,
+ )
+ )
+ fea_dim = 256 if version19 == "v1" else 768
+ if if_f0_3:
+ for _ in range(2):
+ opt.append(
+ "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature%s/mute.npy|%s/logs/mute/2a_f0/mute.wav.npy|%s/logs/mute/2b-f0nsf/mute.wav.npy|%s"
+ % (now_dir, sr2, now_dir, fea_dim, now_dir, now_dir, spk_id5)
+ )
+ else:
+ for _ in range(2):
+ opt.append(
+ "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature%s/mute.npy|%s"
+ % (now_dir, sr2, now_dir, fea_dim, spk_id5)
+ )
+ shuffle(opt)
+ with open("%s/filelist.txt" % exp_dir, "w", encoding="utf8") as f:
+ f.write("\n".join(opt))
+ logger.debug(i18n("训练文件列表写入完成"))
+ # 生成config#无需生成config
+ # 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"
+ logger.info(i18n("使用显卡:%s"), str(gpus16))
+ if pretrained_G14 == "":
+ logger.info(i18n("未使用生成器预训练模型"))
+ if pretrained_D15 == "":
+ logger.info(i18n("未使用判别器预训练模型"))
+ if version19 == "v1" or sr2 == "40k":
+ config_path = "v1/%s.json" % sr2
+ else:
+ config_path = "v2/%s.json" % sr2
+ config_save_path = os.path.join(exp_dir, "config.json")
+ if not pathlib.Path(config_save_path).exists():
+ with open(config_save_path, "w", encoding="utf8") as f:
+ json.dump(
+ config.json_config[config_path],
+ f,
+ ensure_ascii=False,
+ indent=4,
+ sort_keys=True,
+ )
+ f.write("\n")
+ if gpus16:
+ cmd = (
+ '"%s" train/train.py -e "%s" -sr %s -f0 %s -bs %s -g %s -te %s -se %s %s %s -l %s -c %s -sw %s -v %s'
+ % (
+ config.python_cmd,
+ exp_dir1,
+ sr2,
+ 1 if if_f0_3 else 0,
+ batch_size12,
+ gpus16,
+ total_epoch11,
+ save_epoch10,
+ "-pg %s" % pretrained_G14 if pretrained_G14 != "" else "",
+ "-pd %s" % pretrained_D15 if pretrained_D15 != "" else "",
+ 1 if if_save_latest13 == i18n("是") else 0,
+ 1 if if_cache_gpu17 == i18n("是") else 0,
+ 1 if if_save_every_weights18 == i18n("是") else 0,
+ version19,
+ )
+ )
+ else:
+ cmd = (
+ '"%s" train/train.py -e "%s" -sr %s -f0 %s -bs %s -te %s -se %s %s %s -l %s -c %s -sw %s -v %s'
+ % (
+ config.python_cmd,
+ exp_dir1,
+ sr2,
+ 1 if if_f0_3 else 0,
+ batch_size12,
+ total_epoch11,
+ save_epoch10,
+ "-pg %s" % pretrained_G14 if pretrained_G14 != "" else "",
+ "-pd %s" % pretrained_D15 if pretrained_D15 != "" else "",
+ 1 if if_save_latest13 == i18n("是") else 0,
+ 1 if if_cache_gpu17 == i18n("是") else 0,
+ 1 if if_save_every_weights18 == i18n("是") else 0,
+ version19,
+ )
+ )
+ logger.info("%s: %s", i18n("执行命令"), cmd)
+ process = start_train_process(state, cmd)
+ yield from wait_train_processes(
+ state,
+ [process],
+ os.path.join(exp_dir, "train.log"),
+ "模型训练",
+ format_output,
+ True,
+ )
+
+
+def click_train(
+ exp_dir1,
+ sr2,
+ if_f0_3,
+ spk_id5,
+ save_epoch10,
+ total_epoch11,
+ batch_size12,
+ if_save_latest13,
+ pretrained_G14,
+ pretrained_D15,
+ gpus16,
+ if_cache_gpu17,
+ if_save_every_weights18,
+ version19,
+):
+ known_models = tuple(weight_names())
+ action, state = begin_train_task("模型训练")
+ if action == "busy":
+ yield (
+ format_status(
+ "模型训练",
+ "等待中",
+ i18n("%s运行中,请先停止该任务") % i18n(state["name"]),
+ ),
+ button_update(),
+ button_update(),
+ button_update(),
+ )
+ return
+ final_info = None
+ try:
+ yield (
+ format_status("模型训练", "正在启动"),
+ button_update(visible=False),
+ button_update(visible=True),
+ button_update(),
+ )
+ for info in run_train_model(
+ exp_dir1,
+ sr2,
+ if_f0_3,
+ spk_id5,
+ save_epoch10,
+ total_epoch11,
+ batch_size12,
+ if_save_latest13,
+ pretrained_G14,
+ pretrained_D15,
+ gpus16,
+ if_cache_gpu17,
+ if_save_every_weights18,
+ version19,
+ state,
+ ):
+ known_models, model_update = refresh_weight_choices(known_models)
+ yield (
+ info,
+ button_update(visible=False),
+ button_update(visible=True),
+ model_update,
+ )
+ if train_task_stopped(state):
+ final_info = format_status("模型训练", "已停止")
+ except Exception:
+ final_info = format_status("模型训练", "失败", traceback.format_exc())
+ finally:
+ finish_train_task(state)
+ if final_info is None:
+ final_info = format_status("模型训练", "已完成")
+ model_update = change_choices()
+ yield (
+ final_info,
+ button_update(visible=True),
+ button_update(visible=False),
+ model_update,
+ )
+
+
+def stop_train_model():
+ return stop_train_task("模型训练")
+
+
+# but4.click(train_index, [exp_dir1], info3)
+def run_train_index(exp_dir1, version19, state, format_output=True):
+ exp_dir = os.path.join(now_dir, "logs", exp_dir1)
+ os.makedirs(exp_dir, exist_ok=True)
+ log_path = os.path.join(exp_dir, "train_index.log")
+ with open(log_path, "w", encoding="utf8"):
+ pass
+ cmd = (
+ '"%s" train/train_index.py "%s" %s "%s" %s'
+ % (
+ config.python_cmd,
+ exp_dir1,
+ version19,
+ outside_index_root,
+ config.n_cpu,
+ )
+ )
+ process = start_train_process(state, cmd)
+ yield from wait_train_processes(
+ state, [process], log_path, "索引训练", format_output
+ )
+
+
+def train_index(exp_dir1, version19):
+ action, state = begin_train_task("索引训练")
+ if action == "busy":
+ yield (
+ format_status(
+ "索引训练",
+ "等待中",
+ i18n("%s运行中,请先停止该任务") % i18n(state["name"]),
+ ),
+ button_update(),
+ button_update(),
+ )
+ return
+ final_info = None
+ try:
+ yield (
+ format_status("索引训练", "正在启动"),
+ button_update(visible=False),
+ button_update(visible=True),
+ )
+ for info in run_train_index(exp_dir1, version19, state):
+ yield info, button_update(visible=False), button_update(visible=True)
+ if train_task_stopped(state):
+ final_info = format_status("索引训练", "已停止")
+ except Exception:
+ final_info = format_status("索引训练", "失败", traceback.format_exc())
+ finally:
+ finish_train_task(state)
+ if final_info is None:
+ final_info = format_status("索引训练", "已完成")
+ yield final_info, button_update(visible=True), button_update(visible=False)
+
+
+def stop_train_index():
+ return stop_train_task("索引训练")
+
+# 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)
+def train1key(
+ exp_dir1,
+ sr2,
+ if_f0_3,
+ trainset_dir4,
+ spk_id5,
+ np7,
+ f0method8,
+ save_epoch10,
+ total_epoch11,
+ batch_size12,
+ if_save_latest13,
+ pretrained_G14,
+ pretrained_D15,
+ gpus16,
+ if_cache_gpu17,
+ if_save_every_weights18,
+ version19,
+ gpus_rmvpe,
+):
+ known_models = tuple(weight_names())
+ action, state = begin_train_task("一键训练")
+ if action == "busy":
+ yield (
+ format_status(
+ "一键训练",
+ "等待中",
+ i18n("%s运行中,请先停止该任务") % i18n(state["name"]),
+ ),
+ button_update(),
+ button_update(),
+ button_update(),
+ )
+ return
+
+ completed_steps = []
+ step = ""
+ final_info = None
+ running = True
+ start_button = button_update(visible=False)
+ stop_button = button_update(visible=True)
+ try:
+ yield (
+ format_status("一键训练", "正在启动"),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+
+ step = "数据切分"
+ yield (
+ format_workflow_status(step, completed_steps=completed_steps),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+ for info in run_preprocess_dataset(
+ trainset_dir4, exp_dir1, sr2, np7, state, False
+ ):
+ yield (
+ format_workflow_status(step, info, completed_steps),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+ running = not train_task_stopped(state)
+ if running:
+ completed_steps.append(step)
+
+ if running:
+ step = "F0与HuBERT特征提取"
+ yield (
+ format_workflow_status(step, completed_steps=completed_steps),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+ for info in run_extract_f0_feature(
+ gpus16,
+ np7,
+ f0method8,
+ if_f0_3,
+ exp_dir1,
+ version19,
+ gpus_rmvpe,
+ state,
+ False,
+ ):
+ yield (
+ format_workflow_status(step, info, completed_steps),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+ running = not train_task_stopped(state)
+ if running:
+ completed_steps.append(step)
+
+ if running:
+ step = "模型训练"
+ yield (
+ format_workflow_status(step, completed_steps=completed_steps),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+ for info in run_train_model(
+ exp_dir1,
+ sr2,
+ if_f0_3,
+ spk_id5,
+ save_epoch10,
+ total_epoch11,
+ batch_size12,
+ if_save_latest13,
+ pretrained_G14,
+ pretrained_D15,
+ gpus16,
+ if_cache_gpu17,
+ if_save_every_weights18,
+ version19,
+ state,
+ False,
+ ):
+ known_models, model_update = refresh_weight_choices(known_models)
+ yield (
+ format_workflow_status(step, info, completed_steps),
+ start_button,
+ stop_button,
+ model_update,
+ )
+ if not train_task_stopped(state):
+ yield (
+ format_workflow_status(step, completed_steps=completed_steps),
+ start_button,
+ stop_button,
+ change_choices(),
+ )
+ running = not train_task_stopped(state)
+ if running:
+ completed_steps.append(step)
+
+ if running:
+ step = "索引训练"
+ yield (
+ format_workflow_status(step, completed_steps=completed_steps),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+ for info in run_train_index(exp_dir1, version19, state, False):
+ yield (
+ format_workflow_status(step, info, completed_steps),
+ start_button,
+ stop_button,
+ button_update(),
+ )
+ running = not train_task_stopped(state)
+ if running:
+ completed_steps.append(step)
+
+ if not running:
+ final_info = format_workflow_status(
+ step, completed_steps=completed_steps, state="已停止"
+ )
+ else:
+ final_info = format_workflow_status(
+ "", completed_steps=completed_steps, state="已完成"
+ )
+ except Exception:
+ final_info = format_workflow_status(
+ step,
+ traceback.format_exc(),
+ completed_steps,
+ "失败",
+ )
+ finally:
+ finish_train_task(state)
+ model_update = change_choices()
+ yield (
+ final_info,
+ button_update(visible=True),
+ button_update(visible=False),
+ model_update,
+ )
+
+
+def stop_train1key():
+ return stop_train_task("一键训练")
+
+# ckpt_path2.change(change_info_,[ckpt_path2],[sr__,if_f0__])
+def change_info_(ckpt_path):
+ if not os.path.exists(ckpt_path.replace(os.path.basename(ckpt_path), "train.log")):
+ return {"__type__": "update"}, {"__type__": "update"}, {"__type__": "update"}
+ try:
+ info = eval(
+ read_text(
+ ckpt_path.replace(os.path.basename(ckpt_path), "train.log")
+ )
+ .strip("\n")
+ .split("\n")[0]
+ .split("\t")[-1]
+ )
+ sr, f0 = info["sample_rate"], info["if_f0"]
+ version = "v2" if ("version" in info and info["version"] == "v2") else "v1"
+ return sr, str(f0), version
+ except Exception:
+ traceback.print_exc()
+ return {"__type__": "update"}, {"__type__": "update"}, {"__type__": "update"}
+
+
+F0GPUVisible = IS_GPU
+
+
+def change_f0_method(f0method8):
+ if f0method8 == "rmvpe":
+ visible = F0GPUVisible
+ else:
+ visible = False
+ return {"visible": visible, "__type__": "update"}
+
+
+with gr.Blocks(title="RVC WebUI") as app:
+ gr.Markdown("## RVC WebUI")
+ gr.Markdown(
+ value=i18n(
+ "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责.
如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录LICENSE."
+ )
+ )
+ with gr.Tabs():
+ with gr.TabItem(i18n("模型推理")):
+ with gr.Row():
+ sid0 = gr.Dropdown(label=i18n("推理音色"), choices=sorted(names))
+ with gr.Column():
+ refresh_button = gr.Button(
+ i18n("刷新音色列表"), variant="primary"
+ )
+ clean_button = gr.Button(i18n("卸载音色省显存"), variant="primary")
+ spk_item = gr.Slider(
+ minimum=0,
+ maximum=2333,
+ step=1,
+ label=i18n("请选择说话人id"),
+ value=0,
+ visible=False,
+ interactive=True,
+ )
+ clean_button.click(
+ fn=clean, inputs=[], outputs=[sid0], api_name="infer_clean"
+ )
+ with gr.TabItem(i18n("单次推理")):
+ with gr.Group():
+ with gr.Row():
+ with gr.Column():
+ with gr.Row(equal_height=True):
+ with gr.Column(scale=1, min_width=120):
+ vc_transform0 = gr.Number(
+ label=i18n("变调(整数, 半音数量, 升八度12降八度-12)"),
+ value=0,
+ )
+ with gr.Column(scale=2, min_width=200):
+ f0method0 = gr.Radio(
+ label=i18n("选择音高提取算法"),
+ choices=["pm", "rmvpe", "fcpe"],
+ value="rmvpe",
+ interactive=True,
+ )
+ input_audio0 = gr.Audio(
+ label=i18n("拖拽或点击上传待处理音频"),
+ source="upload",
+ type="filepath",
+ interactive=True,
+ )
+
+ with gr.Column():
+ resample_sr0 = gr.Slider(
+ minimum=0,
+ maximum=48000,
+ label=i18n("后处理重采样至最终采样率,0为不进行重采样"),
+ value=0,
+ step=1,
+ interactive=True,
+ )
+ rms_mix_rate0 = gr.Slider(
+ minimum=0,
+ maximum=1,
+ label=i18n(
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络"
+ ),
+ value=0.25,
+ interactive=True,
+ )
+ protect0 = gr.Slider(
+ minimum=0,
+ maximum=0.5,
+ label=i18n(
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果"
+ ),
+ value=0.33,
+ step=0.01,
+ interactive=True,
+ )
+ index_rate1 = gr.Slider(
+ minimum=0,
+ maximum=1,
+ label=i18n("检索特征占比"),
+ value=0.75,
+ interactive=True,
+ )
+ file_index1 = gr.Textbox(
+ label=i18n("特征检索库文件路径(选择模型后自动匹配,可手动修改)"),
+ placeholder="C:\\Users\\Desktop\\model_example.index",
+ interactive=True,
+ )
+ refresh_button.click(
+ fn=change_choices,
+ inputs=[],
+ outputs=sid0,
+ api_name="infer_refresh",
+ )
+ with gr.Group():
+ with gr.Column():
+ but0 = gr.Button(i18n("转换"), variant="primary")
+ with gr.Row():
+ vc_output1 = gr.Textbox(label=i18n("输出信息"))
+ vc_output2 = gr.Audio(
+ label=i18n("输出音频(右下角三个点,点了可以下载)")
+ )
+
+ but0.click(
+ vc.vc_single,
+ [
+ spk_item,
+ input_audio0,
+ vc_transform0,
+ f0method0,
+ file_index1,
+ index_rate1,
+ resample_sr0,
+ rms_mix_rate0,
+ protect0,
+ ],
+ [vc_output1, vc_output2],
+ api_name="infer_convert",
+ )
+ with gr.TabItem(i18n("批量推理")):
+ gr.Markdown(
+ value=i18n(
+ "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. "
+ )
+ )
+ with gr.Row():
+ with gr.Column():
+ vc_transform1 = gr.Number(
+ label=i18n("变调(整数, 半音数量, 升八度12降八度-12)"),
+ value=0,
+ )
+ opt_input = gr.Textbox(
+ label=i18n("指定输出文件夹"), value="opt"
+ )
+ file_index3 = gr.Textbox(
+ label=i18n("特征检索库文件路径(选择模型后自动匹配,可手动修改)"),
+ value="",
+ interactive=True,
+ )
+ f0method1 = gr.Radio(
+ label=i18n("选择音高提取算法"),
+ choices=["pm", "rmvpe", "fcpe"],
+ value="rmvpe",
+ interactive=True,
+ )
+ format1 = gr.Radio(
+ label=i18n("导出文件格式"),
+ choices=["wav", "flac", "mp3", "m4a"],
+ value="wav",
+ interactive=True,
+ )
+
+ with gr.Column():
+ resample_sr1 = gr.Slider(
+ minimum=0,
+ maximum=48000,
+ label=i18n("后处理重采样至最终采样率,0为不进行重采样"),
+ value=0,
+ step=1,
+ interactive=True,
+ )
+ rms_mix_rate1 = gr.Slider(
+ minimum=0,
+ maximum=1,
+ label=i18n(
+ "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络"
+ ),
+ value=1,
+ interactive=True,
+ )
+ protect1 = gr.Slider(
+ minimum=0,
+ maximum=0.5,
+ label=i18n(
+ "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果"
+ ),
+ value=0.33,
+ step=0.01,
+ interactive=True,
+ )
+ index_rate2 = gr.Slider(
+ minimum=0,
+ maximum=1,
+ label=i18n("检索特征占比"),
+ value=1,
+ interactive=True,
+ )
+ with gr.Row():
+ dir_input = gr.Textbox(
+ label=i18n(
+ "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)"
+ ),
+ placeholder="C:\\Users\\Desktop\\input_vocal_dir",
+ )
+ inputs = gr.File(
+ file_count="multiple",
+ label=i18n("也可批量输入音频文件, 二选一, 优先读文件夹"),
+ )
+
+ with gr.Row():
+ but1 = gr.Button(i18n("转换"), variant="primary")
+ vc_output3 = gr.Textbox(label=i18n("输出信息"))
+
+ but1.click(
+ vc.vc_multi,
+ [
+ spk_item,
+ dir_input,
+ opt_input,
+ inputs,
+ vc_transform1,
+ f0method1,
+ file_index3,
+ index_rate2,
+ resample_sr1,
+ rms_mix_rate1,
+ protect1,
+ format1,
+ ],
+ [vc_output3],
+ api_name="infer_convert_batch",
+ )
+ sid0.change(
+ fn=vc.get_vc,
+ inputs=[sid0, protect0, protect1],
+ outputs=[spk_item, protect0, protect1, file_index1, file_index3],
+ api_name="infer_change_voice",
+ )
+ with gr.TabItem(i18n("伴奏人声分离&去混响&去回声")):
+ with gr.Group():
+ gr.Markdown(
+ value=i18n(
+ "人声伴奏分离批量处理,使用UVR5模型。
可选择保留人声模型,或使用DeEcho、DeReverb模型去除延迟和混响。"
+ )
+ )
+ with gr.Row():
+ with gr.Column():
+ dir_wav_input = gr.Textbox(
+ label=i18n("输入待处理音频文件夹路径"),
+ placeholder="C:\\Users\\Desktop\\todo-songs",
+ )
+ wav_inputs = gr.File(
+ file_count="multiple",
+ label=i18n("也可批量输入音频文件, 二选一, 优先读文件夹"),
+ )
+ with gr.Column():
+ model_choose = gr.Dropdown(
+ label=i18n("模型"), choices=uvr5_names
+ )
+ agg = gr.Slider(
+ minimum=0,
+ maximum=20,
+ step=1,
+ label=i18n("人声提取激进程度"),
+ value=10,
+ interactive=True,
+ visible=False, # 先不开放调整
+ )
+ opt_vocal_root = gr.Textbox(
+ label=i18n("指定输出主人声文件夹"), value="opt"
+ )
+ opt_ins_root = gr.Textbox(
+ label=i18n("指定输出非主人声文件夹"), value="opt"
+ )
+ format0 = gr.Radio(
+ label=i18n("导出文件格式"),
+ choices=["wav", "flac", "mp3", "m4a"],
+ value="flac",
+ interactive=True,
+ )
+ but2 = gr.Button(i18n("转换"), variant="primary")
+ vc_output4 = gr.Textbox(label=i18n("输出信息"))
+ but2.click(
+ uvr,
+ [
+ model_choose,
+ dir_wav_input,
+ opt_vocal_root,
+ wav_inputs,
+ opt_ins_root,
+ agg,
+ format0,
+ ],
+ [vc_output4],
+ api_name="uvr_convert",
+ )
+ with gr.TabItem(i18n("训练")):
+ gr.Markdown(
+ value=i18n(
+ "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. "
+ )
+ )
+ with gr.Row():
+ exp_dir1 = gr.Textbox(label=i18n("输入实验名"), value="mi-test")
+ sr2 = gr.Radio(
+ label=i18n("目标采样率"),
+ choices=["40k", "48k"],
+ value="40k",
+ interactive=True,
+ )
+ if_f0_3 = gr.Radio(
+ label=i18n("模型是否带音高指导(唱歌一定要, 语音可以不要)"),
+ choices=[True, False],
+ value=True,
+ interactive=True,
+ )
+ version19 = gr.Radio(
+ label=i18n("版本"),
+ choices=["v1", "v2"],
+ value="v2",
+ interactive=True,
+ visible=True,
+ )
+ np7 = gr.Slider(
+ minimum=0,
+ maximum=config.n_cpu,
+ step=1,
+ label=i18n("提取音高和处理数据使用的CPU进程数"),
+ value=int(np.ceil(config.n_cpu / 1.5)),
+ interactive=True,
+ )
+ with gr.Group(): # 暂时单人的, 后面支持最多4人的#数据处理
+ gr.Markdown(
+ value=i18n(
+ "step2a: 自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化, 在实验目录下生成2个wav文件夹; 暂时只支持单人训练. "
+ )
+ )
+ with gr.Row():
+ trainset_dir4 = gr.Textbox(
+ label=i18n("输入训练文件夹路径"),
+ value=i18n("E:\\语音音频+标注\\米津玄师\\src"),
+ )
+ spk_id5 = gr.Slider(
+ minimum=0,
+ maximum=4,
+ step=1,
+ label=i18n("请指定说话人id"),
+ value=0,
+ interactive=True,
+ )
+ but1 = gr.Button(i18n("处理数据"), variant="primary")
+ stop_but1 = gr.Button(
+ i18n("停止处理数据"), variant="stop", visible=False
+ )
+ info1 = gr.Textbox(label=i18n("输出信息"), value="")
+ but1.click(
+ preprocess_dataset,
+ [trainset_dir4, exp_dir1, sr2, np7],
+ [info1, but1, stop_but1],
+ api_name="train_preprocess",
+ )
+ stop_but1.click(
+ stop_preprocess_dataset,
+ [],
+ [info1, but1, stop_but1],
+ queue=False,
+ )
+ with gr.Group():
+ gr.Markdown(
+ value=i18n(
+ "step2b: 使用CPU提取音高(如果模型带音高), 使用GPU提取特征(选择卡号)"
+ )
+ )
+ with gr.Row():
+ with gr.Column():
+ gpus6 = gr.Textbox(
+ label=i18n(
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2"
+ ),
+ value=gpus,
+ interactive=True,
+ visible=F0GPUVisible,
+ )
+ gpu_info9 = gr.Textbox(
+ label=i18n("显卡信息"), value=gpu_info, visible=F0GPUVisible
+ )
+ with gr.Column():
+ f0method8 = gr.Radio(
+ label=i18n("选择音高提取算法"),
+ choices=["pm", "rmvpe"],
+ value="rmvpe",
+ interactive=True,
+ )
+ gpus_rmvpe = gr.Textbox(
+ label=i18n(
+ "rmvpe卡号配置:以-分隔输入使用的不同进程卡号,例如0-0-1使用在卡0上跑2个进程并在卡1上跑1个进程"
+ ),
+ value="%s-%s" % (gpus, gpus),
+ interactive=True,
+ visible=F0GPUVisible,
+ )
+ with gr.Row():
+ but2 = gr.Button(i18n("特征提取"), variant="primary")
+ stop_but2 = gr.Button(
+ i18n("停止特征提取"), variant="stop", visible=False
+ )
+ info2 = gr.Textbox(label=i18n("输出信息"), value="", max_lines=8)
+ f0method8.change(
+ fn=change_f0_method,
+ inputs=[f0method8],
+ outputs=[gpus_rmvpe],
+ )
+ but2.click(
+ extract_f0_feature,
+ [
+ gpus6,
+ np7,
+ f0method8,
+ if_f0_3,
+ exp_dir1,
+ version19,
+ gpus_rmvpe,
+ ],
+ [info2, but2, stop_but2],
+ api_name="train_extract_f0_feature",
+ )
+ stop_but2.click(
+ stop_extract_f0_feature,
+ [],
+ [info2, but2, stop_but2],
+ queue=False,
+ )
+ with gr.Group():
+ gr.Markdown(value=i18n("step3: 填写训练设置, 开始训练模型和索引"))
+ with gr.Row():
+ save_epoch10 = gr.Slider(
+ minimum=1,
+ maximum=50,
+ step=1,
+ label=i18n("保存频率save_every_epoch"),
+ value=5,
+ interactive=True,
+ )
+ total_epoch11 = gr.Slider(
+ minimum=2,
+ maximum=1200,
+ step=1,
+ label=i18n("总训练轮数total_epoch"),
+ value=20,
+ interactive=True,
+ )
+ batch_size12 = gr.Slider(
+ minimum=1,
+ maximum=40,
+ step=1,
+ label=i18n("每张显卡的batch_size"),
+ value=default_batch_size,
+ interactive=True,
+ )
+ if_save_latest13 = gr.Radio(
+ label=i18n("是否仅保存最新的ckpt文件以节省硬盘空间"),
+ choices=[i18n("是"), i18n("否")],
+ value=i18n("否"),
+ interactive=True,
+ )
+ if_cache_gpu17 = gr.Radio(
+ label=i18n(
+ "是否缓存所有训练集至显存. 10min以下小数据可缓存以加速训练, 大数据缓存会炸显存也加不了多少速"
+ ),
+ choices=[i18n("是"), i18n("否")],
+ value=i18n("否"),
+ interactive=True,
+ )
+ if_save_every_weights18 = gr.Radio(
+ label=i18n(
+ "是否在每次保存时间点将最终小模型保存至weights文件夹"
+ ),
+ choices=[i18n("是"), i18n("否")],
+ value=i18n("否"),
+ interactive=True,
+ )
+ with gr.Row():
+ pretrained_G14 = gr.Textbox(
+ label=i18n("加载预训练底模G路径"),
+ value="assets/pretrained_v2/f0G40k.pth",
+ interactive=True,
+ )
+ pretrained_D15 = gr.Textbox(
+ label=i18n("加载预训练底模D路径"),
+ value="assets/pretrained_v2/f0D40k.pth",
+ interactive=True,
+ )
+ sr2.change(
+ change_sr2,
+ [sr2, if_f0_3, version19],
+ [pretrained_G14, pretrained_D15],
+ )
+ version19.change(
+ change_version19,
+ [sr2, if_f0_3, version19],
+ [pretrained_G14, pretrained_D15, sr2],
+ )
+ if_f0_3.change(
+ change_f0,
+ [if_f0_3, sr2, version19],
+ [f0method8, gpus_rmvpe, pretrained_G14, pretrained_D15],
+ )
+ gpus16 = gr.Textbox(
+ label=i18n(
+ "以-分隔输入使用的卡号, 例如 0-1-2 使用卡0和卡1和卡2"
+ ),
+ value=gpus,
+ interactive=True,
+ )
+ but3 = gr.Button(i18n("训练模型"), variant="primary")
+ stop_but3 = gr.Button(
+ i18n("停止训练模型"), variant="stop", visible=False
+ )
+ but4 = gr.Button(i18n("训练特征索引"), variant="primary")
+ stop_but4 = gr.Button(
+ i18n("停止训练索引"), variant="stop", visible=False
+ )
+ but5 = gr.Button(i18n("一键训练"), variant="primary")
+ stop_but5 = gr.Button(
+ i18n("停止一键训练"), variant="stop", visible=False
+ )
+ info3 = gr.Textbox(label=i18n("输出信息"), value="", max_lines=10)
+ but3.click(
+ click_train,
+ [
+ exp_dir1,
+ sr2,
+ if_f0_3,
+ spk_id5,
+ save_epoch10,
+ total_epoch11,
+ batch_size12,
+ if_save_latest13,
+ pretrained_G14,
+ pretrained_D15,
+ gpus16,
+ if_cache_gpu17,
+ if_save_every_weights18,
+ version19,
+ ],
+ [info3, but3, stop_but3, sid0],
+ api_name="train_start",
+ )
+ stop_but3.click(
+ stop_train_model,
+ [],
+ [info3, but3, stop_but3],
+ queue=False,
+ )
+ but4.click(
+ train_index,
+ [exp_dir1, version19],
+ [info3, but4, stop_but4],
+ )
+ stop_but4.click(
+ stop_train_index,
+ [],
+ [info3, but4, stop_but4],
+ queue=False,
+ )
+ but5.click(
+ train1key,
+ [
+ exp_dir1,
+ sr2,
+ if_f0_3,
+ trainset_dir4,
+ spk_id5,
+ np7,
+ f0method8,
+ save_epoch10,
+ total_epoch11,
+ batch_size12,
+ if_save_latest13,
+ pretrained_G14,
+ pretrained_D15,
+ gpus16,
+ if_cache_gpu17,
+ if_save_every_weights18,
+ version19,
+ gpus_rmvpe,
+ ],
+ [info3, but5, stop_but5, sid0],
+ api_name="train_start_all",
+ )
+ stop_but5.click(
+ stop_train1key,
+ [],
+ [info3, but5, stop_but5],
+ queue=False,
+ )
+
+ with gr.TabItem(i18n("ckpt处理")):
+ with gr.Group():
+ gr.Markdown(value=i18n("模型融合, 可用于测试音色融合"))
+ with gr.Row():
+ ckpt_a = gr.Textbox(
+ label=i18n("A模型路径"), value="", interactive=True
+ )
+ ckpt_b = gr.Textbox(
+ label=i18n("B模型路径"), value="", interactive=True
+ )
+ alpha_a = gr.Slider(
+ minimum=0,
+ maximum=1,
+ label=i18n("A模型权重"),
+ value=0.5,
+ interactive=True,
+ )
+ with gr.Row():
+ sr_ = gr.Radio(
+ label=i18n("目标采样率"),
+ choices=["40k", "48k"],
+ value="40k",
+ interactive=True,
+ )
+ if_f0_ = gr.Radio(
+ label=i18n("模型是否带音高指导"),
+ choices=[i18n("是"), i18n("否")],
+ value=i18n("是"),
+ interactive=True,
+ )
+ info__ = gr.Textbox(
+ label=i18n("要置入的模型信息"),
+ value="",
+ max_lines=8,
+ interactive=True,
+ )
+ name_to_save0 = gr.Textbox(
+ label=i18n("保存的模型名不带后缀"),
+ value="",
+ max_lines=1,
+ interactive=True,
+ )
+ version_2 = gr.Radio(
+ label=i18n("模型版本型号"),
+ choices=["v1", "v2"],
+ value="v1",
+ interactive=True,
+ )
+ with gr.Row():
+ but6 = gr.Button(i18n("融合"), variant="primary")
+ info4 = gr.Textbox(label=i18n("输出信息"), value="", max_lines=8)
+ but6.click(
+ merge,
+ [
+ ckpt_a,
+ ckpt_b,
+ alpha_a,
+ sr_,
+ if_f0_,
+ info__,
+ name_to_save0,
+ version_2,
+ ],
+ info4,
+ api_name="ckpt_merge",
+ ) # def merge(path1,path2,alpha1,sr,f0,info):
+ with gr.Group():
+ gr.Markdown(
+ value=i18n("修改模型信息(仅支持weights文件夹下提取的小模型文件)")
+ )
+ with gr.Row():
+ ckpt_path0 = gr.Textbox(
+ label=i18n("模型路径"), value="", interactive=True
+ )
+ info_ = gr.Textbox(
+ label=i18n("要改的模型信息"),
+ value="",
+ max_lines=8,
+ interactive=True,
+ )
+ name_to_save1 = gr.Textbox(
+ label=i18n("保存的文件名, 默认空为和源文件同名"),
+ value="",
+ max_lines=8,
+ interactive=True,
+ )
+ with gr.Row():
+ but7 = gr.Button(i18n("修改"), variant="primary")
+ info5 = gr.Textbox(label=i18n("输出信息"), value="", max_lines=8)
+ but7.click(
+ change_info,
+ [ckpt_path0, info_, name_to_save1],
+ info5,
+ api_name="ckpt_modify",
+ )
+ with gr.Group():
+ gr.Markdown(
+ value=i18n("查看模型信息(仅支持weights文件夹下提取的小模型文件)")
+ )
+ with gr.Row():
+ ckpt_path1 = gr.Textbox(
+ label=i18n("模型路径"), value="", interactive=True
+ )
+ but8 = gr.Button(i18n("查看"), variant="primary")
+ info6 = gr.Textbox(label=i18n("输出信息"), value="", max_lines=8)
+ but8.click(show_info, [ckpt_path1], info6, api_name="ckpt_show")
+ with gr.Group():
+ gr.Markdown(
+ value=i18n(
+ "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况"
+ )
+ )
+ with gr.Row():
+ ckpt_path2 = gr.Textbox(
+ label=i18n("模型路径"),
+ value="E:\\codes\\py39\\logs\\mi-test_f0_48k\\G_23333.pth",
+ interactive=True,
+ )
+ save_name = gr.Textbox(
+ label=i18n("保存名"), value="", interactive=True
+ )
+ sr__ = gr.Radio(
+ label=i18n("目标采样率"),
+ choices=["32k", "40k", "48k"],
+ value="40k",
+ interactive=True,
+ )
+ if_f0__ = gr.Radio(
+ label=i18n("模型是否带音高指导,1是0否"),
+ choices=["1", "0"],
+ value="1",
+ interactive=True,
+ )
+ version_1 = gr.Radio(
+ label=i18n("模型版本型号"),
+ choices=["v1", "v2"],
+ value="v2",
+ interactive=True,
+ )
+ info___ = gr.Textbox(
+ label=i18n("要置入的模型信息"),
+ value="",
+ max_lines=8,
+ interactive=True,
+ )
+ but9 = gr.Button(i18n("提取"), variant="primary")
+ info7 = gr.Textbox(label=i18n("输出信息"), value="", max_lines=8)
+ ckpt_path2.change(
+ change_info_, [ckpt_path2], [sr__, if_f0__, version_1]
+ )
+ but9.click(
+ extract_small_model,
+ [ckpt_path2, save_name, sr__, if_f0__, info___, version_1],
+ info7,
+ api_name="ckpt_extract",
+ )
+
+ tab_faq = i18n("常见问题解答")
+ with gr.TabItem(tab_faq):
+ try:
+ if tab_faq == "常见问题解答":
+ info = read_text("docs/cn/faq.md")
+ else:
+ info = read_text("docs/en/faq_en.md")
+ gr.Markdown(value=info)
+ except Exception:
+ gr.Markdown(traceback.format_exc())
+
+ if config.iscolab:
+ app.queue(concurrency_count=511, max_size=1022).launch(share=True)
+ else:
+ launch_webui_with_port_fallback(app, config)