voice-changer/server/voice_changer/RVC/RVC.py

249 lines
9.7 KiB
Python
Raw Normal View History

2023-04-05 20:31:10 +03:00
import sys
import os
2023-05-04 17:50:52 +03:00
from dataclasses import asdict
import numpy as np
import torch
2023-05-28 07:54:57 +03:00
import torchaudio
2023-06-17 04:08:32 +03:00
from data.ModelSlot import RVCModelSlot
2023-05-02 14:57:12 +03:00
2023-05-02 16:29:28 +03:00
2023-05-02 14:57:12 +03:00
# avoiding parse arg error in RVC
sys.argv = ["MMVCServerSIO.py"]
if sys.platform.startswith("darwin"):
baseDir = [x for x in sys.path if x.endswith("Contents/MacOS")]
if len(baseDir) != 1:
print("baseDir should be only one ", baseDir)
sys.exit()
modulePath = os.path.join(baseDir[0], "RVC")
sys.path.append(modulePath)
else:
sys.path.append("RVC")
2023-05-04 17:50:52 +03:00
2023-06-21 03:18:51 +03:00
2023-04-28 01:36:08 +03:00
from voice_changer.RVC.RVCSettings import RVCSettings
2023-05-02 06:11:00 +03:00
from voice_changer.RVC.embedder.EmbedderManager import EmbedderManager
2023-07-01 10:45:25 +03:00
from voice_changer.utils.VoiceChangerModel import AudioInOut, PitchfInOut, FeatureInOut, VoiceChangerModel
2023-04-27 17:38:25 +03:00
from voice_changer.utils.VoiceChangerParams import VoiceChangerParams
2023-05-04 09:20:36 +03:00
from voice_changer.RVC.onnxExporter.export2onnx import export2onnx
2023-05-04 17:50:52 +03:00
from voice_changer.RVC.pitchExtractor.PitchExtractorManager import PitchExtractorManager
from voice_changer.RVC.pipeline.PipelineGenerator import createPipeline
from voice_changer.RVC.deviceManager.DeviceManager import DeviceManager
from voice_changer.RVC.pipeline.Pipeline import Pipeline
2023-04-13 02:00:28 +03:00
2023-06-21 03:18:51 +03:00
from Exceptions import DeviceCannotSupportHalfPrecisionException
2023-04-05 20:31:10 +03:00
2023-06-21 03:18:51 +03:00
class RVC(VoiceChangerModel):
2023-06-21 01:23:13 +03:00
def __init__(self, params: VoiceChangerParams, slotInfo: RVCModelSlot):
2023-06-21 03:18:51 +03:00
print("[Voice Changer] [RVC] Creating instance ")
2023-06-21 10:00:03 +03:00
self.deviceManager = DeviceManager.get_instance()
2023-05-04 16:46:42 +03:00
EmbedderManager.initialize(params)
2023-06-21 10:00:03 +03:00
self.settings = RVCSettings()
2023-06-21 01:23:13 +03:00
self.params = params
self.pitchExtractor = PitchExtractorManager.getPitchExtractor(self.settings.f0Detector)
2023-06-17 04:08:32 +03:00
2023-06-21 10:00:03 +03:00
self.pipeline: Pipeline | None = None
self.audio_buffer: AudioInOut | None = None
2023-07-01 10:45:25 +03:00
self.pitchf_buffer: PitchfInOut | None = None
self.feature_buffer: FeatureInOut | None = None
2023-05-30 20:26:16 +03:00
self.prevVol = 0.0
2023-06-21 01:23:13 +03:00
self.slotInfo = slotInfo
self.initialize()
def initialize(self):
2023-06-21 03:18:51 +03:00
print("[Voice Changer] [RVC] Initializing... ")
2023-05-16 04:38:23 +03:00
2023-06-21 01:23:13 +03:00
# pipelineの生成
self.pipeline = createPipeline(self.slotInfo, self.settings.gpu, self.settings.f0Detector)
# その他の設定
2023-06-21 03:18:51 +03:00
self.settings.tran = self.slotInfo.defaultTune
self.settings.indexRatio = self.slotInfo.defaultIndexRatio
self.settings.protect = self.slotInfo.defaultProtect
print("[Voice Changer] [RVC] Initializing... done")
2023-06-19 05:40:16 +03:00
2023-04-28 02:01:15 +03:00
def update_settings(self, key: str, val: int | float | str):
2023-06-21 01:23:13 +03:00
print("[Voice Changer][RVC]: update_settings", key, val)
2023-05-02 14:57:12 +03:00
if key in self.settings.intData:
2023-06-21 01:23:13 +03:00
setattr(self.settings, key, int(val))
2023-05-04 11:15:53 +03:00
if key == "gpu":
self.deviceManager.setForceTensor(False)
2023-06-21 01:23:13 +03:00
self.initialize()
2023-04-05 20:31:10 +03:00
elif key in self.settings.floatData:
setattr(self.settings, key, float(val))
elif key in self.settings.strData:
setattr(self.settings, key, str(val))
2023-05-04 11:15:53 +03:00
if key == "f0Detector" and self.pipeline is not None:
pitchExtractor = PitchExtractorManager.getPitchExtractor(self.settings.f0Detector)
2023-05-04 11:15:53 +03:00
self.pipeline.setPitchExtractor(pitchExtractor)
2023-04-05 20:31:10 +03:00
else:
return False
return True
def get_info(self):
data = asdict(self.settings)
2023-05-31 08:30:35 +03:00
if self.pipeline is not None:
pipelineInfo = self.pipeline.getPipelineInfo()
data["pipelineInfo"] = pipelineInfo
2023-04-05 20:31:10 +03:00
return data
def get_processing_sampling_rate(self):
2023-06-21 03:18:51 +03:00
return self.slotInfo.samplingRate
2023-04-05 20:31:10 +03:00
2023-04-27 17:38:25 +03:00
def generate_input(
2023-04-28 02:01:15 +03:00
self,
newData: AudioInOut,
inputSize: int,
crossfadeSize: int,
solaSearchFrame: int = 0,
2023-04-27 17:38:25 +03:00
):
newData = newData.astype(np.float32) / 32768.0 # RVCのモデルのサンプリングレートで入ってきている。extraDataLength, Crossfade等も同じSRで処理(★1)
2023-04-14 03:18:34 +03:00
2023-07-01 10:45:25 +03:00
new_feature_length = newData.shape[0] * 100 // self.slotInfo.samplingRate
2023-04-28 02:01:15 +03:00
if self.audio_buffer is not None:
# 過去のデータに連結
self.audio_buffer = np.concatenate([self.audio_buffer, newData], 0)
2023-07-01 10:45:25 +03:00
if self.slotInfo.f0:
self.pitchf_buffer = np.concatenate([self.pitchf_buffer, np.zeros(new_feature_length)], 0)
self.feature_buffer = np.concatenate([self.feature_buffer, np.zeros([new_feature_length, self.slotInfo.embChannels])], 0)
2023-04-14 03:18:34 +03:00
else:
self.audio_buffer = newData
2023-07-01 10:45:25 +03:00
if self.slotInfo.f0:
self.pitchf_buffer = np.zeros(new_feature_length)
self.feature_buffer = np.zeros([new_feature_length, self.slotInfo.embChannels])
2023-04-14 03:18:34 +03:00
convertSize = inputSize + crossfadeSize + solaSearchFrame + self.settings.extraConvertSize
2023-04-14 03:18:34 +03:00
if convertSize % 128 != 0: # モデルの出力のホップサイズで切り捨てが発生するので補う。
convertSize = convertSize + (128 - (convertSize % 128))
2023-07-01 06:06:14 +03:00
outSize = convertSize - self.settings.extraConvertSize
2023-06-02 17:48:36 +03:00
# バッファがたまっていない場合はzeroで補う
if self.audio_buffer.shape[0] < convertSize:
self.audio_buffer = np.concatenate([np.zeros([convertSize]), self.audio_buffer])
2023-07-01 10:45:25 +03:00
if self.slotInfo.f0:
self.pitchf_buffer = np.concatenate([np.zeros([convertSize * 100 // self.slotInfo.samplingRate]), self.pitchf_buffer])
self.feature_buffer = np.concatenate([np.zeros([convertSize * 100 // self.slotInfo.samplingRate, self.slotInfo.embChannels]), self.feature_buffer])
2023-04-14 03:18:34 +03:00
2023-04-28 02:01:15 +03:00
convertOffset = -1 * convertSize
2023-07-01 10:45:25 +03:00
featureOffset = -convertSize * 100 // self.slotInfo.samplingRate
2023-04-28 02:01:15 +03:00
self.audio_buffer = self.audio_buffer[convertOffset:] # 変換対象の部分だけ抽出
2023-07-01 10:45:25 +03:00
if self.slotInfo.f0:
self.pitchf_buffer = self.pitchf_buffer[featureOffset:]
self.feature_buffer = self.feature_buffer[featureOffset:]
2023-04-28 02:01:15 +03:00
# 出力部分だけ切り出して音量を確認。(TODO:段階的消音にする)
cropOffset = -1 * (inputSize + crossfadeSize)
cropEnd = -1 * (crossfadeSize)
2023-07-01 10:45:25 +03:00
crop = self.audio_buffer[cropOffset:cropEnd]
vol = np.sqrt(np.square(crop).mean())
2023-05-28 07:54:57 +03:00
vol = max(vol, self.prevVol * 0.0)
2023-04-14 03:18:34 +03:00
self.prevVol = vol
2023-07-01 10:45:25 +03:00
return (self.audio_buffer, self.pitchf_buffer, self.feature_buffer, convertSize, vol, outSize)
2023-04-05 20:31:10 +03:00
def inference(self, data):
2023-05-02 19:11:03 +03:00
audio = data[0]
2023-07-01 10:45:25 +03:00
pitchf = data[1]
feature = data[2]
convertSize = data[3]
vol = data[4]
outSize = data[5]
2023-05-02 19:11:03 +03:00
if vol < self.settings.silentThreshold:
2023-07-01 06:06:14 +03:00
return np.zeros(convertSize).astype(np.int16) * np.sqrt(vol)
2023-05-02 19:11:03 +03:00
2023-07-01 10:45:25 +03:00
if self.pipeline is not None:
device = self.pipeline.device
else:
device = torch.device("cpu")
audio = torch.from_numpy(audio).to(device=device, dtype=torch.float32)
2023-06-21 03:18:51 +03:00
audio = torchaudio.functional.resample(audio, self.slotInfo.samplingRate, 16000, rolloff=0.99)
repeat = 1 if self.settings.rvcQuality else 0
2023-07-04 00:50:58 +03:00
sid = self.settings.dstId
2023-05-02 19:11:03 +03:00
f0_up_key = self.settings.tran
index_rate = self.settings.indexRatio
2023-06-01 07:28:45 +03:00
protect = self.settings.protect
2023-06-17 04:08:32 +03:00
2023-06-21 01:23:13 +03:00
if_f0 = 1 if self.slotInfo.f0 else 0
embOutputLayer = self.slotInfo.embOutputLayer
useFinalProj = self.slotInfo.useFinalProj
2023-06-17 04:08:32 +03:00
try:
2023-07-01 10:45:25 +03:00
audio_out, self.pitchf_buffer, self.feature_buffer = self.pipeline.exec(
sid,
audio,
2023-07-01 10:45:25 +03:00
pitchf,
feature,
f0_up_key,
index_rate,
if_f0,
2023-07-01 10:45:25 +03:00
self.settings.extraConvertSize / self.slotInfo.samplingRate if self.settings.silenceFront else 0., # extaraDataSizeの秒数。RVCのモデルのサンプリングレートで処理(★1)。
embOutputLayer,
useFinalProj,
repeat,
protect,
2023-07-01 06:06:14 +03:00
outSize
)
result = audio_out.detach().cpu().numpy() * np.sqrt(vol)
return result
except DeviceCannotSupportHalfPrecisionException as e:
print("[Device Manager] Device cannot support half precision. Fallback to float....")
self.deviceManager.setForceTensor(True)
self.prepareModel(self.settings.modelSlotIndex)
raise e
2023-05-02 19:11:03 +03:00
return
2023-04-05 20:31:10 +03:00
2023-04-10 18:21:17 +03:00
def __del__(self):
2023-05-04 11:15:53 +03:00
del self.pipeline
2023-04-10 18:21:17 +03:00
2023-06-21 01:23:13 +03:00
print("---------- REMOVING ---------------")
2023-04-29 01:05:44 +03:00
2023-04-10 18:21:17 +03:00
remove_path = os.path.join("RVC")
2023-04-28 02:01:15 +03:00
sys.path = [x for x in sys.path if x.endswith(remove_path) is False]
2023-04-10 18:21:17 +03:00
for key in list(sys.modules):
val = sys.modules.get(key)
try:
file_path = val.__file__
2023-04-11 01:37:39 +03:00
if file_path.find("RVC" + os.path.sep) >= 0:
2023-05-30 20:26:16 +03:00
# print("remove", key, file_path)
2023-04-10 18:21:17 +03:00
sys.modules.pop(key)
2023-04-29 01:05:44 +03:00
except Exception: # type:ignore
# print(e)
2023-04-10 18:21:17 +03:00
pass
2023-04-13 02:00:28 +03:00
def export2onnx(self):
2023-06-23 08:54:39 +03:00
modelSlot = self.slotInfo
2023-04-22 09:12:10 +03:00
2023-05-08 19:01:20 +03:00
if modelSlot.isONNX:
2023-04-13 02:00:28 +03:00
print("[Voice Changer] export2onnx, No pyTorch filepath.")
2023-04-28 02:01:15 +03:00
return {"status": "ng", "path": ""}
2023-04-13 02:00:28 +03:00
2023-05-04 09:20:36 +03:00
output_file_simple = export2onnx(self.settings.gpu, modelSlot)
2023-04-27 17:38:25 +03:00
return {
"status": "ok",
"path": f"/tmp/{output_file_simple}",
"filename": output_file_simple,
}
2023-04-30 20:34:01 +03:00
2023-06-21 01:23:13 +03:00
def get_model_current(self):
return [
{
"key": "defaultTune",
"val": self.settings.tran,
},
{
"key": "defaultIndexRatio",
"val": self.settings.indexRatio,
},
{
"key": "defaultProtect",
"val": self.settings.protect,
},
]