「VSTe 操作するパラメータを管理するコードを覚える」の版間の差分

提供:yonewiki
編集の要約なし
60行目: 60行目:
protected:
protected:
int32      int32OptionMenu01  = 0;
int32      int32OptionMenu01  = 0;
ParamValue ParamValue_Knob01 = 0.1;
ParamValue ParamValue_Knob01 = 0.5;
};
};
   
   
152行目: 152行目:
break;
break;
case PARAMVALUE_KNOB01:
case PARAMVALUE_KNOB01:
ParamValue_Knob01 = ParamValue_value;
ParamValue_Knob01 = (29.5f * ParamValue_value) + 0.5f;
break;
break;
}
}
216行目: 216行目:
parameters.addParameter(param1);//SteinBerg::Vst::parameters.addParameter()
parameters.addParameter(param1);//SteinBerg::Vst::parameters.addParameter()


RangeParameter* param2 = new RangeParameter(STR16("Knob_Para1"), PARAMVALUE_KNOB, STR16(""), 0.0, 1.0, 0.0);
RangeParameter* param2 = new RangeParameter(STR16("Knob_Para1"), PARAMVALUE_KNOB, STR16(""), 0.5, 30.0, 0.5);
parameters.addParameter(param2);//SteinBerg::Vst::parameters.addParameter()
parameters.addParameter(param2);//SteinBerg::Vst::parameters.addParameter()
}
}
269行目: 269行目:
</syntaxhighlight>
</syntaxhighlight>


 このサンプルはOption MenuコントロールとKnobコントロールを追加するコードです。
 このサンプルはOption MenuコントロールとKnobコントロールを追加するコードです。CxxProcクラスが内部的な計算だけをする役割のクラスで、CxxEditクラスがインターフェイスの制御をする役割です。




287行目: 287行目:




 このメンバ変数に値を保持させます。メンバ変数には実際に利用するときの値が格納されます。今回の例ではint32OptionMenu01には0,1,2の3種類。PARAMVALUE_Knob01には0.5~30.0です。
 このメンバ変数に値を保持させます。メンバ変数には実際に利用するときの値が格納されます。今回の例ではint32OptionMenu01には0,1,2の3種類。PARAMVALUE_Knob01には0.5~30.0です。これらの値はDAWのホストが認識する際に0~1の値に置き換えて管理する必要もあります。
 
 
 置き換えを正規化とも言います。0,1,2を0~1で管理する場合は、0 = 0.0, 1 = 0.5, 2 = 1.0のように割り当てられます。0~1の範囲になった値の事を正規化された値と呼ぶことにします。
 
 
 0.5~30.0の場合は0.5が0.0に対応、30が1.0の対応になります。0.5~30.0のように0.5から始まる値を扱う場合は0が基準になるより差分で考えます。つまり、30 - 0.5 = 29.5 ですから、あたかも0~29.5の値を管理するように考え、戻す時には+0.5をする。後で0.5加えればいいので、それぞれの保持値を29.5で割った値を管理していれば0~1の値に変換できることになります。
 
 
 0.5~30の範囲を0.0~1.0で表現するための関数がありまして、170行目にも登場するplainParamToNormalized()という関数です。第一引数に正規化するコントロールのID、第二引数に正規化される前の値を付与します。戻り値に正規化された値が返ります。
 
 
 インターフェースで扱う場合は最初に初期化が必要です。初めてVSTインターフェイスが描画されるとき、EditControllerクラスから派生したクラスCxxEditクラスの関数CxxEdit::Initialize(FUnknown* context)が呼び出されます。129、135行目のような関数を実行してコントロールにID、Tags名称、値の範囲、初期値、単位を結び付ける処理が必要です。
 
 
 Optiom Menuコントロールのように文字列を制御する場合は129行目のStringListParameter関数で結び付けます。Knobコントロールのようにノブの回転量を制御する場合はRangeParameter関数で結び付けます。




 
 

2023年6月1日 (木) 00:37時点における版

概要(プラグイン開発)

 音声処理の核心に触れることなく、パラメータだけを追加して管理するコードを覚えます。音声処理はかなり難しいので、後回しでいいと思います。パラメータの追加方法と管理方法というか制御方法を覚えれば、音声処理をしやすくなるので、先に覚えると楽です。


 ここでは、ユーザ定義の2つのクラスが登場します。好きな名前をつけられる部分です。いかにも勝手に名前つけやがったなという意味でCxxという接頭句をつけてみました。


  • CxxProc(AudioEffectクラスから派生)
  • CxxEdit(EditControllerクラスから派生)


