RvcPyInfer 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- RvcPyInfer/InferProviders.py +108 -0
- RvcPyInfer/InferTask.py +210 -0
- RvcPyInfer/RvcContext.py +125 -0
- RvcPyInfer/__init__.py +42 -0
- RvcPyInfer/__main__.py +4 -0
- RvcPyInfer/_version.py +1 -0
- RvcPyInfer/audio/audio_utils.py +346 -0
- RvcPyInfer/cli.py +112 -0
- RvcPyInfer/error/InferEnvError.py +2 -0
- RvcPyInfer/error/NotSupportedAlgorithmError.py +2 -0
- RvcPyInfer/f0_utils.py +125 -0
- RvcPyInfer/index/RvcFeatIndex.py +19 -0
- RvcPyInfer/infer_env.py +28 -0
- RvcPyInfer/onnx/ContentVec.py +38 -0
- RvcPyInfer/onnx/ModelSimplePool.py +26 -0
- RvcPyInfer/onnx/RvcGen.py +51 -0
- RvcPyInfer/onnx/export/Exporter.py +130 -0
- RvcPyInfer/onnx/export/cli.py +104 -0
- RvcPyInfer/onnx/export/direct_read_sr.py +109 -0
- RvcPyInfer/onnx/model_loader.py +41 -0
- RvcPyInfer/ov/OVCoreSingleton.py +3 -0
- RvcPyInfer/path_utils.py +10 -0
- RvcPyInfer/resource/template/export_onnx.py +249 -0
- RvcPyInfer/type_alist.py +13 -0
- RvcPyInfer/warn/InferEnvWarn.py +5 -0
- RvcPyInfer/warn/InferModelWarn.py +5 -0
- RvcPyInfer/warn/InferWarn.py +2 -0
- rvcpyinfer-0.1.0.dist-info/METADATA +158 -0
- rvcpyinfer-0.1.0.dist-info/RECORD +31 -0
- rvcpyinfer-0.1.0.dist-info/WHEEL +4 -0
- rvcpyinfer-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from enum import Flag, auto
|
|
2
|
+
|
|
3
|
+
from .error.InferEnvError import InferEnvError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class InferProviders(Flag):
|
|
7
|
+
NONE = auto() # 仅用于内部方便处理
|
|
8
|
+
|
|
9
|
+
# ort
|
|
10
|
+
ORT_CPU = auto()
|
|
11
|
+
FORCE_CUDA = auto()
|
|
12
|
+
FORCE_DML = auto()
|
|
13
|
+
CUDA = ORT_CPU | FORCE_CUDA
|
|
14
|
+
DML = ORT_CPU | FORCE_DML
|
|
15
|
+
|
|
16
|
+
# openvino
|
|
17
|
+
OPENVINO_AUTO = auto()
|
|
18
|
+
OPENVINO_CPU = auto()
|
|
19
|
+
OPENVINO_GPU = auto()
|
|
20
|
+
OPENVINO_NPU = auto()
|
|
21
|
+
OPENVINO_OPTIONAL_MULTI = auto()
|
|
22
|
+
OPENVINO_OPTIONAL_HETERO = auto()
|
|
23
|
+
OPENVINO_ALL = OPENVINO_CPU | OPENVINO_GPU | OPENVINO_NPU | OPENVINO_OPTIONAL_MULTI
|
|
24
|
+
|
|
25
|
+
def is_ort(self) -> bool:
|
|
26
|
+
return bool((InferProviders.ORT_CPU | InferProviders.CUDA | InferProviders.DML) & self)
|
|
27
|
+
def is_ov(self) -> bool:
|
|
28
|
+
return bool((InferProviders.OPENVINO_CPU | InferProviders.OPENVINO_GPU | InferProviders.OPENVINO_NPU | InferProviders.OPENVINO_AUTO) & self)
|
|
29
|
+
|
|
30
|
+
def get_onnx_provider(self) -> list[str]:
|
|
31
|
+
res = []
|
|
32
|
+
if self.FORCE_CUDA in self:
|
|
33
|
+
res.append("CUDAExecutionProvider")
|
|
34
|
+
if self.FORCE_DML in self:
|
|
35
|
+
res.append("DmlExecutionProvider")
|
|
36
|
+
if self.ORT_CPU in self:
|
|
37
|
+
res.append("CPUExecutionProvider")
|
|
38
|
+
return res
|
|
39
|
+
|
|
40
|
+
def get_openvino_device(self) -> str:
|
|
41
|
+
devices: list[str] = []
|
|
42
|
+
if self.OPENVINO_GPU in self:
|
|
43
|
+
# 现场找全
|
|
44
|
+
from openvino import Core # pyright: ignore[reportMissingImports]
|
|
45
|
+
devices.extend(filter(lambda name: name.startswith("GPU"), Core().available_devices))
|
|
46
|
+
if self.OPENVINO_NPU in self:
|
|
47
|
+
devices.append("NPU")
|
|
48
|
+
if self.OPENVINO_CPU in self:
|
|
49
|
+
devices.append("CPU")
|
|
50
|
+
devices_str = ",".join(devices)
|
|
51
|
+
if self.OPENVINO_AUTO in self:
|
|
52
|
+
if len(devices) != 0:
|
|
53
|
+
return f"AUTO:{devices_str}"
|
|
54
|
+
else:
|
|
55
|
+
return "AUTO"
|
|
56
|
+
elif self.OPENVINO_OPTIONAL_MULTI in self:
|
|
57
|
+
return f"MULTI:{devices_str}"
|
|
58
|
+
elif self.OPENVINO_OPTIONAL_HETERO in self:
|
|
59
|
+
return f"HETERO:{devices_str}"
|
|
60
|
+
else:
|
|
61
|
+
return devices[0]
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def default() -> "InferProviders":
|
|
65
|
+
available = InferProviders.currently_available()
|
|
66
|
+
|
|
67
|
+
if InferProviders.FORCE_CUDA in available:
|
|
68
|
+
return InferProviders.CUDA
|
|
69
|
+
elif InferProviders.FORCE_DML in available:
|
|
70
|
+
return InferProviders.DML
|
|
71
|
+
elif InferProviders.OPENVINO_CPU in available:
|
|
72
|
+
# 有时候自动的异构并行反而比纯 CPU 还慢,这里保守点
|
|
73
|
+
return InferProviders.OPENVINO_CPU
|
|
74
|
+
elif InferProviders.ORT_CPU in available:
|
|
75
|
+
return InferProviders.ORT_CPU
|
|
76
|
+
else:
|
|
77
|
+
raise InferEnvError("没有任意一个支持的推理引擎,请至少安装一个可选模块")
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def currently_available() -> "InferProviders":
|
|
81
|
+
from .infer_env import HAS_ONE, HAS_OPENVINO, HAS_ORT
|
|
82
|
+
res = InferProviders.NONE
|
|
83
|
+
|
|
84
|
+
if HAS_ORT:
|
|
85
|
+
import onnxruntime as ort # pyright: ignore[reportMissingImports]
|
|
86
|
+
available_providers = ort.get_available_providers()
|
|
87
|
+
if "CPUExecutionProvider" in available_providers:
|
|
88
|
+
res = res | InferProviders.ORT_CPU
|
|
89
|
+
if "CUDAExecutionProvider" in available_providers:
|
|
90
|
+
res = res | InferProviders.FORCE_CUDA
|
|
91
|
+
if "DmlExecutionProvider" in available_providers:
|
|
92
|
+
res = res | InferProviders.FORCE_DML
|
|
93
|
+
if HAS_OPENVINO:
|
|
94
|
+
from openvino import Core # pyright: ignore[reportMissingImports]
|
|
95
|
+
devices = Core().available_devices
|
|
96
|
+
res = res | InferProviders.OPENVINO_AUTO | InferProviders.OPENVINO_OPTIONAL_MULTI | InferProviders.OPENVINO_OPTIONAL_HETERO
|
|
97
|
+
if "CPU" in devices:
|
|
98
|
+
res = res | InferProviders.OPENVINO_CPU
|
|
99
|
+
if any(i.startswith("GPU") for i in devices):
|
|
100
|
+
res = res | InferProviders.OPENVINO_GPU
|
|
101
|
+
if "NPU" in devices:
|
|
102
|
+
res = res | InferProviders.OPENVINO_NPU
|
|
103
|
+
if not HAS_ONE:
|
|
104
|
+
raise InferEnvError("没有任意一个支持的推理引擎,请至少安装一个可选模块")
|
|
105
|
+
|
|
106
|
+
assert res != InferProviders.NONE, "我算是知道了,你们各个都身怀绝技"
|
|
107
|
+
res = res & ~InferProviders.NONE
|
|
108
|
+
return res
|
RvcPyInfer/InferTask.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import soundfile
|
|
7
|
+
from numpy.typing import NDArray
|
|
8
|
+
|
|
9
|
+
from .audio.audio_utils import crossfade, reSR, rms_frame_match, split_by_max_len_with_overlap, split_by_silence
|
|
10
|
+
from .f0_utils import apply_rise_tone, build_f0extract_func, f0_to_mel, normalized_mel
|
|
11
|
+
from .type_alist import Audio, AudioLike, F0ExtractAlgorithm, F0ExtractAlgorithmList, FileLike
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .RvcContext import RvcContext
|
|
15
|
+
|
|
16
|
+
class InferTask:
|
|
17
|
+
audios: list[Audio]
|
|
18
|
+
def __init__(self,
|
|
19
|
+
# -- 必填 --
|
|
20
|
+
context: "RvcContext",
|
|
21
|
+
vec_model: Path,
|
|
22
|
+
gen_model: tuple[Path, int],
|
|
23
|
+
*audios: AudioLike,
|
|
24
|
+
|
|
25
|
+
# 可选扩展
|
|
26
|
+
sid: int = 0,
|
|
27
|
+
seed: int | None = 1234,
|
|
28
|
+
|
|
29
|
+
# -- 特征索引 --
|
|
30
|
+
index_path: Path | None = None,
|
|
31
|
+
index_rate: float = 0.33,
|
|
32
|
+
index_k: int = 8, # 检索的最近特征数量
|
|
33
|
+
|
|
34
|
+
# -- f0 提取 --
|
|
35
|
+
f0extract_algorithm: F0ExtractAlgorithm,
|
|
36
|
+
f0_up_semitone: float = 0,
|
|
37
|
+
f0_min: float = 50,
|
|
38
|
+
f0_max: float = 1100,
|
|
39
|
+
|
|
40
|
+
# -- 强制切片与交叉淡化 --
|
|
41
|
+
slice_max_len: int = 30,
|
|
42
|
+
slice_overlap_len: int = 5,
|
|
43
|
+
|
|
44
|
+
# -- 静音切片 --
|
|
45
|
+
silence_frame_len: int = 20,
|
|
46
|
+
silence_hop_len: int = 10,
|
|
47
|
+
silence_thresh_db: float = -40,
|
|
48
|
+
silence_min_silence_duration_ms: float = 800.0,
|
|
49
|
+
silence_max_transition_ms: float = 100.0,
|
|
50
|
+
|
|
51
|
+
# -- RMS 包络匹配 --
|
|
52
|
+
rms_match_frame_len: int = 20,
|
|
53
|
+
rms_match_hop_len: int = 10,
|
|
54
|
+
rms_match_mix: float = 1.0, # 一般不用改
|
|
55
|
+
) -> None:
|
|
56
|
+
self.context = context
|
|
57
|
+
self.vec = vec_model.resolve()
|
|
58
|
+
self.gen = (gen_model[0].resolve(), gen_model[1])
|
|
59
|
+
self.audio_likes = list(audios)
|
|
60
|
+
self.sid = sid
|
|
61
|
+
self.seed = seed
|
|
62
|
+
self.index_path = index_path
|
|
63
|
+
self.index_rate = index_rate
|
|
64
|
+
self.index_k = index_k
|
|
65
|
+
self.f0extract_algorithm = f0extract_algorithm
|
|
66
|
+
self.f0_up_semitone = f0_up_semitone
|
|
67
|
+
self.f0_min = f0_min
|
|
68
|
+
self.f0_max = f0_max
|
|
69
|
+
self.slice_max_len = slice_max_len
|
|
70
|
+
self.slice_overlap_len = slice_overlap_len
|
|
71
|
+
self.silence_frame_len = silence_frame_len
|
|
72
|
+
self.silence_hop_len = silence_hop_len
|
|
73
|
+
self.silence_thresh_db = silence_thresh_db
|
|
74
|
+
self.silence_min_silence_duration_ms = silence_min_silence_duration_ms
|
|
75
|
+
self.silence_max_transition_ms = silence_max_transition_ms
|
|
76
|
+
self.rms_match_frame_len = rms_match_frame_len
|
|
77
|
+
self.rms_match_hop_len = rms_match_hop_len
|
|
78
|
+
self.rms_match_mix = rms_match_mix
|
|
79
|
+
|
|
80
|
+
def read(self) -> None:
|
|
81
|
+
self.audios = []
|
|
82
|
+
for audio_like in self.audio_likes:
|
|
83
|
+
if isinstance(audio_like, tuple) and len(audio_like) == 2 and isinstance(audio_like[0], np.ndarray) and isinstance(audio_like[1], int):
|
|
84
|
+
self.audios.append(audio_like)
|
|
85
|
+
else:
|
|
86
|
+
assert not isinstance(audio_like, tuple) # 这个类型检查真的很奇怪有没有
|
|
87
|
+
data, sr = soundfile.read(
|
|
88
|
+
audio_like, dtype="float32"
|
|
89
|
+
)
|
|
90
|
+
data = data.astype(np.float32)
|
|
91
|
+
if len(data.shape) != 1:
|
|
92
|
+
data = data.mean(axis=1, dtype=np.float32)
|
|
93
|
+
self.audios.append((data, sr))
|
|
94
|
+
del self.audio_likes
|
|
95
|
+
|
|
96
|
+
def core_chunk_infer(self, audio: Audio, f0extract_func: Callable[[Audio, int], NDArray[np.float32]]) -> Audio:
|
|
97
|
+
model = self.context._vec_pool.get(self.vec)
|
|
98
|
+
feats = model.infer(audio)
|
|
99
|
+
|
|
100
|
+
f0 = f0extract_func(audio, feats.shape[0]) # 这里是没有批处理维度的
|
|
101
|
+
f0 = apply_rise_tone(f0, self.f0_up_semitone)
|
|
102
|
+
|
|
103
|
+
mel = normalized_mel(f0_to_mel(f0), f0_to_mel(self.f0_min), f0_to_mel(self.f0_max))
|
|
104
|
+
mel = np.rint(mel).astype(np.int64)
|
|
105
|
+
|
|
106
|
+
if self.index_rate > 1e-6 and self.index_path is not None and self.context._index_pool is not None:
|
|
107
|
+
index = self.context._index_pool.get(self.index_path)
|
|
108
|
+
feats = index.apply_index(feats, self.index_rate, self.index_k)
|
|
109
|
+
|
|
110
|
+
model = self.context._gen_pool.get(self.gen)
|
|
111
|
+
res = model.infer(
|
|
112
|
+
phone=feats,
|
|
113
|
+
f0_pitch=f0, mel_pitch=mel,
|
|
114
|
+
sid=self.sid, seed=self.seed
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
res_d, res_sr = res
|
|
118
|
+
source_d, source_sr = audio
|
|
119
|
+
|
|
120
|
+
source_len = len(source_d)
|
|
121
|
+
res_len = source_len * res_sr // source_sr
|
|
122
|
+
|
|
123
|
+
pad_len = res_len - len(res_d)
|
|
124
|
+
if pad_len == 0:
|
|
125
|
+
return res
|
|
126
|
+
elif pad_len < 0:
|
|
127
|
+
return (res_d[:res_len], res_sr)
|
|
128
|
+
else:
|
|
129
|
+
return (
|
|
130
|
+
np.pad(res_d, [0, pad_len], mode="constant"),
|
|
131
|
+
res_sr
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def chunk_infer(self, audio: Audio, f0extract_func: Callable[[Audio, int], NDArray[np.float32]]) -> Audio:
|
|
135
|
+
splited = split_by_max_len_with_overlap( # 就算不足一个切片长也不会报错哒
|
|
136
|
+
audio,
|
|
137
|
+
max_len=self.slice_max_len,
|
|
138
|
+
overlap_len=self.slice_overlap_len
|
|
139
|
+
)
|
|
140
|
+
del audio # 节约点内存
|
|
141
|
+
def handle(c):
|
|
142
|
+
return self.core_chunk_infer(c, f0extract_func)
|
|
143
|
+
infer = [handle(i) for i in splited]
|
|
144
|
+
res = crossfade(
|
|
145
|
+
infer,
|
|
146
|
+
overlap_len=self.slice_overlap_len
|
|
147
|
+
)
|
|
148
|
+
return res
|
|
149
|
+
|
|
150
|
+
def gen_infer(self, callback: Callable[[int, Audio], None]) -> None:
|
|
151
|
+
assert self.f0extract_algorithm in F0ExtractAlgorithmList, f"{self.f0extract_algorithm} 不是受支持的 f0 提取算法"
|
|
152
|
+
func = build_f0extract_func(self.f0extract_algorithm, self.f0_min, self.f0_max) # pyright: ignore[reportArgumentType]
|
|
153
|
+
for i, audio in enumerate(self.audios):
|
|
154
|
+
splited = split_by_silence(
|
|
155
|
+
audio,
|
|
156
|
+
frame_len=self.silence_frame_len,
|
|
157
|
+
hop_len=self.silence_hop_len,
|
|
158
|
+
silence_thresh_db=self.silence_thresh_db,
|
|
159
|
+
min_silence_duration_ms=self.silence_min_silence_duration_ms,
|
|
160
|
+
max_transition_ms=self.silence_max_transition_ms
|
|
161
|
+
)
|
|
162
|
+
chunks: list[Audio] = []
|
|
163
|
+
for chunk, is_sil in splited:
|
|
164
|
+
if is_sil:
|
|
165
|
+
chunks.append(reSR(chunk, target_sr=self.gen[1])) # 内部判断相等会直接返回
|
|
166
|
+
else:
|
|
167
|
+
chunks.append(self.chunk_infer(chunk, func))
|
|
168
|
+
datas = [data for data, sr in chunks]
|
|
169
|
+
res = (np.concatenate(datas), self.gen[1])
|
|
170
|
+
res = rms_frame_match(
|
|
171
|
+
source=res,
|
|
172
|
+
target=reSR(audio, target_sr=res[1]),
|
|
173
|
+
frame_len=self.rms_match_frame_len,
|
|
174
|
+
hop_len=self.rms_match_hop_len,
|
|
175
|
+
mix=self.rms_match_mix
|
|
176
|
+
)
|
|
177
|
+
callback(i, res)
|
|
178
|
+
|
|
179
|
+
def run(self) -> list[Audio]:
|
|
180
|
+
self.read()
|
|
181
|
+
|
|
182
|
+
res = []
|
|
183
|
+
self.gen_infer(
|
|
184
|
+
lambda _, a: res.append(a)
|
|
185
|
+
)
|
|
186
|
+
return res
|
|
187
|
+
|
|
188
|
+
def run_and_save(self, *output: FileLike,
|
|
189
|
+
subtype: str | None = None,
|
|
190
|
+
format: str | None = None):
|
|
191
|
+
self.read()
|
|
192
|
+
|
|
193
|
+
if len(self.audios) != len(output):
|
|
194
|
+
raise ValueError(f"音频数量 {len(self.audios)} 与输出数量 {len(output)} 不匹配")
|
|
195
|
+
def save(i: int, a: Audio) -> None:
|
|
196
|
+
d, s = a
|
|
197
|
+
soundfile.write(output[i],
|
|
198
|
+
d, s,
|
|
199
|
+
subtype=subtype,
|
|
200
|
+
format=format)
|
|
201
|
+
self.gen_infer(
|
|
202
|
+
save
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def run_and_callback(self,
|
|
206
|
+
callback: Callable[[int, Audio], None]) -> None:
|
|
207
|
+
self.read()
|
|
208
|
+
self.gen_infer(
|
|
209
|
+
callback=callback
|
|
210
|
+
)
|
RvcPyInfer/RvcContext.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from .index.RvcFeatIndex import RvcFeatIndex
|
|
5
|
+
from .InferProviders import InferProviders
|
|
6
|
+
from .InferTask import InferTask
|
|
7
|
+
from .onnx.ContentVec import ContentVec
|
|
8
|
+
from .onnx.ModelSimplePool import ModelSimplePool
|
|
9
|
+
from .onnx.RvcGen import RvcGen
|
|
10
|
+
from .path_utils import path
|
|
11
|
+
from .type_alist import AudioLike, F0ExtractAlgorithm, PathLike
|
|
12
|
+
from .warn.InferEnvWarn import InferEnvWarn
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# 请注意,RvcContext 并不是线程安全的
|
|
16
|
+
class RvcContext:
|
|
17
|
+
def __init__(self,
|
|
18
|
+
providers: InferProviders = InferProviders.default(),
|
|
19
|
+
vec_pool_permanent_size: int = 1,
|
|
20
|
+
gen_pool_permanent_size: int = 2,
|
|
21
|
+
index_pool_permanent_size: int = 2) -> None:
|
|
22
|
+
self._providers = providers
|
|
23
|
+
self._vec_pool = ModelSimplePool[Path, ContentVec](
|
|
24
|
+
lambda p: ContentVec(p.resolve(), self._providers),
|
|
25
|
+
vec_pool_permanent_size
|
|
26
|
+
)
|
|
27
|
+
self._gen_pool = ModelSimplePool[tuple[Path, int], RvcGen](
|
|
28
|
+
lambda args: RvcGen(args[0].resolve(), args[1], self._providers),
|
|
29
|
+
gen_pool_permanent_size,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from .infer_env import HAS_FAISS
|
|
33
|
+
if HAS_FAISS:
|
|
34
|
+
self._index_pool = ModelSimplePool[Path, RvcFeatIndex](
|
|
35
|
+
lambda p: RvcFeatIndex(p.resolve()),
|
|
36
|
+
index_pool_permanent_size
|
|
37
|
+
)
|
|
38
|
+
else:
|
|
39
|
+
self._index_pool = None
|
|
40
|
+
|
|
41
|
+
def clear_pool(self) -> None:
|
|
42
|
+
self._vec_pool.clear()
|
|
43
|
+
self._gen_pool.clear()
|
|
44
|
+
|
|
45
|
+
def build_task(self,
|
|
46
|
+
# -- 必填 --
|
|
47
|
+
vec_model: PathLike,
|
|
48
|
+
gen_model: PathLike,
|
|
49
|
+
gen_model_sr: int,
|
|
50
|
+
*audios: AudioLike,
|
|
51
|
+
|
|
52
|
+
# 可选扩展
|
|
53
|
+
sid: int = 0,
|
|
54
|
+
seed: int | None = 1234,
|
|
55
|
+
|
|
56
|
+
# -- 特征索引 --
|
|
57
|
+
index_path: PathLike | None = None,
|
|
58
|
+
index_rate: float = 0.33,
|
|
59
|
+
index_k: int = 8, # 检索的最近特征数量
|
|
60
|
+
|
|
61
|
+
# -- f0 提取 --
|
|
62
|
+
f0extract_algorithm: F0ExtractAlgorithm = "dio",
|
|
63
|
+
f0_up_semitone: float = 0,
|
|
64
|
+
f0_min: float = 50,
|
|
65
|
+
f0_max: float = 1100,
|
|
66
|
+
|
|
67
|
+
# -- 强制切片与交叉淡化 --
|
|
68
|
+
slice_max_len: int = 30,
|
|
69
|
+
slice_overlap_len: int = 5,
|
|
70
|
+
|
|
71
|
+
# -- 静音切片 --
|
|
72
|
+
silence_frame_len: int = 20,
|
|
73
|
+
silence_hop_len: int = 10,
|
|
74
|
+
silence_thresh_db: float = -40,
|
|
75
|
+
silence_min_silence_duration_ms: float = 800.0,
|
|
76
|
+
silence_max_transition_ms: float = 100.0,
|
|
77
|
+
|
|
78
|
+
# -- RMS 包络匹配 --
|
|
79
|
+
rms_match_frame_len: int = 20,
|
|
80
|
+
rms_match_hop_len: int = 10,
|
|
81
|
+
rms_match_mix: float = 1.0, # 一般不用改
|
|
82
|
+
) -> "InferTask":
|
|
83
|
+
vec = path(vec_model)
|
|
84
|
+
gen = path(gen_model)
|
|
85
|
+
|
|
86
|
+
if not vec.exists():
|
|
87
|
+
raise FileNotFoundError(f"找不到模型: {vec}")
|
|
88
|
+
if not gen.exists():
|
|
89
|
+
raise FileNotFoundError(f"找不到模型: {gen}")
|
|
90
|
+
|
|
91
|
+
if index_path is not None:
|
|
92
|
+
from .infer_env import HAS_FAISS
|
|
93
|
+
if not HAS_FAISS:
|
|
94
|
+
warnings.warn("您正在使用特征索引,但并未安装 faiss。如果您想使用特征索引请安装 [index] 可选依赖", InferEnvWarn)
|
|
95
|
+
index_path = None
|
|
96
|
+
else:
|
|
97
|
+
index_path = path(index_path)
|
|
98
|
+
if not index_path.exists():
|
|
99
|
+
raise FileNotFoundError(f"找不到特征索引: {index_path}")
|
|
100
|
+
|
|
101
|
+
return InferTask(
|
|
102
|
+
self,
|
|
103
|
+
vec,
|
|
104
|
+
(gen, gen_model_sr),
|
|
105
|
+
*audios,
|
|
106
|
+
sid=sid,
|
|
107
|
+
seed=seed,
|
|
108
|
+
index_path=index_path,
|
|
109
|
+
index_rate=index_rate,
|
|
110
|
+
index_k=index_k,
|
|
111
|
+
f0extract_algorithm=f0extract_algorithm,
|
|
112
|
+
f0_up_semitone=f0_up_semitone,
|
|
113
|
+
f0_min=f0_min,
|
|
114
|
+
f0_max=f0_max,
|
|
115
|
+
slice_max_len=slice_max_len,
|
|
116
|
+
slice_overlap_len=slice_overlap_len,
|
|
117
|
+
silence_frame_len=silence_frame_len,
|
|
118
|
+
silence_hop_len=silence_hop_len,
|
|
119
|
+
silence_thresh_db=silence_thresh_db,
|
|
120
|
+
silence_min_silence_duration_ms=silence_min_silence_duration_ms,
|
|
121
|
+
silence_max_transition_ms=silence_max_transition_ms,
|
|
122
|
+
rms_match_frame_len=rms_match_frame_len,
|
|
123
|
+
rms_match_hop_len=rms_match_hop_len,
|
|
124
|
+
rms_match_mix=rms_match_mix,
|
|
125
|
+
)
|
RvcPyInfer/__init__.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from . import f0_utils
|
|
2
|
+
from ._version import __version__ # noqa: F401
|
|
3
|
+
from .audio import audio_utils
|
|
4
|
+
from .cli import main as ModelInferCLI
|
|
5
|
+
from .error.InferEnvError import InferEnvError
|
|
6
|
+
from .error.NotSupportedAlgorithmError import NotSupportedAlgorithmError
|
|
7
|
+
from .InferProviders import InferProviders
|
|
8
|
+
from .InferTask import InferTask
|
|
9
|
+
from .onnx.ContentVec import ContentVec
|
|
10
|
+
from .onnx.export import Exporter as OnnxExporter
|
|
11
|
+
from .onnx.export.cli import main as ModelExportToolCLI
|
|
12
|
+
from .onnx.export.direct_read_sr import direct_read_sr
|
|
13
|
+
from .onnx.ModelSimplePool import ModelSimplePool
|
|
14
|
+
from .onnx.RvcGen import RvcGen
|
|
15
|
+
from .RvcContext import RvcContext
|
|
16
|
+
from .type_alist import Audio, AudioLike, F0ExtractAlgorithm, F0ExtractAlgorithmList, FileLike, PathLike
|
|
17
|
+
from .warn.InferEnvWarn import InferEnvWarn
|
|
18
|
+
from .warn.InferModelWarn import InferModelWarn
|
|
19
|
+
from .warn.InferWarn import InferWarn
|
|
20
|
+
|
|
21
|
+
# 用户肯定不会希望我的 __version__ 去污染 ta 的版本号
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Audio", "AudioLike", "PathLike", "FileLike", "F0ExtractAlgorithm", "F0ExtractAlgorithmList",
|
|
25
|
+
"InferEnvError",
|
|
26
|
+
"NotSupportedAlgorithmError",
|
|
27
|
+
"InferWarn",
|
|
28
|
+
"InferEnvWarn",
|
|
29
|
+
"InferModelWarn",
|
|
30
|
+
"f0_utils",
|
|
31
|
+
"audio_utils",
|
|
32
|
+
"InferProviders",
|
|
33
|
+
"RvcContext",
|
|
34
|
+
"InferTask",
|
|
35
|
+
"OnnxExporter",
|
|
36
|
+
"direct_read_sr",
|
|
37
|
+
"ModelExportToolCLI",
|
|
38
|
+
"ContentVec",
|
|
39
|
+
"RvcGen",
|
|
40
|
+
"ModelSimplePool",
|
|
41
|
+
"ModelInferCLI"
|
|
42
|
+
]
|
RvcPyInfer/__main__.py
ADDED
RvcPyInfer/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|