はじめに
はいっ、というわけで今日も元気にVST3プラグインを作っていきましょう。本日の企画はVST3プラグインのバイパス機能の実装についてです。この記事はVST3プラグインを書く人向けの内容です。
バイパスの扱い
バイパス機能について簡単に説明すると、本来おこなうはずのエフェクト処理をスルーして入力を無加工で出力に流す機能です。要はエフェクトオフですね。
VST3におけるバイパス機能は、プラグイン側の責任で実装します。似たような機能にプラグインのActivate/Deactivateもあってこっちはどちらかというとホスト側の責任で実装します。
プラグイン側の責任なのでバイパス機能は実装してもしなくてもプラグインとしては正常に動作しますが、VST3 SDKのドキュメントではバイパス機能を提供することが強く推奨されています。大事なことなので2回書かれててます。
VST3 Documentation Parameters and Automation
https://developer.steinberg.help/display/VST/Parameters+and+Automation
It is highly recommended that this bypass parameter is provided by the effect plug-in.
(中略)
It is highly recommended to export a bypass parameter for an effect plug-in.
後者は、バイパス機能のパラメータは、ひとつのプラグインにつき唯ひとつだけってことを強調してるっぽいですね。
また、公式テストツールvalidatorを使ってテストすると、バイパスパラメータが提供されていないと警告が出ます。
[Scan Parameters]
Info: ===Scan Parameters ====================================
Info: This component exports 1 parameter(s)
Info: Parameter 000 (id=0): [title="Volume"] [unit=""] [type = F, default = 1.000000, unit = 0]
Info: Warning: No bypass parameter found. Is this intended ?
[Succeeded]
バイパスの実装
EditControllerのinitialize()関数で、他のパラメータと同じようにaddParameterします。このとき、これがバイパス機能であることを示すため、Vst::ParameterInfo::kIsBypassフラグを必ずつけてください。
kMyBypassIdはどこかで #define kMyBypassId 0 のようにユニークなIDを定義しておきます。
parameters.addParameter(STR16("Bypass"), nullptr, 1, 0.0, \
Vst::ParameterInfo::kCanAutomate | Vst::ParameterInfo::kIsBypass, kMyBypassId);
第3引数のstepCountの値1はOn/Offの二段階のパラメータであることを示します。1 2
AudioProcessorの音声処理process()関数はこんな感じになります。
まずはパラメータ変更チェック。一見複雑ですがほとんどがパラメータチェックの定型処理なので、見るべきはswitch文の中です。変更されたパラメータのIDがkMyBypassIdだったら、valueをboolに変換してメンバ変数bBypassに格納しています。
tresult PLUGIN_API MyGainProcessor::process (Vst::ProcessData& data)
{
if (data.inputParameterChanges)
{
int32 numParamsChanged = data.inputParameterChanges->getParameterCount ();
for (int32 index = 0; index < numParamsChanged; index++)
{
if (auto* paramQueue = data.inputParameterChanges->getParameterData (index))
{
Vst::ParamValue value;
int32 sampleOffset;
int32 numPoints = paramQueue->getPointCount ();
if (paramQueue->getPoint(numPoints - 1, sampleOffset, value) == kResultTrue)
{
switch (paramQueue->getParameterId())
{
case kMyBypassId:
bBypass = (value > 0.5);
break;
}
}
}
}
}
それに続く音声処理です。bBypassフラグが立っていたら、入力バッファを出力バッファにmemcpyし、その後のエフェクト処理をスキップして戻ります。ただし入力バッファと出力バッファが同じ場合があるのでif文でチェックしています。
バイパスはこの辺をプラグインの責任で実装する必要があるということですね。
int32 numChannels = data.inputs[0].numChannels;
Vst::Sample32** in = data.inputs[0].channelBuffers32;
Vst::Sample32** out = data.outputs[0].channelBuffers32;
if (bBypass)
{
uint32 sampleFramesSize = Vst::getSampleFramesSizeInBytes(processSetup, data.numSamples);
for (int32 ch = 0; ch < numChannels; ch++)
{
if (in[ch] != out[ch])
{
memcpy(out[ch], in[ch], sampleFramesSize);
}
}
return kResultOk;
}
// 実際のエフェクト処理を以下に記述
getSampleFramesSizeInBytesは、public.sdk/source/vst/vstaudioprocessoralgo.hに含まれるので #include しておいてください。
上の例は32bit決め打ちで実装しているので、64bit対応の場合はSample32の部分を適切に切り替えるような実装が必要です(参考:VST3の32bit/64bit処理対応について)。
またパラメータ状態の保存と復帰のためにMyAudioProcessor::setState/getState、MyEditController::setComponentStateあたりも実装した方が良いですが、それは他のパラメータ処理と同じ書き方で今回の本題からは外れるのでここでは省略します。
そんなわけで今回の記事は以上です。
よかったらチャンネル登録、高評価よろしくおねがいします!(YouTuber風)