はじめてのVST3プラグイン
前回はVST3のプラグインを作成する際のVisual Studioのプロジェクト作成について記載いたしましたので、今回はさっそくVST3プラグインを作成します。
今回作成するプラグインは下記のとおりです。
- パラメーターはなし
- 入力をそのまま出力する
- 入力バス、出力バスは1つで、共にステレオ(2ch)
上記にある「バス」についての概念は次回あたりで記載できればと思います。
このVST3プラグインのサンプルソースファイルはこちらからダウンロードできます→vst3dev_20210403
ZIPファイルの中の「vst3dev01_はじめてのVST3プラグイン」フォルダが今回のサンプルソースファイルになります。
コンパイル・ビルドの方法は簡単にこちらでご説明しております。ご参考までに。→サンプルソースファイルのビルド方法
はじめてのVST3プラグイン作成の流れ
はじめてのVST3プラグインでは、主に下記の3つを実装します。
- FUIDの作成
- プラグイン情報の設定
- 音声処理クラスの作成
ここで作成するはじめてのVST3プラグインのサンプルは実行時に一部のホストアプリケーション(Sonar、VSTHost等のDAW)でエラー(例外)が発生する場合がございます。
エラー発生の際は「VST3プラグインのパラメーター実装方法1」まで進めていただき、再度試していただきますようお願いします。
FUIDの作成
VST3プラグイン開発ではFUIDというIDが必要となります。
VST3はMicrosoftのCOM(Component Object Model)と呼ばれる仕様(規格)を参考に設計されており、このCOMではグローバル一意識別子(GUID)と呼ばれるIDをクラスに関連付けます。
VST3でもCOMと同様にFUIDと呼ばれるIDを音声処理クラスと次々回するパラメーター操作クラスに関連付ける決まりになっています。
FUIDはGUID生成ツールを使用して取得する必要があります。
Visual StudioにもGUID生成ツールが付属しているため、下記の手順で取得します。
- Visual Studioのメニューから「ツール」→「GUIDの作成」を選択。
- 「4.レジストリ形式」にチェックを入れ「コピー」ボタンを押す
- クリップボードにGUIDがコピーされるので、メモ帳などに貼り付ける。
※GUID生成ツールを立ち上げるか、「新規GUID」ボタンを押すと、新しいGUIDが生成されます。
取得したFUIDはグローバルのFUIDクラスに設定します。取得したGUIDとFUIDは区切りが異なるため、下記のようにGUIDを8桁ずつ区切ってFUIDクラスの引数にします。
【例】
発行されたGUIDが
{01234567-89AB-CDEF-0123-456789ABCDEF}
の場合、FUIDクラスの引数には
FUID ProcessorUID (0x01234567, 0x89ABCDEF, 0x01234567, 0x89ABCDEF);
と8桁ずつ区切ったものを指定する。
実際のコードは下記のとおりです。(VST3の開発では名前空間を指定する必要がありますので忘れないようご注意ください。)
【myvst3fuid.h】
1 2 3 4 5 6 7 8 9 10 11 |
// VST3 SDKのインクルードファイル #include "pluginterfaces\base\funknown.h" // VST3作成に必要なの名前空間を使用 namespace Steinberg{ namespace Vst { // FUIDの生成 static const FUID ProcessorUID (0x1E5620F0, 0xB0C54434, 0x93A6D2E6, 0x793DF84E); } } // namespace SteinbergとVstの終わり |
VST3プラグインを作成する際にFUIDは使いまわさないように注意してください。
同じFUIDのVST3プラグインが複数あると作ったVST3プラグインを正常に認識してくれない場合があります。
プラグイン情報の設定
次にVST3プラグイン情報の設定を行います。
ここで設定する内容はホストアプリケーション(DAWなど)が音声処理クラスやパラメーター操作クラスを呼び出すための情報になります。
一部の内容はホストアプリケーション(DAWなど)に表示されることがあります。
VST3プラグイン情報はマクロを使って下記のように定義・作成します。
(製作者情報などは#define定義していますが、BEGIN_FACTORY_DEFマクロやDEF_CLASS2に直接指定しても問題ありません。)
【factory.cpp】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// VST3 SDKのインクルードファイル #include "public.sdk/source/main/pluginfactoryvst3.h" // 自作VSTのヘッダファイルをインクルード #include "myvst3fuid.h" #include "processor.h" // 製作者(製作会社)の名前。終端文字「\0」含めて64文字まで。 #define MYVST_VENDOR "C++ de VST" // 製作者(製作会社)のWebサイトのURL。終端文字「\0」含めて256文字まで。 #define MYVST_URL "https://www.utsbox.com/" // 製作者(製作会社)の連絡先メールアドレス。終端文字「\0」含めて128文字まで。 #define MYVST_EMAIL "mailto:xxxx@example.com" // 自作するVSTの名前。終端文字「\0」含めて64文字まで。 #define MYVST_VSTNAME "MyVST Name" // 自作するVSTのバージョン。終端文字「\0」含めて64文字まで。 #define MYVST_VERSION "0" // 自作するVSTのカテゴリ。終端文字「\0」含めて64文字まで。 #define MYVST_SUBCATEGORIES Vst::PlugType::kFx // =================================================================================== // DLLファイルの初期化、終了処理関数 // =================================================================================== // 基本的に何もする必要はない。(VST SDK 3.7.1より前のバージョンの場合はコメントを外すこと) //bool InitModule() { return true; } //bool DeinitModule() { return true; } // =================================================================================== // 自作VSTプラグインの生成 // =================================================================================== BEGIN_FACTORY_DEF(MYVST_VENDOR, MYVST_URL, MYVST_EMAIL) // MyVSTProcessorクラスの作成を行う DEF_CLASS2(INLINE_UID_FROM_FUID(Steinberg::Vst::ProcessorUID), PClassInfo::kManyInstances, kVstAudioEffectClass, MYVST_VSTNAME, Vst::kDistributable, MYVST_SUBCATEGORIES, MYVST_VERSION, kVstVersionString, Steinberg::Vst::MyVSTProcessor::createInstance ) END_FACTORY |
それぞれのマクロの概要については下記の通りです。
- BEGIN_FACTORY_DEFマクロ
概要 | VST3プラグイン情報をホストアプリケーションに知らせるためのクラス(CPluginFactoryクラス)を作成するマクロ。 このマクロの後にDEF_CLASS2マクロを使用して音声処理クラス等の情報を設定する。 最後はEND_FACTORYマクロを呼び出す。 |
|
---|---|---|
引数 | 1つ目 | VST3プラグイン製作者の名前。 |
2つ目 | VST3プラグインに関するのURL。 | |
3つ目 | VST3プラグインに連絡先eMailアドレス。 |
- DEF_CLASS2マクロ
概要 | VST3プラグイン内にある音声処理クラス・パラメーター操作クラスの情報を設定するマクロ。 BEGIN_FACTORY_DEFマクロとEND_FACTORYマクロの中で呼び出す。 |
|
---|---|---|
引数 | 1つ目 | 音声処理クラス・パラメーター操作クラスのFUID。 (事前に設定したFUIDクラスをINLINE_UID_FROM_FUIDマクロを使って設定する。) |
2つ目 | PClassInfo::kManyInstancesを指定する。(現時点ではこれ以外指定できない。) | |
3つ目 | DEF_CLASS2マクロで記載しているクラスの種類。 音声処理クラスの場合…kVstAudioEffectClass、パラメーター操作クラスの場合…kVstComponentControllerClassを指定する。 |
|
4つ目 | VST3プラグインの名前。 | |
5つ目 | 音声処理クラスの場合…Vst::kDistributable、パラメーター操作クラスの場合…0を指定する。(現時点ではこれ以外指定できない。) | |
6つ目 | VST3プラグインのカテゴリ。とりあえずエフェクタならVst::PlugType::kFx、インストルメントならVst::PlugType::kInstrumentを選ぶとよい。 カテゴリについてはこちら → VSTプラグインのカテゴリ一覧の一覧を参照 音声処理クラスにはカテゴリを設定するが、パラメーター操作クラスは0とする。 |
|
7つ目 | VST3プラグインのバージョン。 | |
8つ目 | kVstVersionStringを指定する。(現時点ではこれ以外指定できない。) | |
9つ目 | 音声処理クラス・パラメーター操作クラスの生成関数のポインタ。 基本的に「Steinberg::Vst::<クラス名>::createInstance」にする。(クラス名は音声処理クラス・パラメーター操作クラスのクラス名) |
DLLファイルの初期化、終了処理関数のInitModule()関数とDeinitModule()については、VST SDK 3.7.2以降 不要な関数のためコメントアウトしています。
VST SDK 3.7.1以前のものを使用している場合は、InitModule()関数とDeinitModule()のコメントアウトを外してください。
詳細は割愛いたします。基本的にtrueを返すだけで問題ありません。
音声処理を行うクラスの作成
最後にVST3プラグインの中心となる音声処理を行うクラスを作成します。
音声処理クラスはVST SDKにあるAudioEffectクラスを継承して下記のように宣言します。
(VST3の開発では名前空間を指定する必要がありますので忘れないようご注意ください。)
【processor.h】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// VST3 SDKのインクルードファイル #include "public.sdk/source/vst/vstaudioeffect.h" // VST3作成に必要なの名前空間を使用 namespace Steinberg{ namespace Vst { // =================================================================================== // 音声信号を処理するProcessorクラス // =================================================================================== class MyVSTProcessor : public AudioEffect { public: // クラスを初期化する関数(必須) tresult PLUGIN_API initialize(FUnknown* context); // バス構成を設定する関数。 tresult PLUGIN_API setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts); // 音声信号を処理する関数(必須) tresult PLUGIN_API process(ProcessData& data); // 自作VST Processorクラスのインスタンスを作成するための関数(必須) static FUnknown* createInstance(void*) { return (IAudioProcessor*)new MyVSTProcessor(); } }; } } // namespace SteinbergとVstの終わり |
【processor.cpp】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
// 自作VST用のインクルードファイル #include "myvst3fuid.h" #include "processor.h" // VST3作成に必要なの名前空間を使用 namespace Steinberg{ namespace Vst { // =================================================================================== // クラスを初期化する関数 // =================================================================================== tresult PLUGIN_API MyVSTProcessor::initialize(FUnknown* context) { // まず継承元クラスの初期化を実施 tresult result = AudioEffect::initialize(context); if (result == kResultTrue) { // 入力と出力を設定 addAudioInput(STR16("AudioInput"), SpeakerArr::kStereo); addAudioOutput(STR16("AudioOutput"), SpeakerArr::kStereo); // 以下固有の初期化を実施。 // 今回は何もしない } // 初期化が成功すればkResultTrueを返す。 return result; } tresult PLUGIN_API MyVSTProcessor::setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) { // inputとoutputのバスが1つずつで、かつinputとoutputの構成がステレオの場合 if (numIns == 1 && numOuts == 1 && inputs[0] == SpeakerArr::kStereo && outputs[0] == SpeakerArr::kStereo) { return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts); } // 対応していないバス構成の場合、kResultFalseを返す。 return kResultFalse; } // =================================================================================== // 音声信号を処理する関数 // =================================================================================== tresult PLUGIN_API MyVSTProcessor::process(ProcessData& data) { // 入力・出力バッファのポインタをわかりやすい変数に格納 // inputs[]、outputs[]はAudioBusの数だけある(addAudioInput()、addAudioOutput()で追加した分だけ) // 今回はAudioBusは1つだけなので 0 のみとなる // channelBuffers32は32bit浮動小数点型のバッファで音声信号のチャンネル数分ある // モノラル(kMono)なら 0 のみで、ステレオ(kStereo)なら 0(Left) と 1(Right) となる Sample32* inL = data.inputs[0].channelBuffers32[0]; Sample32* inR = data.inputs[0].channelBuffers32[1]; Sample32* outL = data.outputs[0].channelBuffers32[0]; Sample32* outR = data.outputs[0].channelBuffers32[1]; // numSamplesで示されるサンプル分、音声を処理する for (int32 i = 0; i < data.numSamples; i++) { outL[i] = inL[i]; outR[i] = inR[i]; } // 問題なければkResultTrueを返す(おそらく必ずkResultTrueを返す) return kResultTrue; } } } // namespace SteinbergとVstの終わり |
initialize()関数について
initialize()関数は名前のとおり、クラスの初期化を行う関数です。
引数のcontextについては継承元のinitialize()関数に渡す以外に使用することはほとんどありませんので説明は省略します。
ここでは、継承元のinitialize()関数を呼び出した後、addAudioInput()関数・addAudioOutput()関数を呼び出し、入力と出力のバスを定義しています。
バス構成やaddAudioInput()関数・addAudioOutput()関数については次回の「VST3のバスについて」で説明いたします。
開発するVST3プラグイン固有の初期化(音声処理に使う変数やクラスの初期化など)が必要な場合も、このinitialize()関数内で実施します。
(今回は特に固有の初期化を行う必要がないため、何も実施していません。)
setBusArrangements()関数について
setBusArrangements()関数はバス構成が変更されたとき呼び出されます。(「入力バスがモノラルからステレオに変更された」「出力バスが新たに接続された」等)
詳細な説明はここでは省略いたします。
setBusArrangements()関数等についてはバスの概要と合わせて次回の「VST3のバスについて」で説明いたします。
process()関数について
process()関数は実際に音声の処理を行う関数です。
引数のProcessData& dataには処理に必要な一通りの情報(パラメーターの変更内容や音声データの入力・出力バッファのポインタなど)が入っています。
56行目~59行目では、音声データが入っている入力バッファ・出力バッファを使いやすいよう、Sample32型のポインタ変数に代入しています。(Sample32型は32bit浮動小数点型でfloatの再定義です。)
【processor.cpp】
56 57 58 59 60 61 62 63 64 |
// 入力・出力バッファのポインタをわかりやすい変数に格納 // inputs[]、outputs[]はAudioBusの数だけある(addAudioInput()、addAudioOutput()で追加した分だけ) // 今回はAudioBusは1つだけなので 0 のみとなる // channelBuffers32は32bit浮動小数点型のバッファで音声信号のチャンネル数分ある // モノラル(kMono)なら 0 のみで、ステレオ(kStereo)なら 0(Left) と 1(Right) となる Sample32* inL = data.inputs[0].channelBuffers32[0]; Sample32* inR = data.inputs[0].channelBuffers32[1]; Sample32* outL = data.outputs[0].channelBuffers32[0]; Sample32* outR = data.outputs[0].channelBuffers32[1]; |
引数dataのinputs[]、outputs[]の配列サイズは入力バス・出力バスの数と同じになります。入力バス・出力バスの数はそれぞれdata.numInputs・data.numOutputsで取得できます。
厳密には違いますが、基本的には入力バス・出力バスの数はaddAudioInput()関数・addAudioOutput()関数を呼び出した回数になります。
今回はaddAudioInput()関数・addAudioOutput()関数を1回ずつ呼び出しているので、data.inputs[0]とdata.outputs[0]のみとなります。(それぞれの配列サイズは1です。)
(例えばaddAudioInput()関数を3回呼び出すと、data.inputs[]の配列サイズは3となります。)
メンバー変数data.inputs[]のchannelBuffers32[][]配列には入力音声データが入っています。2次元配列になっており各次元の配列サイズは
channelBuffers32[バスのチャンネル数][音声データのサイズ]
となります。
チャンネル数は、data.inputs[x].numChannelsで取得できます。今回、addAudioInput()関数で「SpeakerArr::kStereo」を指定しているので、右チャンネルと左チャンネルで2チャンネル(=配列サイズは2)となります。
ステレオ(SpeakerArr::kStereo)以外にモノラル(SpeakerArr::kMono、配列サイズは1)や5.1ch(SpeakerArr::k51、配列サイズは6)など、他のバス構成もありますので詳しくはVST SDKのマニュアルなどを参照してください。
音声データのサイズはメンバー変数data.numSamplesで指定されます。
メンバー変数data.outputs[]は出力音声データを書き出すためのバッファとなります。内容はメンバー変数data.inputs[]と同様です。
以上から、使いやすいように定義したSample32型のポインタ変数inL・inR・outL・outRはそれぞれ下記のようになります。
- inL…ステレオ 左チャンネルの入力音声データが入った配列(配列の頭のポインタ)
- inR…ステレオ 右チャンネルの入力音声データが入った配列(配列の頭のポインタ)
- outL…ステレオ 左チャンネルの出力音声データを書き込むための配列(配列の頭のポインタ)
- outR…ステレオ 右チャンネルの出力音声データを書き込むための配列(配列の頭のポインタ)
なお、引数dataの他のメンバー変数の説明については省略いたします。
主なメンバー変数ついては「VST3プラグインのパラメーター実装方法1」などで順次説明させていただきます。
続いて、61行目~66行目では音声処理を実施しています
【processor.cpp】
61 62 63 64 65 66 67 68 69 |
// numSamplesで示されるサンプル分、音声を処理する for (int32 i = 0; i < data.numSamples; i++) { outL[i] = inL[i]; outR[i] = inR[i]; } // 問題なければkResultTrueを返す(おそらく必ずkResultTrueを返す) return kResultTrue; |
今回は特に何もせず入力音声データをそのまま出力しています。(入力音声バッファのデータを出力音声バッファにコピーしているだけです。)
最後はkResultTrueを返して終了します。
ソースファイルのビルド
上記4つのファイル(processor.h、processor.cpp、myvst3fuid.h、factory.cpp)を作成した後、ビルドするとVST3プラグインが作成されます。
Visual Studioのプロジェクトフォルダを標準の状態から変更していない場合、下記に<プロジェクト名>.vst3というファイルで作成されます。
(コンパイル・ビルドの方法はこちらでもご説明しております。→サンプルソースファイルのビルド方法)
- C:\Users\<ユーザ名>\source\repos\<プロジェクト名>\x64\debug\<プロジェクト名>.vst3
作成したVST3プラグインは下記のVST3専用フォルダへのコピーする必要があります。
一部のホストアプリケーションを除いて、下記のVST3専用フォルダ以外に.vst3ファイルがあっても認識してくれません。
- C:\Program Files\Common Files\VST3\
今回のVST3プラグインはフォルダ構成を気にせず、上記フォルダ内に保存すれば認識されるはずです。
なお、上記フォルダにコピーしたにもかかわらず、作成したVSTプラグインが認識されない場合は、「VST3プロジェクトの作成」の補足にある、「モジュール定義ファイル追加について」を試すか、「プラグイン情報の設定」のコードの中のBEGIN_FACTORY_DEF~END_FACTORYまでを「extern “C” {}」で括ってみてください。
また、ここでのVST3のサンプルは実行時に一部のホストアプリケーション(Sonar、VSTHost等)でエラー(例外)が発生する場合がございます。
エラー発生の際は「VST3プラグインのパラメーター実装方法1」まで進めていただき、再度試していただきますようお願いします。
最後に
以上でとりあえず音声処理を行えるVST3を作成することができるはずです。
このVST3プラグインのサンプルソースファイルはこちらからダウンロードできます → vst3dev_20210403.zip
コンパイル・ビルドの方法は簡単にこちらでご説明しております。→サンプルソースファイルのビルド方法
次回は、バスについての概念を記載できればと思います。
次回→VST3のバスについて
VST3プラグイン作りの情報はこちらにもございます → はじめてのVST3プラグイン作り
ご指摘やご質問などがございましたら、コメント欄か掲示板、Twitterでご連絡いただければと思います。
■掲示板
■Twitterアカウント:@vstcpp URL:https://twitter.com/vstcpp
大変有益な記事の作成、ありがとうございます。勉強させていただいております。
この記事では入力データをそのまま出力していますがステレオの左右を逆にしたいと思い、processor.cppの一部を
outL[i] = inR[i];
outR[i] = inL[i];
に書き換えてみましたが、どうもうまくいかないようです(モノラルのようになってしまいます)。
もし原因や改善策などがございましたらご教示いただければ幸いです。
sagamatさん
書き込みありがとうございます。
いくつかのDAWは入力・出力バッファが同じポインタになっている場合があります。
// いくつかのDAWではoutL[i]とinL[i]が同じバッファの場合がある。
outL[i] = inR[i]; // outL[i]とinL[i]が同じバッファ場合、ここでinL[i]もinR[i]と同じ値になる。
outR[i] = inL[i]; // すでにinL[i]がinR[i]と同じ値なので、outR[i]はinR[i]と同じになる。(モノラルのようになる)
解決策として、一時的な変数にinL[i]・inR[i]の値を代入して利用します。
Sample32 tmpL = inL[i];
Sample32 tmpR = inR[i];
outL[i] = tmpR;
outR[i] = tmpL;
うつぼかずら様
ありがとうございます!
ご教示いただいた解決策を試してみたところ、希望通りに動作いたしました。
お忙しいところご対応いただき感謝申し上げます。
うつぼかずら様
上記、sagamatからの御礼です。
失礼いたしました。