VSTiの作り方
6.VSTエフェクト実践編
よし、まず
http://www.steinberg.net/en/company/3rd_party_developer.html
からVST Audio Plug-Ins SDKを落とすんだ。 ver 2.4がおすすめだぞ
SDKを落とすには3rd party developerアカウントを作る必要がある。 My Steinberg アカウントだけだと落とせないぞ。ここは正直に(*)の付いた項目、 名前、住所、電話番号なんかまで入れないと駄目みたいだぞ。
開発環境は整っているか? VC++ が必要だ。Express Editionでもいけるはずだ
vstsdk2.4\public.sdk\samples\vst2.x\again\win にあるagain.slnを開いてみろ
これが一番簡単なサンプルだ。 とりあえずそのままビルドするとreleaseディレクトリにagain.dllができているはずだ。 それを自分のDAWのvstpluginsディレクトリに放り込んで動作を確認するのだ。
動いたか? GUIもないパラメータ1個だけの素っ気無い奴だが、音量の制御が できるはずだ。
動作が確認できたらソースをチェックだ。
肝の部分はagain.cppにある。processReplacing()とprocessDoubleReplacing()だ。
vst2.3まで(だったかな)はfloatしか扱えなかったが今はdoubleでもデータを扱えるように なっているので関数が2つある。でも平行して作っていくのは面倒なので、 取りあえず最初はfloatを扱う processReplacing()だけでいいぞ。
again.cppの最初の方にあるAGain::AGain()で canDoubleReplacing()っていうのが 呼ばれているな。これをコメントアウトしてしまえばdoubleの処理は必要なくなるぞ。
processReplacing()の中に
(*out1++) = (*in1++) * fGain; (*out2++) = (*in2++) * fGain;ていうのがあるな。これが音量を制御している部分だ。 2行あるのはL/Rの2chという意味だぞ。
信号は -1.0から+1.0の範囲だ。 fGainは音量のパラメータでこれは0.0から1.0まで変化するぞ。
じゃあビットクラッシャーでも作ってみるか。なんでビットクラッシャーかというと バッファ処理せんでいいから簡単なのだ。
例の2行をこんな感じにしてDoEffect()にまとめる。
(*out1++) = DoEffect(*in1++,fGain); (*out2++) = DoEffect(*in2++,fGain);
fGainは使い回しだ。真面目に作る人はちゃんとクラスのメソドにしてくれ。
でDoEffect()は
float DoEffect(float x,float fGain) { float fStep; if(fGain==1.f) return x; fStep=fGain*64.f+1.f; return (float)((int)(x*fStep))/fStep; }
こんなんでどうだ?
ビットクラッシャーと言っても2のn乗じゃなくてもいい。階段状にするだけだ。 fStepがステップの数だ。fGainが0-1なのでfStepは1-65になる。 ただしfGainを振り切っている65なら原音のまま戻してみる。 単に入力をステップの数でかけて整数化して元のレベルにもどしているだけだ。
これでビルドして試してみてくれ。Gainを小さくするとビット数が下がる。目一杯 にすると原音のままだ。
できたか?
そういえばパラメータの表示がGainでdB表示のままだな
again.cppの中にAGain::getParameterName()てのがある。 ここでパラメータの名前が決まる。
vst_strncpy (label, "Bits", kVstMaxParamStrLen);
て書き換えちゃえ
ちょい下にAGain::getParameterLabel()てのがあるな。これが単位(dB)だ。 これも書き換えちゃえ
vst_strncpy (label, "bits", kVstMaxParamStrLen);
だ
肝心の数値の表示はAGain::getParameterDisplay()で決まる。 fGain 0.0~1.0に対してfStepで使った式 fGain*64.f+1.f ステップになるぞ。 これをビット数に直すのだ。
void AGain::getParameterDisplay (VstInt32 index, char* text) { float fStep=fGain*64.f+1.f; if(fGain==1.0) strcpy(text,"--"); else sprintf(text,"%f",log10f(fStep)/log10f(2.f)); }
logとか使ってるから<math.h>のインクルード忘れずにな。あ、<stdio.h>もいるかな。 fGainが振り切ってる時は "--"表示にしてる。 でなければ、log10f(fStep)/log10f(2.f)、つまり2を底とした対数だな。数学覚えてるか?
これでbits表示になるぞ。多少誤差あるけどな。
エフェクトの名前がagainのままだったな
同じくagain.cpp内の
AGain::getEffectName()とか
AGain::getProductString()とか
AGain::getVendorString()とかを好きなように書き換えてみれ。
何? 名前が変わらん? そりゃ、ファイル名がagain.dllのままだからな。 DAWによって何処から名前を取っているか違うかも知れんがな。 Cubaseだと表示されるエフェクト名はファイル名だと思うぞ。
プロジェクトのプロパティで取りあえず出力ファイル名をBitCrusher.dllにでも したらどうだ? DAWによるが、ファイル名変えたらDAWのプラグイン情報とかは再スキャン した方がいいかも知れんぞ。
何か面白いものが出来たら誰かに使ってみてもらうかぁ!! て前にだ。
AGain::AGain()の中に、setUniqueID ('Gain');てのがあるな。これがVSTのユニークIDだ。 全てのプラグインでユニークな値を持つ必要があるIDだ。みんなが'Gain'てIDのプラグイン を作ったら困った事になる。まあ、最近のDAWだとIDがぶつかってても大丈夫だったりする らしいけど、ものによっては動かんかも知れん。
作法に則ってユニークIDを付けるのだ!!
でどうやって?
適当に付けたらぶつかるかも知れんじゃないか。 て事でここに行くのだ。
http://ygrabit.steinberg.de/~ygrabit/public_html/index.html
なんだこの怪しいページは・・・と言ってはいかん。
VST開発の責任者な人のページであるぞ。
真ん中あたりに「Register a VST Audio Plug-In ID 」てのがあるはずだ。
ここで登録して4文字のIDをゲットだ!
正直なんでこんな所にあるのかわからんし、現在もちゃんとIDのデータベースとして 機能してるのか疑問が無くもないが、まあ、ここが正式なはずだ。
ここで取得したID文字列をsetUniqueID()に渡すのだ。
あ、ID変えたらDAWのプラグイン情報の再スキャンを忘れずにな!
さて、パラメータ増やしてみるか
さすがに1つだけだと寂しいしな。
パラメータの数がどこで決まるかというと、
AGain::AGain()の所だな。AudioEffectXクラスを継承してるだろ
そこの引数が(audioMaster,1,1) だけどこれの最後のパラメータだ。コメントにも 1parameter only ってかいてあるよな。
(audioMaster, 1, 2)
ってしちゃえ。これでパラメータ2つだ。
これだけでDAW上のツマミは2個になるけど実体が伴ってないな。
どうやって扱うかというと、下の方のsetParameter()とかgetParameter()とか パラメータ関係のメソドのindexって言う引数だ。こいつが0から始まる何番目の パラメータかをあらわすぞ。
だから
void AGain::setParameter (VstInt32 index, float value){ switch(index) { case 0: fBits = value; break; case 1: fFreq=value; break; } }
て感じでindexで切り分けてくれ。あ、ちなみに0番目の変数はfGainじゃなくてfBitsにしたからな。
AGainのクラスの宣言でfGainて変数があった所を
float fBits; float fFreq;
てしたぞ。
さて、いじるメソドはsetParameter(),getParameter(),getParameterName(),getParameterDisplay() getParameterLabel() て所か。結構多いな。全部indexが渡ってるからそれで切り分けるんだ。
新たに作ったパラメータはfFreqだ。
名前は"Freq", 単位は"kHz",表示は1.0なら"--"、それ以外はfFreq*47.+1.にしてみる これで、1kHz~48kHzて事だな。
getParameterDisplay()はこんな感じだ!
void AGain::getParameterDisplay (VstInt32 index, char* text) { float fStep; switch(index) { case 0: fStep=fBits*254.f+2.f; if(fBits==1.0) strcpy(text,"--"); else sprintf(text,"%f",log10(fStep)/log10(2.0)); break; case 1: if(fFreq==1.0) strcpy(text,"--"); else sprintf(text,"%f",fFreq*47.+1.); break; } }
あ、Bitsの式もちょっといじったな。 Stepが1だと音にならないから 2~256 ステップになるようにしたぞ。
このパラメータで何がしたいかというと、時間方向の分解能を荒くするっていうのを やってみるのだ! 何かパラメータのバリエーションが苦しいが、まあ、壊し系だからな。
何つうか、ほれ。サンプル&ホールド的な処理をfFreqの周波数でするのだ。
ただそのためにはチャンネル毎の前の値を覚えとく必要があるぞ。
注意事項としては、エフェクターを複数使って複数のインスタンスが作られたりするのでな、 覚えとく必要があるものは、メンバー変数にしておくのがいいぞ。
ついでにDoEffect()もメンバーにしておくか。いちいちパラメータ渡すのもアレだしな。
という事でagain.hのクラス宣言に追加だ!!
float fBits; float fFreq; float fPhase; float fLastVal[2]; float DoEffect(int iCh,float x);
こんだけ。
DoEffect()の一番目引数はチャンネルを渡すのだ。fLastVal[]に前の値を覚えるぞ!! fPhaseはfFreqで指定する周波数に対する位相だ!!
processReplacing()の2行はこう!!
(*out1++) = DoEffect(0,*in1++); (*out2++) = DoEffect(1,*in2++);
DoEffect()はこうだ!!
float AGain::DoEffect(int iCh,float x) { float fStep; float fTemp; fPhase-=1.0/sampleRate; if(fFreq==1.0 || fPhase<0.f) { fPhase+=1.0/(fFreq*47000.+1000); if(fBits<1.f) { fStep=fBits*254.f+2.f; x=(float)((int)(x*fStep))/fStep; } fLastVal[iCh]=x; } return fLastVal[iCh]; }
sampleRateはDAWが動いてるサンプリング周波数だ。どこからきたかって? スーパークラスのAudioEffect (audioeffect.h)で宣言されているのだよ。
DoEffect()はサンプル毎に呼ばれるからな、呼び出し毎に 1.0/sampleRate (秒) 経過しているのだよ。それを引いていってマイナスになったら 1.0/(fFreq*47000+1000) つまり設定した周波数の周期を足すのだ!! これでDAWの動作周波数にかかわらず 設定した周期で処理するぞ!
処理の中身は前回のビットクラッシャーと同じな(ステップの設定が違うが)
という事で簡単VSTエフェクト、BitCrusherができました。
BitCrusher.dllと変更したソースはここにあります。
bitcrusher.zipじゃあの!!