TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

Python から VST2 インストゥルメント(VSTi)を読み込んで音を鳴らす

前回、録音した音声にSwiftF0で疑似ラベルを付けて、決定木を学習することを試した。

学習したモデルを使うことで、アコースティックギター、エレキギター、エレキベースで、安定してピッチ推定が行えるようになった。

ギター、ベース以外にも様々な楽器のピッチ推定精度を上げるために、様々な楽器の複数のピッチでの音声データが欲しい。

物理的に楽器を鳴らしてマイクで収録するのは、非常にコストと時間がかかるため、VST2 インストゥルメント(VSTi)を使うことを考える。

VST2 インストゥルメント(VSTi)

CubaseなどのDAWで読み込みできる楽器のプラグインである。
様々な楽器のVST2 インストゥルメントが販売されている。
最近はVST3に移行している。

Native InstrumentsのKontaktは、プロの音楽制作現場で業界標準として使用されているサンプルベースのソフトウェア音源である。
膨大な音色が用意されている。
だいぶ古いバージョンだが、Kontakt5を購入しているので、Kontakt5を使って訓練データを生成する。

VSTiが扱えるPythonライブラリ

VSTiが扱えるPythonライブラリを探したところ、DawDreamerで、VST インストゥルメント/エフェクトの読み込み・処理が可能であることがわかった。
エフェクトにも対応しているので、EQで加工するなどしてデータ拡張することもできる。

なお、WindowsではPython 3.11までしか対応していないので、uvでPythonバージョン指定でインストールした。

再生プログラム

VST インストゥルメントを読み込んで、WAVファイルに保存するサンプルプログラムは以下の通り。

import os
import numpy as np
import soundfile as sf
import dawdreamer as daw

SAMPLE_RATE = 44100
BUFFER_SIZE = 256

KONTAKT_DLL = r"C:\Program Files\Native Instruments\VSTPlugins 64 bit\Kontakt 5.dll"
STATE_PATH  = r".\kontakt5_instrument.state" # 一度作ると以後使い回せる
OUT_WAV     = r".\out_16bit.wav"
RENDER_SEC  = 6.0

engine = daw.RenderEngine(SAMPLE_RATE, BUFFER_SIZE)
engine.set_bpm(120.0)

kontakt = engine.make_plugin_processor("Kontakt5", KONTAKT_DLL)

# --- 1) 音色ロード済みstateが無ければ、UIで手動ロードしてstate保存 ---
if not os.path.exists(STATE_PATH):
    print("STATEが無いのでKontakt UIを開きます。Kontakt上でNKI等をロードしてからUIを閉じてください。")
    kontakt.open_editor()     # UIが開く(閉じた後に復帰する想定)
    kontakt.save_state(STATE_PATH)
    print(f"Saved state -> {STATE_PATH}")

# --- 2) stateをロードして、MIDIを流してレンダリング ---
kontakt.load_state(STATE_PATH)

kontakt.add_midi_note(60, 100, 0.0, 2.0)
kontakt.add_midi_note(64, 100, 1.0, 1.5)
kontakt.add_midi_note(67, 100, 2.5, 2.0)

engine.load_graph([(kontakt, [])])
engine.render(RENDER_SEC)

audio = engine.get_audio()  # shape: (channels, samples)
st = audio[:2, :]

# (samples, channels) にして 16-bit PCM で書く
st_sf = np.clip(st.T, -1.0, 1.0).astype(np.float32)
sf.write(OUT_WAV, st_sf, SAMPLE_RATE, subtype="PCM_16")

注意点

音色指定

音色は、UIで選択する必要がある。
調べた限りではコードから指定する方法はなかった。
一度選択するとstateを保存して、次回からはそのsateをロードできる。

出力チャンネル

出力は、64チャンネルになる。
そのままWAVに保存しても再生できないため、ch1-2を抽出している。

まとめ

PythonライブラリDawDreamerでVSTiを制御しWAVを書き出す方法について記載した。
次はフィルタを適用する方法を調べたい。