SoftVC VITS 歌声変換と OpenVINO™

この Jupyter ノートブックは、ローカルへのインストール後にのみ起動できます。

GitHub

このチュートリアルは、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 フロー内でいくつかのモデル (TextEncoderGeneratorResidualCouplingBlock など) を使用しますが、この場合、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/