ヘッダファイルではこんな風に定義されます。何も処理をしない基礎で既に習得済みの部分も多いですが、全部を記載します。追記した部分を後で示します。


#pragma once

//include headerfiles
#include "pluginterfaces\base\funknown.h"
#include "public.sdk/source/vst/vstaudioeffect.h"
#include "public.sdk/source/vst/vsteditcontroller.h"
#include "public.sdk/source/main/pluginfactory.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include "pluginterfaces/base/ibstream.h"
#include "vstgui/plugin-bindings/vst3editor.h"

namespace Steinberg{
namespace Vst {

#define xxVENDOR   "VENDOR"         
#define xxURL      "https://wiki.yo-net.jp/"
#define xxEMAIL    "mailto:__info@yo-net.jp"
#define xxVSTNAME  "UnknownVST"
#define xxVERSION  "1"
#define xxSUBCATEGORIES Vst::PlugType::kFx

static const FUID ProcessorUID(0x13E7D9FD, 0xE02B4003, 0xA2324A98, 0x45A53825);
//{13E7D9FD-E02B-4003-A232-4A9845A53825}
static const FUID ControllerUID(0xBFA33A3F, 0x06B34AEE, 0x83B53575, 0x0F947340);
//{BFA33A3F-06B3-4AEE-83B5-35750F947340}
enum ParaTag
{
	INT32_OPTION_MENU01 = 1,
	PARAMVALUE_KNOB01,
};

Class CxxProc : AudioEffect
{
public:
	CxxProc();

	tresult PLUGIN_API initialize(FUnknown* context) SMTG_OVERRIDE;
	tresult PLUGIN_API process(ProcessData& data) SMTG_OVERRIDE;
	tresult PLUGIN_API getState(IBStream* state) SMTG_OVERRIDE;
	tresult PLUGIN_API setState(IBStream* state) SMTG_OVERRIDE;
	static FUnknown* createInstance(void*) {
      return (IAudioProcessor*)new CxxProc(); 
    }

protected:
	int32      int32OptionMenu01  = 0;
	ParamValue ParamValue_Knob01 = 0.5;
};
 
Class CxxEdit : EditController
{
public:
	tresult PLUGIN_API initialize(FUnknown* context) SMTG_OVERRIDE;
	tresult PLUGIN_API getState(IBStream* state) SMTG_OVERRIDE;
	tresult PLUGIN_API setState(IBStream* state) SMTG_OVERRIDE;
	tresult PLUGIN_API setComponentState(IBStream* state) SMTG_OVERRIDE;
	IPlugView* PLUGIN_API createView(const char* name) SMTG_OVERRIDE;
	static FUnknown* createInstance(void*) { return (IEditController*)new CxxEdit(); }
};

}}//close namespace


ソースファイルはこんな感じ


#include "vst.h"
BEGIN_FACTORY_DEF(xxVENDOR, xxURL, xxEMAIL)

	DEF_CLASS2(INLINE_UID_FROM_FUID(Steinberg::Vst::ProcessorUID),
	PClassInfo::kManyInstances,
	kVstAudioEffectClass,
	xxVSTNAME,
	Vst::kDistributable,
	xxSUBCATEGORIES,
	xxVERSION,
	kVstVersionString,
	Steinberg::Vst::CxxProc::createInstance)

	DEF_CLASS2(INLINE_UID_FROM_FUID(Steinberg::Vst::ControllerUID),
	PClassInfo::kManyInstances,
	kVstComponentControllerClass,
	xxVSTNAME " Controller",
	0,                          
	"",                         
	xxVERSION,
	kVstVersionString,
	Steinberg::Vst::CxxEdit::createInstance)

END_FACTORY

