SoftVC VITS 歌声変換と OpenVINO™¶
この Jupyter ノートブックは、ローカルへのインストール後にのみ起動できます。
このチュートリアルは、SoftVC VITS 歌声変換プロジェクトに基づいています。このプロジェクトの目的は、開発者自身が愛するアニメ・キャラクターに歌のタスクを実行できるようにすることでした。開発者の意図は、架空の人物に焦点を当て、実在の人物を避けることであり、実在の人物に関連するものはすべて、開発者の意図からそれているとします。
歌声変換モデルでは、SoftVC コンテンツ・エンコーダーを使用してソースオーディオから音声特徴を抽出します。これらの特徴ベクトルは、テキストベースの中間表現へ変換せずに、直接 VITS に供給されます。その結果、元のオーディオのピッチとイントネーションが維持されます。
このチュートリアルでは、基本モデルのフローを使用します。
目次¶
必要条件¶
%pip install -q --upgrade pip setuptools
%pip install -q "openvino>=2023.2.0"
!git clone https://github.com/svc-develop-team/so-vits-svc -b 4.1-Stable
%pip install -q --extra-index-url https://download.pytorch.org/whl/cpu tqdm librosa "torch>=2.1.0" "torchaudio>=2.1.0" faiss-cpu gradio "numpy==1.23.5" "fairseq==0.12.2" praat-parselmouth
事前トレーニングされたモデルと構成をダウンロードします。推奨エンコーダー ContentVec と、Pony Preservation Project によって作成された so-vits-svc-4.0 モデル例のコレクションからのモデルを使用します。このプロジェクトまたは別のプロジェクトから他の事前トレーニング済みモデルを選択することも、独自のモデルを準備することもできます。
# Fetch `notebook_utils` module
import urllib.request
urllib.request.urlretrieve(
url='https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/main/notebooks/utils/notebook_utils.py',
filename='notebook_utils.py'
)
from notebook_utils import download_file
# ContentVec
download_file("https://huggingface.co/lj1995/VoiceConversionWebUI/resolve/main/hubert_base.pt", "checkpoint_best_legacy_500.pt", directory="so-vits-svc/pretrain/")
# pretrained models and configs from a collection of so-vits-svc-4.0 models. You can use other models.
download_file("https://huggingface.co/therealvul/so-vits-svc-4.0/resolve/main/Rainbow%20Dash%20(singing)/kmeans_10000.pt", "kmeans_10000.pt", directory="so-vits-svc/logs/44k/")
download_file("https://huggingface.co/therealvul/so-vits-svc-4.0/resolve/main/Rainbow%20Dash%20(singing)/config.json", "config.json", directory="so-vits-svc/configs/")
download_file("https://huggingface.co/therealvul/so-vits-svc-4.0/resolve/main/Rainbow%20Dash%20(singing)/G_30400.pth", "G_30400.pth", directory="so-vits-svc/logs/44k/")
download_file("https://huggingface.co/therealvul/so-vits-svc-4.0/resolve/main/Rainbow%20Dash%20(singing)/D_30400.pth", "D_30400.pth", directory="so-vits-svc/logs/44k/")
# a wav sample
download_file("https://huggingface.co/datasets/santifiorino/spinetta/resolve/main/spinetta/000.wav", "000.wav", directory="so-vits-svc/raw/")
元のモデルを使用して推論を実行¶
内部相対パスを壊さないように、ディレクトリーを so-vits-svc
に変更します。
%cd so-vits-svc
ソヴィッツモデルを定義します。
from inference.infer_tool import Svc
model = Svc("logs/44k/G_30400.pth", "configs/config.json", device='cpu')
kwargs
を定義し、推論を行います。
kwargs = {
'raw_audio_path': 'raw/000.wav', # path to a source audio
'spk': 'Rainbow Dash (singing)', # speaker ID in which the source audio should be converted.
'tran': 0,
'slice_db': -40,
'cluster_infer_ratio': 0,
'auto_predict_f0': False,
'noice_scale': 0.4,
}
audio = model.slice_inference(**kwargs)
そして、元のオーディオと結果を比較します。
import IPython.display as ipd
# original
ipd.Audio("raw/000.wav", rate=model.target_sample)
# result
ipd.Audio(audio, rate=model.target_sample)
OpenVINO IR モデルへ変換¶
モデル・コンポーネントは PyTorch モジュールであり、ov.convert_model
関数を使用して直接変換できます。ov.save_model
関数を使用して変換結果をシリアル化します。Svc
はモデルではなく、内部でモデル推論を行います。基本シナリオでは、net_g_ms
という名前の SynthesizerTrn
のみが使用されます。このモデルのみを変換するだけで十分であるため、infer
メソッドに forward
メソッドを再割り当てする必要があります。
SynthesizerTrn
フロー内でいくつかのモデル (TextEncoder
、Generator
、ResidualCouplingBlock
など) を使用しますが、この場合、OpenVINO を使用すると、内部を確認せずに 1 ステップでパイプライン全体を変換できます。
import openvino as ov
import torch
from pathlib import Path
dummy_c = torch.randn(1, 256, 813)
dummy_f0 = torch.randn(1, 813)
dummy_uv = torch.ones(1, 813)
dummy_g = torch.tensor([[0]])
model.net_g_ms.forward = model.net_g_ms.infer
net_g_kwargs = {
'c': dummy_c,
'f0': dummy_f0,
'uv': dummy_uv,
'g': dummy_g,
'noice_scale': torch.tensor(0.35), # need to wrap numeric and boolean values for conversion
'seed': torch.tensor(52468),
'predict_f0': torch.tensor(False),
'vol': torch.tensor(0)
}
core = ov.Core()
net_g_model_xml_path = Path('models/ov_net_g_model.xml')
if not net_g_model_xml_path.exists():
converted_model = ov.convert_model(model.net_g_ms, example_input=net_g_kwargs)
net_g_model_xml_path.parent.mkdir(parents=True, exist_ok=True)
ov.save_model(converted_model, net_g_model_xml_path)
OpenVINO モデルの実行¶
OpenVINO を使用して推論を実行するデバイスをドロップダウン・リストから選択します。
import ipywidgets as widgets
import openvino as ov
core = ov.Core()
device = widgets.Dropdown(
options=core.available_devices + ["AUTO"],
value='AUTO',
description='Device:',
disabled=False,
)
device
net_g_ms
モデルのインターフェイスを維持するには、そのモデルのラッパーを作成する必要があります。次に、net_g_ms
の元のモデルを変換された IR モデルで置き換えます。ov.compile_model
を使用して、デバイスへロードできるようにします。
class NetGModelWrapper:
def __init__(self, net_g_model_xml_path):
super().__init__()
self.net_g_model = core.compile_model(net_g_model_xml_path, device.value)
def infer(self, c, *, f0, uv, g, noice_scale=0.35, seed=52468, predict_f0=False, vol=None):
if vol is None: # None is not allowed as an input
results = self.net_g_model((c, f0, uv, g, noice_scale, seed, predict_f0))
else:
results = self.net_g_model((c, f0, uv, g, noice_scale, seed, predict_f0, vol))
return torch.from_numpy(results[0]), torch.from_numpy(results[1])
model.net_g_ms = NetGModelWrapper(net_g_model_xml_path)
audio = model.slice_inference(**kwargs)
結果をチェックします。元のモデルによって作成されたものと同じですか?
import IPython.display as ipd
ipd.Audio(audio, rate=model.target_sample)
インタラクティブな推論¶
import gradio as gr
src_audio = gr.Audio(label="Source Audio", type='filepath')
output_audio = gr.Audio(label="Output Audio", type='numpy')
title = 'SoftVC VITS Singing Voice Conversion with Gradio'
description = f'Gradio Demo for SoftVC VITS Singing Voice Conversion and OpenVINO™. Upload a source audio, then click the "Submit" button to inference. Audio sample rate should be {model.target_sample}'
def infer(src_audio, tran, slice_db, noice_scale):
kwargs["raw_audio_path"] = src_audio
kwargs["tran"] = tran
kwargs["slice_db"] = slice_db
kwargs["noice_scale"] = noice_scale
audio = model.slice_inference(**kwargs)
return model.target_sample, audio
demo = gr.Interface(
infer,
[
src_audio,
gr.Slider(-100, 100, value=0, label="Pitch shift", step=1),
gr.Slider(-80, -20, value=-30, label="Slice db", step=10, info="The default is -30, noisy audio can be -30, dry sound can be -50 to preserve breathing."),
gr.Slider(0, 1, value=0.4, label="Noise scale", step=0.1, info="Noise level will affect pronunciation and sound quality, which is more metaphysical"),
],
output_audio,
title=title,
description=description,
examples=[['raw/000.wav', 0, -30, 0.4, False]]
)
try:
demo.queue().launch(debug=False)
except Exception:
demo.queue().launch(share=True, debug=False)
# if you are launching remotely, specify server_name and server_port
# demo.launch(server_name='your server name', server_port='server port in int')
# Read more in the docs: https://gradio.app/docs/