音声処理クラスの作成と初期化
前回はVST3プラグインの情報を表示する簡単なVST3ホストの作成について記載いたしました。
今回はVST3プラグインから音声処理クラスを取得するプログラムの説明を実施します。
今回作成するプラグインは下記のとおりです。
- VST3プラグインを読み込む
- VST3プラグインから音声処理クラスを作成する(new)
実際に音声処理を行うまではもう少し手順がありますが、まずは音声処理クラスを作成するところまで実施いたします。
このVST3プラグインのサンプルソースファイルはこちらからダウンロードできます→vst3host_20210410
ZIPファイルの中の「vst3host02_音声処理クラスの作成と初期化」フォルダが今回のサンプルソースファイルになります。
なお、Visual Studioのプロジェクトは「コンソールアプリケーション」で作成しております。
プロジェクト設定などは「Visual Studioのプロジェクト設定」もご参照ください。
今回の説明内容
本ページでは、前回説明したVST3プラグインの利用の流れ(下記)のうち、3.についての内容を説明します。
なお、前回説明した内容については、説明を省略させていただきます。
- VST3プラグインのファイル(.vst3ファイル)をホストアプリケーションに組み込む(読み込む)
- VST3プラグインのファイル内にある音声処理クラスとパラメーター操作クラスの一覧を取得
- 音声処理クラスとパラメーター操作クラスの一覧から必要なクラスのインスタンスを作成
- 作成した音声処理クラスとパラメーター操作クラスのインスタンスを初期化
VST3ホストのソースコード
まず、ソースコードは下記となります。今回説明する点については、ハイライトしております。説明は後述します。
【main.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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
// ここからVSTホストに必要なヘッダ // VST3ファイルの読込に必要 // module_win32.cppをプロジェクトに追加すること // ※ module_win32.cppはWindows用なので、Linuxの場合はmodule_linux.cpp、 // macOSの場合はmodule_mac.mmをかわりに追加する。(たぶん) #include "public.sdk/source/vst/hosting/module.h" // 読み込んだVST3ファイルから各クラスを取得するため必要 // plugprovider.cppをプロジェクトに追加すること #include "public.sdk/source/vst/hosting/plugprovider.h" // plugproviderで必要なPluginContext用のヘッダ #include "public.sdk/source/vst/hosting/hostclasses.h" // ホスト用クラスを使用するときに必要なネームスペース using namespace VST3::Hosting; // ここからVSTプラグイン関係(音声処理クラスやパラメーター操作クラス)のヘッダ #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/base/ftypes.h" #include "pluginterfaces/vst/vsttypes.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" // 音声処理クラスやパラメーター操作クラスの使用時に必要なネームスペース using namespace Steinberg; using namespace Steinberg::Vst; // ここからそれ以外のヘッダ #include <stdio.h> using namespace std; // メイン関数はここから int main() { string path = u8".\\vst_file_name.vst3"; // VST3プラグインのパス string error; // VST3プラグインのファイルを読み込みに失敗したときのエラー用 // --------------------------------------------------------------------------- // PluginContextを作成・セットアップする // PluginContextはVST3プラグインがホストアプリ名やインターフェイスの対応状況を取得したり、 // allocateMessage()でIMessageを取得するために必要。(詳細は割愛) HostApplication pluginContext; PluginContextFactory::instance().setPluginContext(&pluginContext); // --------------------------------------------------------------------------- // Moduleクラスを使用してVST3プラグインのファイルを読み込む shared_ptr<Module> module = Module::create(path, error); if (!module) { // 読み込みに失敗したら終了 printf("Error : VST3ファイルの読込に失敗\n"); printf(" : path = %s\n", path.c_str()); printf(" : error = %s\n", error.c_str()); return 1; } // --------------------------------------------------------------------------- // PluginFactoryクラスを取得する // PluginFactoryクラスはVST3プラグインファイル内にあるプラグイン情報取得や音声処理クラスと // パラメータ操作クラスを列挙・作成するクラス PluginFactory factory = module->getFactory(); // PluginFactoryクラスでVST3プラグインファイル内にあるクラスを列挙しながら // 音声処理クラスのみをaudioClassInfoにコピーする // VST3プラグインファイル内には複数の音声処理クラスが存在する場合があるため一度列挙する vector<ClassInfo> audioClassInfo; for (ClassInfo classInfo : factory.classInfos()) { // 音声処理クラスはカテゴリーがkVstAudioEffectClassとなっているものとなる // (ちなみにパラメータ操作クラスはkVstComponentControllerClass ) if (classInfo.category() == kVstAudioEffectClass) { audioClassInfo.push_back(classInfo); } } // VST3プラグインファイル内に音声処理クラスがなかった場合は終了する。 if (audioClassInfo.size() == 0) { printf("Error : VST3ファイル内に音声処理クラスがありません。\n"); return 1; } // --------------------------------------------------------------------------- // PlugProviderクラスを作成する // PlugProviderクラスはPluginFactoryクラスとClassInfoクラスから音声処理クラスを // 作成・初期化するクラス shared_ptr<PlugProvider> plugProvider; plugProvider = make_shared<PlugProvider>(factory, audioClassInfo[0], true); // PlugProviderクラスが正常に作成できたかチェック if (!plugProvider) { // 読み込みに失敗したら終了 printf("Error : PlugProviderクラスの作成に失敗\n"); return 1; } printf("最初に見つかった音声処理クラス「%s」を作成・初期化しました。\n", audioClassInfo[0].name().c_str()); // --------------------------------------------------------------------------- // PlugProviderクラスから音声処理クラスを取得する。(音声処理クラスは初期化済み) IComponent* component = plugProvider->getComponent(); // 音声処理クラスはIComponentクラスとIAudioProcessorクラスを継承している // PlugProviderクラスから取得できるのはIComponentクラスのみなので、 // IComponentクラス取得後、queryInterfaceでIAudioProcessorクラスも取得する IAudioProcessor* processor; component->queryInterface(IAudioProcessor::iid, (void**)&processor); if (processor == nullptr) { printf("Error : 音声処理クラスの実装が不十分なため失敗\n"); return 1; } // ~~~~ // 本来はここでcomponentを使用して音声処理クラスのセットアップ、音声処理などを行う。 // ~~~~ // --------------------------------------------------------------------------- // 終了処理 // IAudioProcessorはrelease()を呼び出して解放する。 processor->release(); // IComponentクラスはPlugProviderクラスで解放する。 // PlugProviderクラス自体はmake_sharedで作成されるので解放は不要。 plugProvider->releasePlugIn(component, nullptr); // Moduleクラスはmake_sharedで作成されているので解放は不要。 // PluginFactoryクラスは解放の必要は特にない。 printf("終了します\n"); return 0; } |
必要なヘッダファイルと定義
今回は新たにヘッダファイル「plugprovider.h」と「hostclasses.h」を読み込んでいます。
ヘッダファイル「plugprovider.h」にはファクトリークラスから音声処理クラスなどのインスタンスを取得するためのPlugProviderクラス(後述)の定義が記載されています。
また、ヘッダファイル「hostclasses.h」はPlugProviderクラス(後述)の内部で使用される、HostApplicationクラスの定義が記述されています。
なお、「plugprovider.h」を利用するためにはVisual StudioのプロジェクトにPlugProviderクラスのソースファイル(下記)を追加する必要があります。
- C:\VST_SDK\VST3_SDK\public.sdk\source\vst\hosting\plugprovider.cpp
次に、読み込んだVST3プラグインを使用するため、プラグイン関連のヘッダファイルを読み込みます。
それぞれのヘッダファイルは下記のようになります。
ヘッダ | 用途 |
---|---|
funknown.h | VST SDKの多くのクラスの継承元となるFUnknownクラスの定義されたヘッダファイル(とりあえず読み込んでおく) |
ftypes.h | VST SDKで使われている基本的な型(int32やchar16など)が定義されたヘッダファイル |
vsttypes.h | VST SDKの音声処理クラスやパラメータ操作クラスで使われている型(Sample32やParamIDなど)が定義されたヘッダファイル |
ivstaudioprocessor.h | 音声処理クラスの継承元となるIComponentクラスやIAudioProcessorクラス、その他 構造体などが定義されたヘッダファイル (IComponentクラス自体はivstcomponent.hで定義されており、ivstaudioprocessor.hで読み込まれている) |
ivsteditcontroller.h | パラメータ操作クラスの継承元となるIEditControllerクラスやその他 構造体などが定義されたヘッダファイル |
他のヘッダファイルについては前回の内容をご確認ください
PluginContextの作成・セットアップ
最初にPlugProviderクラス(後述)の内部で使用されるHostApplicationクラスを準備・セットアップします。
HostApplicationクラスはホストアプリの名称やインターフェイス対応状況をVST3プラグインに知らせたり、VST3プラグインのallocateMessage()の際にIMessageを作成して渡したりするために必要となります。
VST3プラグイン側からは、これらの情報はPluginContextと呼ばれます。
PluginContextの作成・セットアップでは、下記の通りHostApplicationクラスを定義して、PluginContextFactory::setPluginContext()で設定しています。
【main.cpp】
40 41 42 43 44 45 |
// --------------------------------------------------------------------------- // PluginContextを作成・セットアップする // PluginContextはVST3プラグインがホストアプリ名やインターフェイスの対応状況を取得したり、 // allocateMessage()でIMessageを取得するために必要。(詳細は割愛) HostApplication pluginContext; PluginContextFactory::instance().setPluginContext(&pluginContext); |
HostApplicationクラスの詳細については割愛いたしますが、上記の設定だけすれば機能的な部分では問題はほとんどありません。
気になるのであればVST SDKの下記ファイルをご確認いただければと思います。
- public.sdk/source/vst/hosting/hostclasses.cpp
- public.sdk/source/vst/hosting/pluginterfacesupport.cpp
なお、HostApplicationクラスのgetName()関数の実装が悪く、ホストアプリの名称が「My VST3 HostApplication」とハードコーディングされています。
ホスト名を変えたければ、HostApplicationクラスをもとにして似たクラスを作成する必要があります。
音声処理クラスの取得
メイン関数では、前回と同様に、まずVST3プラグインのファイル読み込みとファクトリークラス(PluginFactoryクラス)の取得の完了を実施します。
(VST3プラグインファイルの読み込みとファクトリークラスの取得についての説明は前回の内容をご確認ください。)
その後、取得したPluginFactoryクラスでVST3プラグインファイル内にある音声処理クラス情報のみを変数audioClassInfoに取得しています。
これは「VST3プラグインファイル内に音声処理クラス(エフェクターやシンセサイザー)は複数存在してもよい」という仕様があるためで、いったん「どんな音声処理クラスがあるか」を配列等に列挙・取得しておき、後から「どの音声処理クラス(エフェクターやシンセサイザー)を読み込むか」を選ぶためです。
ちなみに、VST3プラグインファイル内に複数の音声処理クラスがあるサンプルとして、VST SDK付属のagainサンプルがあります。
againサンプルはVST3プラグインファイル内に「AGain VST3」と「AGain SideChain VST3」の2つの音声処理クラス(エフェクター)があります。
【main.cpp】
66 67 68 69 70 71 72 73 74 75 76 77 78 |
// PluginFactoryクラスでVST3プラグインファイル内にあるクラスを列挙しながら // 音声処理クラスのみをaudioClassInfoにコピーする // VST3プラグインファイル内には複数の音声処理クラスが存在する場合があるため一度列挙する vector<ClassInfo> audioClassInfo; for (ClassInfo classInfo : factory.classInfos()) { // 音声処理クラスはカテゴリーがkVstAudioEffectClassとなっているものとなる // (ちなみにパラメータ操作クラスはkVstComponentControllerClass ) if (classInfo.category() == kVstAudioEffectClass) { audioClassInfo.push_back(classInfo); } } |
なお、VST3プラグインファイル内に音声処理クラス情報が1つも場合、音声処理できないので終了します。
【main.cpp】
80 81 82 83 84 85 |
// VST3プラグインファイル内に音声処理クラスがなかった場合は終了する。 if (audioClassInfo.size() == 0) { printf("Error : VST3ファイル内に音声処理クラスがありません。\n"); return 1; } |
プラグインプロバイダクラスの作成
音声処理クラス情報の列挙と取得が終われば、ファクトリークラスと音声処理クラスの情報(audioClassInfo[0])をもとにプラグインプロバイダクラス(PlugProviderクラス)を作成します。
PlugProviderクラスは名前の通り、音声処理クラスやパラメータ操作クラスを準備(作成と初期化)して利用できるように提供してくれるクラスです。
PlugProviderクラスを作成する際はmake_sharedを使用して作成します。コンストラクタに渡す引数などは下記になります。
PlugProviderクラスの他の主なメンバー関数については、順番に紹介・説明いたしますのでここでは省略します。
- PlugProviderコンストラクタ
概要 | PlugProviderクラスのコンストラクタ。このコンストラクタ内で音声処理クラスやパラメータ操作クラスの作成や初期化が行われる。 | ||
---|---|---|---|
引数 | 型 | 変数名 | 概要 |
PluginFactory& | factory | 音声処理クラス情報を列挙した際に使用したファクトリークラスへの参照です。 | |
ClassInfo | info | 作成・初期化する音声処理クラスの情報です。 (この音声処理クラスに関連付けられたパラメータ操作クラスの作成や初期化も行われます。) |
|
bool | plugIsGlobal | ホストコンテキストであるgStandardPluginContext(今回説明は省略)を使用するかどうかのフラグです。 PlugProviderクラスの実装を見る限りでは基本的にtrue以外の選択肢はありませんので、trueを指定します。 なお、省略可能で省略するとtrueとなります。 |
今回のサンプルでは、とりあえず最初に見つかった音声処理クラスをもとにPlugProviderクラスを作成しています。
(先ほど音声処理クラスを列挙・取得しましたが、ここでは説明のため、あえて簡素化しています。実用的なアプリケーションにするには、利用者に「列挙した音声処理クラス(エフェクタなど)からどのクラスを読み込むか」を選択させる等の対応を行います。)
【main.cpp】
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
// --------------------------------------------------------------------------- // PlugProviderクラスを作成する // PlugProviderクラスはPluginFactoryクラスとClassInfoクラスから音声処理クラスを // 作成・初期化するクラス shared_ptr<PlugProvider> plugProvider; plugProvider = make_shared<PlugProvider>(factory, audioClassInfo[0], true); // PlugProviderクラスが正常に作成できたかチェック if (!plugProvider) { // 読み込みに失敗したら終了 printf("Error : PlugProviderクラスの作成に失敗\n"); return 1; } printf("最初に見つかった音声処理クラス「%s」を作成・初期化しました。\n", audioClassInfo[0].name().c_str()); |
PlugProviderクラスの補足ですが、PlugProviderクラスが作成された際には下記3つが内部で実施されます。
実施概要 | 実施内容 |
---|---|
音声処理クラスのインスタンス作成と初期化 | 音声処理クラスのcreateInstance()関数を使って音声処理クラスを作成するとともに、音声処理クラスのinitialize()関数が呼び出します。 |
音声処理クラスの対になるパラメータ操作クラスのインスタンス作成と初期化 | 音声処理クラスでgetControllerClassId()関数を呼び出し、パラメータ操作クラスのFUIDを取得します。 その後、パラメータ操作クラスのcreateInstance()関数を使ってパラメータ操作クラスを作成するとともに、パラメータ操作クラスのinitialize()関数が呼び出されます。 |
音声処理クラスとパラメータ操作クラスの接続 | 各クラスの作成・初期化後に、音声処理クラスとパラメーター操作クラスのconnect()関数が呼び出され、両クラスの接続が行われます。ここでは詳細を割愛しますが、パラメータ情報のやり取りのための接続ではなく、sendMessage()関数やnotify()関数でメッセージをやり取りするための接続となります。 |
音声処理クラスの取得
PlugProviderクラスを作成が終われば、いよいよ音声処理クラスを取得します。
音声処理クラスは基本的にAudioEffectクラスを継承して作られるのですが、ホストアプリケーションを作成する際は、このAudioEffectクラスを直接取得しないようにします。
(AudioEffectクラスを直接取得しないのは、VST Module Architecture(VST-MA)と呼ばれる設計思想に基づいているためです。VST-MAについては詳細を割愛します。)
代わりに、AudioEffectクラスの継承元である3つのインターフェイスクラス(下記)を使用してセットアップや音声処理を行います。
- IComponentクラス
- IAudioProcessorクラス
- IConnectionPointクラス
上記の、3つのインターフェイスクラスのうち、音声処理で主に使用するのはIComponentクラス・IAudioProcessorクラスになります。
PlugProviderクラスからはgetComponent()関数でIComponentクラスを取得することができます。
- getComponent()関数
概要 | PlugProviderクラスから初期化済みのIComponentクラス(音声処理クラスの継承元)を取得する関数 | ||
---|---|---|---|
戻り値 | 型 | 概要 | |
IComponent* | 初期化済みのIComponentクラス(音声処理クラスの継承元)のポインタです。 | ||
引数 | 型 | 変数名 | 概要 |
引数はありません。 |
IAudioProcessorクラスの取得は、IComponentクラスのqueryInterface()関数を呼び出すことで取得します。
dynamic_castではなく、queryInterface()関数を使用するのもVST-MAの設計思想に基づいているためです。
- queryInterface()関数
概要 | 継承先のクラスが、引数1のFUIDで示されたインターフェイスクラスを継承しているか問い合わせ、継承している場合、そのインターフェイスクラスのポインタを取得します。 例えば、IComponentクラスの継承先クラス(ここではAudioEffectクラス)がIAudioProcessorクラスを継承しているかをFUIDで問い合わせ、継承している場合、IAudioProcessorクラスのポインタを取得します。 |
||
---|---|---|---|
戻り値 | 型 | 概要 | |
tresult | 問い合わせの結果、継承している場合はkResultOk(0x00000000)が、継承していない場合はkNoInterface(0x80004002)が返ります。 (tresultはintの再定義) |
||
引数 | 型 | 変数名 | 概要 |
const TUID | _iid | 該当のインターフェイスクラスを継承するか問い合わせるためのFUIDです。 FUIDは各インターフェイスクラスクラスのヘッダファイル(ivstaudioprocessor.hやivsteditcontroller.h等)で定義された定数になります。 「インターフェイスクラス名::iid」の形式で記載すれば使用することができます。 |
|
void** | obj | 問い合わせ結果であるインターフェイスクラスのポインタを保存するための変数になります。 |
【main.cpp】
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
// --------------------------------------------------------------------------- // PlugProviderクラスから音声処理クラスを取得する。(音声処理クラスは初期化済み) IComponent* component = plugProvider->getComponent(); // 音声処理クラスはIComponentクラスとIAudioProcessorクラスを継承している // PlugProviderクラスから取得できるのはIComponentクラスのみなので、 // IComponentクラス取得後、queryInterfaceでIAudioProcessorクラスも取得する IAudioProcessor* processor; component->queryInterface(IAudioProcessor::iid, (void**)&processor); if (processor == nullptr) { printf("Error : 音声処理クラスの実装が不十分なため失敗\n"); return 1; } // ~~~~ // 本来はここでcomponentを使用して音声処理クラスのセットアップ、音声処理などを行う。 // ~~~~ |
IComponentクラスとIAudioProcessorクラスの取得後は、両方のインターフェイスクラスを使用して、音声処理クラスのセットアップを行います。
音声処理クラスのセットアップについては、今回 説明が多くなり過ぎるため、次回 説明いたします。
終了処理
最後に終了処理を行います。終了処理では下記を実施します。
- queryInterfaceで取得したインターフェイスクラスについてrelease()関数を呼び出す
- plugProviderクラスでreleasePlugIn()関数を呼び出す。
今回queryInterfaceで取得したインターフェイスクラスはIAudioProcessorクラスになるので、IAudioProcessorクラスのrelease()関数を呼び出します。
deleteを実施する必要はありません。release()関数の詳細は割愛いたします。
インターフェイスクラスの解放後は、plugProviderクラスでのreleasePlugIn()関数の呼び出します。
引数はplugProviderクラスから取得した音声処理クラスの継承元クラス(IComponentクラス)とパラメーター操作クラスの継承元クラス(IEditControllerクラス)のポインタになります。
今回、パラメータ操作クラスは取得していないのでnullptrとしています。releasePlugIn()関数の詳細は割愛いたします。
【main.cpp】
127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
// --------------------------------------------------------------------------- // 終了処理 // IAudioProcessorはrelease()を呼び出して解放する。 processor->release(); // IComponentクラスはPlugProviderクラスで解放する。 // PlugProviderクラス自体はmake_sharedで作成されるので解放は不要。 plugProvider->releasePlugIn(component, nullptr); // Moduleクラスはmake_sharedで作成されているので解放は不要。 // PluginFactoryクラスは解放の必要は特にない。 printf("終了します\n"); return 0; |
最後に
実行結果のサンプルについては特に面白味もないため割愛しましたが、音声処理クラスの作成と初期化は以上となります。
この後、音声処理クラスをセットアップすれば実際に音声処理を行うことができるようになります。
次回は、作成した音声処理クラスをセットアップするところまでを説明いたします。
VST3プラグイン作りの情報はこちらにもございます → はじめてのVST3プラグイン作り
ご指摘やご質問などがございましたら、コメント欄か掲示板、Twitterでご連絡いただければと思います。
■掲示板
■Twitterアカウント:@vstcpp URL:https://twitter.com/vstcpp