namespace Steinberg{
namespace Vst {

CxxProc::CxxProc()
{
	setControllerClass(ControllerUID);
}

tresult PLUGIN_API CxxProc::initialize(FUnknown* pFUnknown_Context)
{
	tresult tresult_r = AudioEffect::initialize(pFUnknown_Context);
	if (tresult_r == kResultTrue)
	{
		addAudioInput(STR16("AudioInput"), SpeakerArr::kStereo);
		addAudioOutput(STR16("AudioOutput"), SpeakerArr::kStereo);
	}

	return tresult_r;
}


tresult PLUGIN_API CxxProc::process(ProcessData& refProcessDat_Data)
{
	if (refProcessDat_Data.inputParameterChanges != nullptr)
	{
		int32 int32ParamChangeCount = refProcessDat_Data.inputParameterChanges->getParameterCount();
		for (int32 int32i = 0; int32i < int32ParamChangeCount; int32i++)
		{
			IParamValueQueue* pIParamValueQueue_Q = refProcessDat_Data.inputParameterChanges->getParameterData(i);
			if (pIParamValueQueue_Q != nullptr)
			{
				int32 int32Tag = pIParamValueQueue_Q->getParameterId();

				int32 int32valueChangeCount = pIParamValueQueue_Q->getPointCount();

				ParamValue ParamValue_value;
				int32 int32SampleOffset;
				if (pIParamValueQueue_Q->getPoint(int32valueChangeCount - 1, int32SampleOffset, ParamValue_value) == kResultTrue)
				{
					switch (int32Tag)
					{
					case INT32_OPTION_MENU01:
						int32OptionMenu01 = (int32)(ParamValue_value * 2.0f);//3択は0, 0.5, 1 →x2.0→ 0, 1, 2 、2択は0, 1 →x1.0→ 0, 1 同様に4択ならx3
						break;
					case PARAMVALUE_KNOB01:
						ParamValue_Knob01 = (29.5f * ParamValue_value) + 0.5f;
						break;
					}
				}
			}
		}
	}

	if (refProcessDat_Data.numInputs != 1 || refProcessDat_Data.numOutputs != 1)
	{
		return kResultTrue;
	}
	
	if (refProcessDat_Data.inputs[0].numChannels != 2 || refProcessDat_Data.outputs[0].numChannels != 2)
	{
		return kResultTrue;
	}

	Sample32* pSample32_inL = refProcessDat_Data.inputs[0].channelBuffers32[0];
	Sample32* pSample32_inR = refProcessDat_Data.inputs[0].channelBuffers32[1];
	Sample32* pSample32_outL = refProcessDat_Data.outputs[0].channelBuffers32[0];
	Sample32* pSample32_outR = refProcessDat_Data.outputs[0].channelBuffers32[1];

	for (int32 int32i = 0; int32i < refProcessDat_Data.numSamples; int32i++)
	{

		pSample32_outL[int32i] = pSample32_inL[int32i];
		pSample32_outR[int32i] = pSample32_inR[int32i];

	}

	return kResultTrue;
}

tresult PLUGIN_API CxxProc::getState(IBStream* pIBStream_State)
{
	pIBStream_State->write(&int32OptionMenu01, sizeof(int32));
	pIBStream_State->write(&ParamValue_Knob01, sizeof(ParamValue));
	return kResultOk;
}

tresult PLUGIN_API CxxProc::setState(IBStream* pIBStream_State)
{
	tresult tresult_r;
	tresult_r = pIBStream_State->read(&int32OptionMenu01, sizeof(int32));
	if (tresult_r != kResultOk) { return kResultFalse; }

	tresult_r = pIBStream_State->read(&ParamValue_Knob01, sizeof(ParamValue));
	if (tresult_r != kResultOk) { return kResultFalse; }

	return kResultOk;
}

tresult PLUGIN_API CxxEdit::initialize(FUnknown* pFUnknown_Context)
{
	tresult tresult_r = EditController::initialize(pFUnknown_Context);
	if (tresult_r == kResultTrue)
	{
		StringListParameter* param1 = new StringListParameter(STR16("OptionMenu_Para1"), INT32_OPTION_MENU);
		param1->appendString(STR16("TEXT1"));
		param1->appendString(STR16("TEXT2"));
		param1->appendString(STR16("TEXT3"));
		parameters.addParameter(param1);//SteinBerg::Vst::parameters.addParameter()

		RangeParameter* param2 = new RangeParameter(STR16("Knob_Para1"), PARAMVALUE_KNOB, STR16(""), 0.5, 30.0, 0.5);
		parameters.addParameter(param2);//SteinBerg::Vst::parameters.addParameter()
	}

	return tresult_r;
}

tresult PLUGIN_API CxxEdit::getState(IBStream* pIBStream_State)
{
	return kResultOk;
}

tresult PLUGIN_API CxxEdit::setState(IBStream* pIBStream_State)
{
	return kResultOk;
}


tresult PLUGIN_API CxxEdit::setComponentState(IBStream* pIBStream_State)
{
	tresult tresult_r;

	int32 int32TmpOptionMenu01;
	tresult_r = pIBStream_State->read(&int32TmpOptionMenu01, sizeof(int32));
	if (tresult_r != kResultOk) { return kResultFalse; }

	ParamValue ParamValue_TmpKnob01;
	tresult_r = pIBStream_State->read(&ParamValue_TmpKnob01, sizeof(ParamValue));
	if (tresult_r != kResultOk) { return kResultFalse; }


	ParamValue ParamValue_Normalvalue;
	ParamValue_Normalvalue = plainParamToNormalized(INT32_OPTION_MENU, (ParamValue)int32TmpOptionMenu01);
	setParamNormalized(INT32_OPTION_MENU01, ParamValue_Normalvalue);

	ParamValue_Normalvalue = plainParamToNormalized(PARAMVALUE_KNOB, ParamValue_TmpKnob01);
	setParamNormalized(PARAMVALUE_KNOB01, ParamValue_Normalvalue);

	return kResultOk;
}

IPlugView* PLUGIN_API CxxEdit::createView(FIDString FIDString_Name)
{
	if (strcmp(FIDString_Name, ViewType::kEditor) == 0)
	{
		return new VSTGUI::VST3Editor(this, "view", "myEditor.uidesc");
	}
	return nullptr;
}

} } // namespace SteinbergとVstの終わり

 このサンプルはOption MenuコントロールとKnobコントロールを追加するコードです。CxxProcクラスが内部的な計算だけをする役割のクラスで、CxxEditクラスがインターフェイスの制御をする役割です。


 ヘッダファイルの28行目、29行目で列挙型を使って以下のようにIDとして機能する変数を定義しています。


  • INT32_OPTION_MENU01 = 1
  • PARAMVALUE_KNOB01 = 2


 この番号を使って67行目以降でIDによる操作されたコントロールの識別と168行目以降でコントロールの初期化のときに使われるIDとしています。ヘッダファイルで定義したCxxProc内のアクセス指定子protected:部で対応する内部変数を以下のようにしています。


  • int32OptionMenu01
  • PARAMVALUE_Knob01


 このメンバ変数に値を保持させます。メンバ変数には実際に利用するときの値が格納されます。今回の例ではint32OptionMenu01には0,1,2の3種類。PARAMVALUE_Knob01には0.5~30.0です。これらの値はDAWのホストが認識する際に0~1の値に置き換えて管理する必要もあります。


 置き換えを正規化とも言います。0,1,2を0~1で管理する場合は、0 = 0.0, 1 = 0.5, 2 = 1.0のように割り当てられます。0~1の範囲になった値の事を正規化された値と呼ぶことにします。


 0.5~30.0の場合は0.5が0.0に対応、30が1.0の対応になります。0.5~30.0のように0.5から始まる値を扱う場合は0が基準になるより差分で考えます。つまり、30 - 0.5 = 29.5 ですから、あたかも0~29.5の値を管理するように考え、戻す時には+0.5をする。後で0.5加えればいいので、それぞれの保持値を29.5で割った値を管理していれば0~1の値に変換できることになります。


 0.5~30の範囲を0.0~1.0で表現するための関数がありまして、170行目にも登場するplainParamToNormalized()という関数です。第一引数に正規化するコントロールのID、第二引数に正規化される前の値を付与します。戻り値に正規化された値が返ります。


 インターフェースで扱う場合は最初に初期化が必要です。初めてVSTインターフェイスが描画されるとき、EditControllerクラスから派生したクラスCxxEditクラスの関数CxxEdit::Initialize(FUnknown* context)が呼び出されます。129、135行目のような関数を実行してコントロールにID、Tags名称、値の範囲、初期値、単位を結び付ける処理が必要です。


 Optiom Menuコントロールのように文字列を制御する場合は129行目のStringListParameter関数で結び付けます。Knobコントロールのようにノブの回転量を制御する場合はRangeParameter関数で結び付けます。