したらばTOP ■掲示板に戻る■ 全部 1-100 最新50 | まとめる | |

C++でVST作りの指摘・質問

1 管理人 :2010/12/10(金) 18:06:52
「C++でVST作り」内の誤記やサンプルコードのバグ、指摘・質問等がございましたらこちらへお願いいたします。

C++でVST作り→http://www39.atwiki.jp/vst_prog/

146 管理人★ :2017/11/15(水) 22:25:03
らいかさん
無事解決してよかったです。

147 霧林 :2017/12/05(火) 02:51:52
初めまして。VST プラグインの制作を試みている学生です。
VST 開発について詳細に解説しているサイトは少ないので、
「C++でVST作り」は大変参考になっています。貴重なサイトをありがとうございます。
プログラミング及び VST 初心者なのでわけのわからないことを書いてしまうかもしれませんが、質問をしてもよろしいでしょうか。

簡単なエフェクターを作成してみる - http://vstcpp.wpblog.jp/?page_id=314
こちらの記事を始まりとした、VST2.4 で実装しているトレモロエフェクトについての質問です。
このトレモロエフェクトに、「入力信号にトレモロ効果がかかるまでの時間(ディレイ)」
というパラメータを実装したい場合、どのようにすればよろしいでしょうか?

管理人様が実装イメージとして書かれているプログラムでは、
for(int i = 0; i < wavelength; i++)
この for 文内で入力信号の長さ分、信号処理を行っていることがわかります。
なので、例えばサンプリング周波数 44100Hz の入力信号に対して、
トレモロ効果がかかるまで1秒間あけたい場合は、単純に考えると

for(int i = 0; i < wavelength; i++)
{
 if(i <= 44099) {
  output[i] = input[i];
 }
 else if(i >= 44100) {
  //トレモロの計算…省略
  output[i] = b * input[i];
 }
}

このように実装すればよいと考えました(数値がダイレクトなのはご容赦ください)。
i が 0〜44099 の間(1秒間)は入力信号をそのまま出力信号に代入して、
i が 44100 以降は入力信号にトレモロ処理を施した値を出力信号に代入するといった具合です。

しかし、実際に prosessReplacing 関数内で上記イメージのように書き直してもうまく動いてくれません。
ビルドはできますが、実際に使用してみるとトレモロ処理に移らない感じです。
これを望みの挙動にするためには、どのようにすればよろしいでしょうか。

また、prosessReplacing 関数では、実装イメージの wavelength が sampleFrames という変数になっていますが、
この sampleFrames という変数がよく理解できていません。そのためにうまくプログラムが書けないのだと思いますが…
これは、入力信号の長さという単純なものとは違うのでしょうか?

現在 VST3 について意欲的に更新されている最中、旧バージョンの話題で申し訳ありません。
お時間のある時にでもご回答いただければ幸いです。

148 管理人★ :2017/12/05(火) 20:30:31
霧林さん

書き込みありがとうございます。ST2.4でも全然問題ありません。

簡単に言うとsampleFramesは「入力信号の一部の長さ」になります。

ホストアプリ(DAWなど)は入力信号を数ミリ秒ずつ順番にVSTに渡していきます。
音声処理をリアルタイムに行うためにこのような方法がとられます。

1回に渡す入力信号は数ミリ秒ですので、sampleFramesはだいたい50〜200ぐらいになります。
1秒分の長さを処理するためにホストアプリ(DAWなど)はprosessReplacing関数を200〜1000回ぐらい呼び出します。
(sampleFramesや何回呼び出されるかは環境によって異なります。)

いただいた例の「VSTを読み込んでから1秒後にトレモロがかかるようにする」で、
ずっとトレモロがかからなかったのは、「 if(i <= 44099) 」の条件に合致していたからだと思います。
(for文のint iは毎回 0 になりますし、sampleFramesは大きくても200ぐらいですので)

ですので、実際に例の通りに動かそうとすると、VSTが読み込まれてからの時間(サンプル数)を
保持しておく必要があります。実装例としては下記となるとおもいます。

①メンバー変数としてVSTが読み込まれてからのサンプル数を保持する変数を追加。
【例】
VstInt32 totalSample;

②上記変数をコンストラクタ等で 0 に初期化しておく。
【例】
totalSample = 0;

③prosessReplacing関数のfor文内で処理を行う。
 経過したsampleFrames分はtotalSampleに加えていく。
【例】
for(int i = 0; i < sampleFrames; i++)
{
 if(totalSample < 44100) {
  output[i] = input[i];
totalSample++;
 }
 else {
  //トレモロの計算…省略
  output[i] = b * input[i];
 }
}

149 霧林 :2017/12/06(水) 03:41:54
管理人様

迅速なご回答ありがとうございます。わかりやすい解説で助かります!
無事トレモロ効果がかかるまでの時間を設定することができました。
いくつか気になる点があるので、また質問させていただきます。
(ちなみにホストアプリは VSTHost (http://www.hermannseib.com/vsthost.htm) を使用しています。
その他の環境は Windows 7 (64bit), Visual Studio 2015 Community です)


ご教示いただいた実装例でそのまま実装してみたところ、
「信号が入力されてから1秒後」ではなく、「VST を読み込んでから1秒後」になるようですね。
VSTHost でビルドした VST を読み込んだところ、
読み込み1秒後からは入力信号に常時トレモロがかかる状態になりました(普通のトレモロエフェクトと同じ状態)。
信号の入出力を全くしなくても、設定した時間が経過次第この状態になるので、
processReplacing 関数は信号の入力がない状態でも呼び出され続けているのでしょうか?

今回私が目指しているものは
「VST 読み込みから n 秒後」ではなく、
「毎回、エフェクトに信号が入力されてから n 秒後」なので(わかりにくくて申し訳ありません)、
管理人様のコードを下記のようにしてみました。

if (inputs[L][0] == 0 && inputs[R][0] == 0){ //入力信号の先頭が 0 の時
 totalSample = 0; //totalSample を 0 に固定しておく
}
else{
 for (int i = 0; i < sampleFrames; i++){
  if (totalSample < 44100){
   outputs[L][i] = inputs[L][i];
   outputs[R][i] = inputs[R][i];
   totalSample++;
  }
  else{
   //トレモロの計算…省略
   outputs[L][i] = b * inputs[L][i];
   outputs[R][i] = b * inputs[R][i];
  }
 }
}

時間は例として 44100=1秒 としています。

VST を読み込んでから1秒経つと、それ以降の入力信号にトレモロがかかりっぱなしの状態になるのは、
processReplacing 関数が呼び出され続け、totalSample がリセットされないことが原因だと推察し、
for 文に移る前に上記のような if 文を書いてみました。

このように実装すると、毎音入力から1秒後にはトレモロがかかるようになりました。
しかし、最初 if 文の条件を「入力信号のバッファ(?)の先頭が 0 」としているため、
入力信号のリリースがわずかでも残っていると、その次に入力した信号にもトレモロがかかってしまいます。
文章だと少しわかりにくいので、今回のトレモロを録音してみました。

http://up.cool-sound.net/src/cool53865.mp3

ド〜、ミ〜、ソ〜 | ド〜ミ〜ソ〜
と鳴らしています。

前半のように各音きちんと区切って鳴らす分には問題ありませんが、
後半のレガートのような演奏をすると、入力信号が 0 にならないため、
totalSample がリセットされず継続してトレモロがかかってしまいます。

これを信号が入力(ノートオン)される度に、
1秒経ってからトレモロがかかるようにするには、どのようにすればよろしいでしょうか。
ノートオンという言葉の通り、MIDI 信号の処理も必要になりますかね?

面倒くさい質問になってしまい申し訳ありません(汗

150 管理人★ :2017/12/06(水) 22:19:18
霧林さん

いつ入力信号があるか分からないので、processReplacing関数は
信号の入力がない状態でも呼び出され続けます。


「毎回、エフェクトに信号が入力されてからn秒後」とするにはなかなか難しいです。

信号が入力されたという状態をどう考えるか?や
エフェクターをかける対象をどんなものにするか?で変わってくると思います。

記載いただいているサンプルだとおそらくノイズが少しでも乗ると
「信号が入力された」という状態になってしまいます。

信号が入力されたかどうかを判断する方法の一つに音圧をもとにするものがあります。
コンプレッサーでアタックやリリースを出すときに使われる方法です。
RMS(2乗平均平方根)を使えば音圧を取得できるので、RMS値が一定値を超えたら
入力があったとみなす方法です。
ただし、小さい音やオルガンなどの減衰のない音に対してはあまり効果がないと思います。

他にも音程が変わったら入力があったとみなす方法もあると思います。
音圧と組み合わせてみるのもいいかもしれません。

エフェクトをかける対象がシンセで必ずMIDIメッセージを取得できるなら
MIDIノートオンメッセージを条件にすると簡単で正確だと思います。

回答になっていないですが、ご参考になればと思います。

151 霧林 :2017/12/12(火) 01:46:15
管理人様

返信が遅くなってしまい申し訳ございません。ご回答ありがとうございます。
processReplacing 関数、信号の入力がない状態でも呼び出されているのですね。

なるほど、信号が入力される度に…となると難しいのですね。
私がエフェクトをかける対象として考えているのは、今のところソフト音源(VSTi)のみなので、
MIDI ノートオンメッセージを条件にしてみる方法で挑戦してみます。

今回、エフェクト効果がかかるまでの時間の実装方法について質問をした理由を一応説明しておくと、
時間機能を付けたビブラートの VST エフェクトを作りたいなと思っているからです。
イメージとしては、MIDI のビブラート系の CC にある
Vibrato Rate, Depth, Delay のうちの”Delay”の実装ですね。

また何かわからないことがあれば質問するかもしれませんが、
その時はよろしくお願いいたします。

152 管理人★ :2017/12/12(火) 20:45:12
霧林さん

とりあえずは解決してよかったです。
また何かあればよろしくお願いします。

153 SNRER :2018/02/09(金) 16:56:10
管理人さま

はじめまして
VST作成に役立つ資料いつも参考にさせていただいております。
オーディオFxのVSTを作成中なのですが、
ホスト側で違うチャンネルに2つ以上自作VSTを起動した際に、コントローラの値が
同期してしまい、一つのVSTの値を変更すると起動しているVST全ての同コントローラの値が変わってしまっております。

複数起動の場合には何か特別な処置を施す必要がありますでしょうか。
初歩的な質問で申し訳ないです。。

154 管理人★ :2018/02/10(土) 21:56:11
SNRERさん

書き込みありがとうございます。
複数のVSTプラグインを起動する際に特別な処理は必要なかったと思います。

考えられるとすれば…
コントローラの値を保存する変数をグローバル変数にしたり、staticで定義したりすると
複数のスレッドで値が共有されてしまったと思います。

状況がわかればもう少し詳細に回答できるかもしれません…。

155 SNRER :2018/02/13(火) 16:54:37
管理人さま

GUI、コントローラ、プロセッサで値を共有するため、グローバル変数を使用しております。
GUIがオートメーションに対応して自動で動けなかったのをなんとかしようと、
グローバル変数に頼っておりました。
ここが問題ですね。力技になってしまっているので、複数スレッドの事まで考慮できていませんでした。

SDKのサンプルコードをみていると、バインディングする記述があったので、
もう少し試してみようと思います。

もし簡単な解決法があればご教示いただけますと幸いですが、
 特別は処理は必要ないこと
 全ての値が変わってしまう原因が分かった
ことで、大変助かりました。

ありがとうございました。

156 管理人★ :2018/02/13(火) 21:47:28
SNRERさん

とりあえず解決(?)してよかったです。

> SDKのサンプルコードをみていると、バインディングする記述があった
というのがすごく気になります…
どのサンプルなのか教えていただけると助かります。

またオートメーションの際にGUIに値を更新する方法ですが、
実は私もよくわかっていません。

VST GUIには標準でタイマーがあるので、タイマーを使うと比較的簡単にできるのでは?
と考えていますが、正しい実装ではないような気がしています。

157 管理人★ :2018/02/13(火) 21:57:22
ちなみに…
タイマーの使い方はVSTGUIEditorクラスのnotify関数をオーバライドして使います。

下記のような方法で実装すればオートメーションに伴ってGUIが更新されるはずです。
(動作確認したわけではないので、動かなかったら教えてください。)
なお、インデントは全角スペースになっているのでご注意ください。

①VST GUIクラス(VSTGUIEditorを継承したクラス)でコントロール用の変数を定義
 (ここではつまみ(ノブ)コントロールとしています。)
【例】
CKnob* knob1;

②notify関数をオーバーライド
【例】
public:
 CMessageResult notify(CBaseObject *sender, const char *message);

③open関数でコントロールを作成した際にポインタを保存しておく
【例】
bool PLUGIN_API MyVSTGUIEditor::open(void* parent, const PlatformType& platformType)
{
// GUIウィンドウが開かれたときに、UIを作成する
  〜〜 中略 〜〜

  // つまみ(ノブ)コントロールを作成( createKnob関数は http://vstcpp.wpblog.jp/?p=1550 参照)
  knob1 = (CKnob*)createKnob(1, 20, 20);

  〜〜 中略 〜〜
}

④notify関数でタイマーイベントを受け取り、コントロールを更新
 (コントロールの更新はsetDirty関数を使います。)
【例】
CMessageResult MyVSTGUIEditor::notify(CBaseObject *sender, const char *message)
{
  if (message == CVSTGUITimer::kMsgTimer)
  {
    if (knob1 != nullptr)
    {
      knob1 ->setDirty(); // 表示を更新
    }
  }

  return VSTGUIEditor::notify(sender, message);
}

158 管理人★ :2018/02/13(火) 22:15:27
すいません…。
「④notify関数でタイマーイベントを受け取り、コントロールを更新」で
パラメーターの値を取得する必要がありそうです。

CMessageResult MyVSTGUIEditor::notify(CBaseObject *sender, const char *message)
{
  if (message == CVSTGUITimer::kMsgTimer)
  {
    if (knob1 != nullptr)
    {
       // 現在の値をパラメーター操作クラスから取得し反映する
      ParamID tag = 1;
      ParamValue value = controller->getParamNormalized(tag);
      knob1->setValueNormalized(value);

      knob1->setDirty(); // 表示を更新
    }
  }

  return VSTGUIEditor::notify(sender, message);
}

159 SNRER :2018/02/14(水) 18:38:32
管理人さま

詳しいご説明誠にありがとうございました。
私はまだプログラミング初心者ですが、サイトも今回の解説もとても分かり易く感謝しております。

GUIクラスで常に呼ばれる系の関数が見つけられずにいましたが、notifyがまさにそれなのですね。
解説いただいた通りでオートメーションが追従しました。さすが管理人さまです!

バインディングについてですが、「note_expression_synth_ui」を見ておりました。
実際に使用してみるとGUIを使ったVSTで、オートメーションに追従していたので、
この中にヒントがあると思い探っておりましたが、「setValueNormalized」は見つかっておりません。
やはり何か実装が違うのでしょうか。。

また、
「#include "vstgui/plugin-bindings/vst3editor.h"」の中の

//-----------------------------------------------------------------------------
//! @brief VST3 Editor with automatic parameter binding
//! @ingroup new_in_4_0
//-----------------------------------------------------------------------------

からの部分が怪しい気がしましたが、勉強不足で理解できておりません。。。。

加えて、大変不躾ではございますが、
今回ご教示いただいた「kMsgTimer」ですと、再生バーを動かしただけでは反応しませんが、
「note_expression_synth_ui」では再生開始しなくても、
バーを違う地点に動かすだけで、その地点のオートメーションの値がGUIに反映されておりました。
(私が作っているのはエフェクトで、このVSTはインストゥルメントという違いはありますが・・・)

できれば「note_expression_synth_ui」同様に再生開始前に追従したいのですが、
kMsgTimer以外のメッセージ種別は何かありますでしょうか?
(探してはみたのですが、みつからず・・)

大変お手間をとらせて申し訳ありません。

160 管理人★ :2018/02/14(水) 22:57:52
SNRERさん

バインディングの情報ありがとうございます。

私もオートメーションと再生バーの追従はまだ試していないのでもう少し確認してみます。

kMsgTimer以外のメッセージもたしかあったのですが、再生やバー移動などの関連の
メッセージはなかったと思います。

「note_expression_synth」で再生バーの追従が出来ているということは
おそらく、notify関数でkMsgTimerを使うのはあまりいい方法ではないと思います。

あいまいな回答で申し訳ありません。

161 SNRER :2018/02/15(木) 10:01:08
管理人さま

ご返答ありがとうございます。
こんな素人の質問に付き合っていただき本当に感謝しております。
私も引き続き調べてみようと思います!

162 管理人★ :2018/02/15(木) 23:04:52
SNRERさん

私も>>157-158の方法を試してみました。

つまみ(ノブ)等の実装方法(下記)をベースにnotify関数
で更新する方法をとりましたが、再生バーを移動させた際も
コントロールの状態は更新されました。

http://vstcpp.wpblog.jp/?p=1230

SDKは3.6.8を動かせるDAWがないので使用したSDKは3.6.0になります。
DAWはSteinberg Sequel 3です。

可能であれば使用しているSDKやDAWなどを教えていただけますでしょうか?

また、動かないのであれば、setDirty関数をinvalid関数に変えて
試していただけますでしょうか?
(インデントは全角スペースになっているのでご注意ください。)

④notify関数でタイマーイベントを受け取り、コントロールを更新
CMessageResult MyVSTGUIEditor::notify(CBaseObject *sender, const char *message)
{
  if (message == CVSTGUITimer::kMsgTimer)
  {
    if (knob1 != nullptr)
    {
       // 現在の値をパラメーター操作クラスから取得し反映する
      ParamID tag = 1;
      ParamValue value = controller->getParamNormalized(tag);
      knob1->setValueNormalized(value);

      knob1->invalid(); // 表示を更新
    }
  }

  return VSTGUIEditor::notify(sender, message);
}

なお、「note_expression_synth」の中身を確認しましたが、
オートメーションと再生バーの追従方法は自前でクラスを用意していました。

サンプルの実装方法を見る限りでは、
notify関数のkMsgTimerで更新する方法でも問題なさそうです。

163 SNRER :2018/02/16(金) 15:27:56
管理人さま

ご返信ありがとうございます!
本当に丁寧に考えていただき、とても嬉しいです。

まず、私の環境についてですが、
DAW SONAR Platinum(つい先日開発中止になってしまいましたが・・・・)
SDK 3.6.8

となります。

ご教示いただいた「invalid」についてですが、
「setValueNormalized」の行までだけで表示は更新されている様で、
「invalid」「setDirty」
どちらをコメントアウトしても動作しております。
また、「invalid」を試してみましたが、こちらの環境では再生バーに追従できませんでした。

その後に、VisualStadio からSONARにアタッチしてデバッグをしていて
 再生バーを動かしただけでもnotify内のif分の中まで入ってきているが
 getPramで取得してくる値が再生バーを動かす前の値がそのまま残っている。
という事がわかりました。

しかし、再生バーを動かしてプロセッサ関数に来た時に
「getPoint」で取得するvalueは再生バーを動かした後の最新の値がホストから提供されていました。

この最新の値をコントローラ側でも把握できればいけそうですが、
EditController* controller
のパラメータがどのタイミングにどの様に更新されているのかを理解できておりません。

ちなみに先日のバインディングの件ですが、
怪しいと思っていたところはVST上でエディターを起動する事ができて、
それ上でのバインドの事を指している様な感じでしょうか。
実際VST上で起動してエディターの中身も見てみましたが、用意されたテンプレート以外の自分で作った絵を使う事が出来ない感じに見えましたので、
今回の自分がやりたい事にはマッチしない感じでした。
お騒がせしてすみません。。

引き続きいろいろと試してみます!

164 管理人★ :2018/02/16(金) 22:09:16
SNRERさん

ご確認ありがとうございます。少し整理のために、私の理解している動作を記載させていただきます。

まず、オートメーションや再生バーを動かした時、DAWからは状態とGUIを反映するため下記が行われる

 A)音声処理クラス(AudioEffectを継承したクラス)へは
  process関数の引数ProcessData& dataに値を設定し反映する。
  音声処理クラスは渡された値で内部状態を更新する。

 B)パラメータ操作クラス(EditControllerを継承したクラス)へは
  setParamNormalized関数を呼び出して変更を通知・反映する。(値を更新する)
  パラメータ操作クラスはsetParamNormalized関数のtagで指定された
  パラメータの値を更新する。

上記とは別にVST GUIクラス(VSTGUIEditoを継承したクラス)はタイマーを使って
0.1秒ごと(デフォルト時)にnotifyを呼び出している。

 C)notifyでは、パラメータ操作クラスから現在の値をgetParamNormalized関数で取得し、
  コントロール(ノブなど)の値を更新している。
  値更新後はsetDirty関数で画面UIの更新をDAWに通知し、DAWに再描画させる。(0.1秒ごと)

VST GUIクラスのEditController* controllerはパラメータ操作クラスへのポインタなので
B)のタイミングで値は更新されている。

C)は0.1秒ごとに呼び出されるので、どこかのタイミングでコントロールは更新される
再生バーを動かした時でも0.1秒以内には更新される。

以上が私の理解している動作になります。

いま、A)とC)は呼び出されていることが確認できており、B)が確認できていない状況で
間違いないでしょうか?

B)が呼び出されない可能性はDAWの実装に原因があるように思えます。
(ただ、サンプルVSTで動いているのが気になります…)

再生バーを動かしたときにB)が確認できるか?(DAWからsetParamNormalized関数が呼ばれているか?)
を確認してもらえますでしょうか?

また、呼び出されていても反映されていない状況もあるかと思います。
たとえばParameterクラスを継承して自作Parameterクラスを作っていて、そのクラスの実装が
間違っている場合などです。
自作Parameterクラス等は作っていないでしょうか?

最後にバインディングの件ですが、VST SDK 3.6.8からサンプルのVST GUIが
Inline UI Editor(VST3Editorクラス)を使うようになったようです。
このInline UI Editorはコード書かなくても、マウスでUIを作れる方法のようです
バインディングというのは、
 「マウスでコントロール(ノブなど)を作っても自動で割り当てしてくれる」
という意味だと私は理解しています。

長文失礼しました。以上よろしくお願いします。

165 SNRER :2018/02/20(火) 10:03:14
管理人さま

ご返信ありがとうございます!
作業できる環境から少し離れておりました。

 再生バーを動かしたときにB)が確認できるか
など確認して追ってご報告いたします。

166 SNRER :2018/02/20(火) 15:22:56
管理人さま

改めまして、パラメータ共有の流れについてご説明誠にありがとうございました!
いつも本当に丁寧なご回答助かっております。

まず、

いま、A)とC)は呼び出されていることが確認できており、B)が確認できていない状況で
間違いないでしょうか?

についてはその通りでございます。

vsteditcontroller.cpp
内の

tresult PLUGIN_API EditController::setParamNormalized (ParamID tag, ParamValue value)
{
Parameter* parameter = getParameterObject (tag);
if (parameter)
{
parameter->setNormalized (value);
return kResultTrue;
}
return kResultFalse;
}

のあたりでブレイクしてみましたが、
再生開始時には通っている様で、再生バーを動かしただけの場合には通りませんでした。

やはり再生していない場合では最新の値がセットされない事で、GUIの値が更新されないと思われます。

また、自作Parameterクラスなどは作っておりません。
バインディングについてもご説明いただきありがとうございました。

サンプルで動くという事は何かしらホストから発信はあるはずなので、
もう少しサンプルのソースを見てみようと思います。

167 管理人★ :2018/02/20(火) 21:43:34
SNRERさん

ご確認ありがとうございます。

EditController::setParamNormalized関数の「parameter->setNormalized (value);」
が呼び出されていないのですね…。

「note_expression_synth」サンプルソースもParameterクラスのsetNormalized関数から
更新するタイミングを受け取っているようなので、本来なら動かない気がします…。

「note_expression_synth」と同じ方法でGUIを更新する方法は今私も試しているところです。
なにか分かりましたら、また共有させていただきます。

あと、「つまみ(ノブ)等の実装方法」( http://vstcpp.wpblog.jp/?p=1230 )を元に
私がnotify試したサンプルを下記に保存しておりますので、よければご参考にしていただければと思います。

http://vstcpp.wpblog.jp/wp-content/uploads/2018/02/20180220_vst3.zip

168 SNRER :2018/02/21(水) 16:06:14
管理人さま

ご返信ありがとうございます!

ご指摘の通り、setParamNormalizedが呼ばれない事で、
「parameter->setNormalized (value);」
が実行されていないという感じでした。

「note_expression_synth」の
「note_expression_synth_controller.cpp」
でオーバーライドされている setParamNormalized でブレイクしてみましたが、
再生バーを動かすだけ、というか、SONARをアクティブにした時点でブレイクしました。

こちらは正常にsetParamNormalizedが呼ばれている様です。
何が違うのか・・・現状まだ分かりませんが、ホストは正しく動作している様です。

また、頂いたソースで試してみたのですが、
VST起動の際、 Sample32* inL = data.inputs[0].channelBuffers32[0]; がnullでエラーになったので、
変数への格納を
  for (int32 i = 0; i < data.numSamples; i++)
の中で行う事で動作しました。

ただし、簡易デバッグとして使っております 
 「Vst3HostDemo_x64.exe」
というソフトではそのままで問題無く起動しておりますので、
未再生状態でプロセッサを呼んでしまう様なSONAR独自の動きのせいでエラーになるのかもしれません。

続いて動作内容ですが、
再生開始からは同期しますが、バーを動かすだけでは追従しませんでした。

なぜ「note_expression_synth」では
 setParamNormalized
が再生バー移動でもコールされているのかが肝だと思いますので、
自分も引き続き調べてみます。
何か気付きがあればまたご報告いたします。

169 管理人★ :2018/02/21(水) 21:17:59
SNRERさん

何度もご確認ありがとうございます。

やはり私のサンプルでも動かなかったのですね…。
となると後は下記ぐらいの内容しか思いつきません…。

1.再生バーの動作ではsetState関数やsetComponentState関数が呼ばれている
 →setState関数やsetComponentState関数からsetParamNormalized関数が
  呼び出されている可能性があります。

2.パラメータ操作クラスで初期化時に何らかのフラグを立てている
 →パラメータ変更時にホストからsetParamNormalized関数を呼び出すような
  設定をしている可能性があります。
  (サンプルを見た感じではありませんでしたが…)

3.音声処理クラスから、パラメータ更新を受け取っている
 →音声処理クラスのprocessでdata.outputParameterChangesをセットし、
  パラメータの更新を通知している可能性があります。
  (たぶんこの可能性はないと思いますが…)

可能性としては以上になります。
上記の可能性でない場合、正直分からないというのが現状です。

170 SNRER :2018/02/26(月) 10:01:55
管理人さま

可能性についてピックアップありがとうございます!
1.2.3.について自分なりに見てみましたが、
現状答えにはたどり着けておりません。

方向性は変わりますが、
VST上でのエディターについてもう少し調べておりました。
どうもエディターで作った内容はリソースファイルとして出力される様で、
その中に各コントローラとタグを結びつける記述がありました。

自分で作ったPNGもエディターから使用する方法が分かりましたので、
ひとまずこちらで試してみようと思います。
(エディター上で、何故か他ドライブからPNGをインポートすると落ちるのですが、
 Cドライブからインポートなら落ちないという事が分かるまでに時間がかかりました)

また発見がありましたら共有させていただきます。

171 管理人★ :2018/02/26(月) 23:40:49
SNRERさん

ご確認ありがとうございます。原因が分からず残念です。

Inline UI Editor(VST3Editorクラス)の情報ありがとうございます。
どうもSDK 3.6.8からこのエディターが推奨されているようですが、
私はまだ試せていないので情報頂けると非常にたすかります。
(全部のサンプルがこのエディターになっていたのでそう考えています。)

また、試したわけではありませんが、私が分かっている範囲で
Inline UI Editor(VST3Editorクラス)でのUI更新方法について
分かる内容を記載させていただこうと思います。
本日はまだ整理できていないので近日中に記載させていただきます。

172 管理人★ :2018/03/02(金) 20:11:36
SNRERさん

遅くなりすいません。
Inline UI Editor(VST3Editorクラス)でのUI更新方法について
分かる内容を記載させていただきます。

Inline UI Editor(VST3Editorクラス)ではParameterクラスと依存関係を作成して、
Parameterクラスが変更されたとき(setNormalized関数が呼ばれたとき)に
コントローラを更新できるようにしているようです。

具体的にはvst3editor.cppのParameterChangeListenerクラスで実装されています。

①ParameterクラスのaddDependent関数を呼び出して依存関係にあるクラスを登録する。
 (VST3EditorクラスではコンストラクタでParameterChangeListener自身を登録)

②ParameterクラスのsetNormalized関数が呼び出されると変更されたことを
 依存関係にあるクラスに通知するためchanged関数が呼び出される。

③changed関数が呼び出されるとVST内部でいったんキューイングされた後、
 ParameterChangeListenerクラスのupdate関数が呼び出される。

④ParameterChangeListenerクラスのupdate関数内で関連付けられた
 コントロール(CControlクラス)の値を更新(setValueNormalized関数の呼び出し)し、
 再描画(invalid関数の呼び出し)する。

以上がInline UI Editor(VST3Editorクラス)の更新方法のようです。
ご参考までに。

173 SNRER :2018/03/12(月) 17:35:03
管理人さま

大変お返事遅くなりました。
申し訳ないです。

Inline UI Editorで進めていきたいのですが、
うまくバインドされずにまた困っておりました。

ご教示いただいた内容を紐解きつつ確認してみます!
また進捗あればご報告させていただきます。

174 管理人★ :2018/03/12(月) 22:18:33
SNRERさん

ご確認ありがとうございます。

私も最近Inline UI Editorを試しました。
慣れるまでちょっと大変なツールですね…。

バインドされないとのことですが、「Control-tag」の項目に
パラメーターのタグを設定してもバインドされないでしょうか…?

175 SNRER :2018/04/02(月) 17:19:30
管理人さま

ご無沙汰しております。
多忙により少しコードを触れずにおりました。

Control-tagにはタグ名称、その下のエリアで、ラベルとタグの記載をしているのですが、
それによって作られたコントローラが何の反応もなく(動かす事はできるのですが)
つまんで動かしてもvalueChangedにも来ていない状況です。

今、UI Editorの記事を拝見しました。まず、この記事に沿って構築し直してみようと思います。
貴重な記事大変感謝致します。

176 管理人★ :2018/04/02(月) 20:21:39
SNRERさん

こちらこそいろいろ情報を頂きありがとうございます。
valueChanged()が呼び出されていないとなるとちょっと分からないですね…

Inline UI Editorの記事がご参考になればと思います。

177 SNRER :2018/04/04(水) 10:41:47
管理人さま

parameters.addParameter(STR16("●●"), STR16("●●"), 0, 0, ParameterInfo::kCanAutomate, TAG, kRootUnitId);

の様に設定しておりましたが、ここをノートエクスプレッションに合わせて

Parameter* param;
param = new RangeParameter(STR16("●●"), TAG, STR16("●●"), 0, 100, 100);
param->setPrecision(1);
parameters.addParameter(param);

の様に変更する事で、オートメーション追従まではできました。

引き続き改めてコードとにらめっこしていますが、
UIエディターを使って作った場合は、そもそも「guieditor.cpp」などに相当する部分は必要なく、
「guieditor.cpp」に作った valueChanged なども必要無いという事になるでしょうか?(通っていなくて動いているので)


但し、再生バーの地点を動かしただけの場合にはやはり、
setPramNomalized が呼ばれておらず、表示も更新されません。

引き続き自分なりに調べてみますが、ひとまず動いたという事でご報告させていただきました。

178 管理人★ :2018/04/04(水) 21:30:12
SNRERさん

一つ問題が解決してよかったです。

Inline UI Editorで「guieditor.cpp」に相当するものは
VST SDK内にある「vst3editor.cpp」になります。
valueChanged関数もこの「vst3editor.cpp」で定義されています。

また、Sonarの再生バーを動かした場合、
パラメータ操作クラス(EditController)のsetComponentState関数が
呼び出されているのではないかと思っています。

ノートエクスプレッションのサンプルでは、setComponentState関数から
setParamNormalized関数が呼び出されていますのでこれによって
UIの状態が更新されていると考えています。

179 あると :2018/04/17(火) 18:59:40
管理人様

お世話になります.私,貴サイトを参考にVST3プラグインを作成しようとしているものです.
現在,貴サイトの「最小構成のVST3」の手順を行い,ビルドを行ったところなのですが,
問題が生じたので,もし何か解決の糸口が見つからないかと考え書き込ませていただきます.

現在,
Windows10(64bit)
Visual Studio 2017 community
VST SDK3.6.8
という貴サイトでの環境を構築し,
手順通り進めることでビルドが成功しました.
ソースコードは全て貴サイトのものを使用させていただきました.
生成された.vst3ファイルを動作確認するため,.vst3を
C:ProgramFiles\Common Files\VST3に入れました.

しかし,現在使用しているDAW「StudioOne3.5 professional」
でインサートプラグインとして使用したところ,
VSTプラグイン一覧上で自作VST3は見えるのですが,
インサートしてもインサート欄に追加されず,何も起きません.
ちなみに,他のVST2,3プラグインは正常にインサート欄に追加されて使用できます.

このような現象に心当たりはありますでしょうか?
DAWとの相性などもあるのでしょうか?.

辿々しい説明でわかりづらいとは思いますが,はじめの一歩でつまずいてしまい
大変困窮しておりますので,何卒ご返信いただければ幸いです.

180 管理人★ :2018/04/17(火) 20:06:42
あるとさん

書き込みありがとうございます。

頂いた事象についての心当たりはないのですが、
「最小構成のVST3」では何か足りないのかもしれませんね…
(説明しやすいようにかなり無理して削っている部分があるので…)

もし可能であれば、他のDAWで動作確認していただけないでしょうか?

VST SDKにもテスト用ホストが「VST3_SDK\bin\Windows 64 bit」にあります。
「VST3PluginTestHost_x64_Installer_2.6.0.zip」を解凍すればインストーラーがあります。
(私はこちらで動作確認しております。)

テスト用ホストで動くのであれば、パラメーター実装方法等で少し機能を追加してから、
再度Studio Oneで動作確認いただけますでしょうか?
(「最小構成のVST3」にはパラメーター操作クラスがないので、おそらくこのあたりで
はじかれているのではないかと推測しています。)

181 あると :2018/04/20(金) 10:19:16
管理人様

ご返信ありがとうございます!
返信が遅くなってしまい申し訳ございません.

テスト用ホストの存在を知らなかったので,
その情報だけでも有難いです!

テスト用ホストをDLしてみたところ,
サンプルコードのvst3プラグインは認識されました.
しかし,本当に信号処理が出来ているかわからなかったので,
出力を1/4にするようにコードを書き換えて実行したのですが,
バイパス機能が実装されていないため,オンオフで確認することが出来ませんでした.
ということで,バイパス機能を実装してみたいと思います.
パラメータ実装の方も試してみます.

また,他のDAWでも試してみたほうがいいというアドバイスを受け,
知り合いのCubase使いのDTMerにプラグインを渡して試してもらったのですが,
そもそも認識されなかったそうです.おそらくCubase pro9.5です.
REAPERもDLしてみましたが,vst3を読み込んだら落ちました.

とりあえず解決できそうな糸口は色々と見つかったので試してみます.
ご回答ありがとうございました.何か進展があった際はご報告いたします.

182 管理人★ :2018/04/21(土) 19:33:24
あるとさん

ご連絡ありがとうございます。

他のDAWでも駄目でしたか…
ちなみに私のサイトに記載されている環境構築方法の場合
Windows 7だとたしか動きません…。
機能追加した結果等を教えていただけると幸いです。

183 猫十 :2018/06/29(金) 03:46:30
WavetableSynthとAsyncFileLoadのサンプルありがとうございます。
すっかりご無沙汰してしまい、大変恐縮しております。

3.6.10への移行はちょっと保留にしまして(汗)、遅ればせながらGUI有りの拙作にWavetableSynthとAsyncFileLoadを混ぜる形で組み込んでみています。
WavetableSynthとAsyncFileLoadの信号処理部が相互に干渉しない形でなら混ぜ合わせはできたのですが、
wavbufをsetWaveして打鍵に沿わせようとすると途端にうまく行きません。

現状は添付のtest.wavをwavbufに読み込ませた後にfloatに変換したものと取得したサイズ(51528)をsetWaveに渡しています。
もしよろしければこのあたりの手順についてご教示いただければ幸いです。
何卒宜しくお願いいたします。

184 管理人★ :2018/06/29(金) 21:15:13
猫十さん

書き込みありがとうございます。

昔のコードなので読み返しながらになりますが、作られたプログラムの処理の流れや
setWave()等に渡している引数等を教えていただけると助かります。

また、「うまく行かない」というのはどういう状況なのかも教えてください。

185 猫十 :2018/06/30(土) 00:22:25
管理人様

お世話になります。猫十です。
ご返信ありがとうございます。

現在の手順なのですが、

コンストラクタで管理人様のtest.wavを読み込んだ後、

>wavbuf = new short[wavsize];
のあとに
f_wavbuf = new float[wavsize];
としまして、

>memcpy_s(wavbuf, wavsize * sizeof(short), buf + sizeof(MyWAVEHEADER), wh.datasize);
のあとに


for (int i = 0; i < wavsize; i++)
{
f_wavbuf[i] = (float)(wavbuf[i]) / 32768.0f;
}
こんな感じでfloatに変換し(汗)
これでkeytableに紐づけできる状況が整ったと(私が勝手に)判断しまして、

for (int i = 0; i < 128; i++)
{
keytable[i] = f_wavbuf;
}
としてます。

setWaveは

onMidiKeyOnで

cvoice[i].setWave(keytable[noteNo], wavsize, 44100.0f / wavsize);

のようにしてます。

この状況で鍵盤を弾くと打鍵に合わせて音階付きでノイズが鳴ります。

以上が現在の流れです。

お手数をおかけいたしますが何卒よろしくお願いいたします。

186 管理人★ :2018/06/30(土) 10:38:51
猫十さん

2.4の環境がもうないので試せていませんが
onMidiKeyOn()でsetWave()の第3引数を、440.0fぐらいで
試していただけますでしょうか?

 cvoice[i].setWave(keytable[noteNo], wavsize, 440.0f);

187 猫十 :2018/06/30(土) 13:21:52
管理人様

ご返信ありがとうございます。

さっそく試してみました。

...ドラム鳴ってます(驚)
打鍵(NoteNo=60)で再生速度こそオリジナルと違いますがドラムが聴こえてきます。
NoteNoに追従して再生速度も変わっています。

ありがとうございます!

ちなみにこれはどういうことなんでしょうか?
もしよろしければぜひご教示いただきたく存じます。

188 管理人★ :2018/06/30(土) 13:51:02
猫十さん

無事解決してよかったです。

setWave()の第3引数は単純な「サンプルレート÷サイズ」ではなく「波形データ自体の音程(周波数)」になります。

今回のように「44,100÷51,528」とすると波形データの周波数は「約0.85Hz」となるので
例えばA4の音(440Hz)で鳴らそうとすると、517倍(440Hz÷0.85Hz)のスピードで波形データを
再生することになります。

MyWavTableSynthサンプルプログラムは「波形データサイズ=1周期分」なので
単純に「44,100÷8」で周波数(=5,512Hz)を計算していますが、wavファイル等から波形データを読み込んだ場合、
「波形データサイズ=1周期分」とならないので、別途指定してあげる必要があります。

 // 第三引数の周波数を設定するにはwavファイルが何Hzかあらかじめ調べておく必要がある。
 (onMidiKeyOn()のこの部分のコメントです。改めてみると分かりにくいコメントで恐縮です。)

今回はリズム音だったのでピッチ自体が不明なため、とりあえずA4キーを押したときに
そのまま再生されるよう440Hzとしました。
ピアノ等の音程のあるwavサンプルの場合はその音程に合わせた周波数を設定すれば大丈夫だと思います。

189 猫十 :2018/06/30(土) 22:28:26
管理人様

お世話になっております。

こちらこそサンプリング周波数とデータサイズの関係に考えがいたらず、お手数をおかけいたしました。

まだまだ課題は山積みですが引き続き頑張ります。
またお尋ねさせていただくこともあるかと思いますが何卒よろしくお願いいたします。

190 猫十 :2018/07/15(日) 22:14:17
管理人様

こんばんは。
またまた書き込み失礼します。

2.4でWAVファイルを読み込むサンプラーもどきを作っています。
VSTクラス(AudioEffectX)で読み込んだWAVファイルの情報を文字列としてGUIクラス(AEffGUIEditor)に送りたいのですが方法がわかりません。

VSTクラス(AudioEffectX)とGUIクラス(AEffGUIEditor)間で相互に文字列を受け渡すにはどうすればよいのでしょうか?

いつも質問ばかりですいません。
何卒よろしくお願いいたします。

猫十

191 管理人★ :2018/07/16(月) 13:53:10
猫十さん

2.4ではやったことがなく、すでに開発環境もないので試せませんが、
下記の方法で出来るのではないでしょうか?
(コードのイメージは全角スペースになっているのでご注意ください。)

①音声処理クラス側(AudioEffectXを継承したクラス側)で文字列を受け取る関数を定義する

class MyVST : public AudioEffectX
{
public:
  // 引数の文字列 fileをメンバー変数のfilepathに設定する関数
  void setFilePath(char* file) { strcpy(file, filepath); };

  〜〜以下略〜〜
protected:
  char filepath[256]; // ファイルパス用の文字列
};

②GUIクラス(AEffGUIEditorを継承したクラス側。MyGUIとする)で
 CTextEditコントロールなどから文字列をうけとった際に通知する処理を追加する

void MyGUI::valueChanged (CDrawContext *pContext, CControl *pControl)
{
  // どのパラメーターが操作されたかを取得する。
  VstInt32 index = pControl->getTag();

  // 操作されたパラメーターを確認。
  // ファイル名入力用のパラメーターなら音声処理クラスに文字列を渡す
  if ( index == MYVST_FILENAME )
  {
    char filepath[256];

    // テキストエディットから文字列を取得
    // (SDK 2.4のテキストエディットの文字列の長さは256固定なので注意)
    ((CTextEdit*)pControl)->getText(filepath);

    // 音声処理クラスに文字列を渡す
    ((MyVST*)effect)->setFilePath(filepath);
  }
  〜〜以下略〜〜
}

以上です。CTextEditの作成方法は下記とほぼ同じだったと思います。
http://vstcpp.wpblog.jp/?p=1722

なお、音声処理クラスのfilepathに書き込む際は排他処理等がいるかもしれません。

192 管理人★ :2018/07/16(月) 15:45:38
猫十さん

すいません。今質問を読み返して逆方向だということに気付きました

やりかたは>>191と同様で出来ると思います。
(コードのイメージは全角スペースになっているのでご注意ください。)

①GUIクラス側(AEffGUIEditorを継承したクラス側)で文字列を受け取る関数を定義する

class MyGUI : public AEffGUIEditor, CControlListener
{
public:
  // 引数の文字列 fileをメンバー変数のfilepathに設定する関数
  void setFilePath(char* file) { strcpy(file, filepath); };

  〜〜以下略〜〜
protected:
  char filepath[256]; // ファイルパス用の文字列
};

②音声処理クラス側(AudioEffectXを継承したクラス側 MyVSTとする)で
 CTextEditコントロールなどから文字列をうけとった際に通知する処理を追加する

void MyVST::processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames)
{
  〜〜以下略〜〜

  // wavファイルのパスが変更された場合だけ、MyGUI側に新しいファイルパスを渡す
  // (条件式は省略。プログラムに合わせてください。)
  if(〜〜〜〜)
  {
    // たぶん排他処理が必要
    ((MyGUI*)editor)->setFilePath(newfilepath);
  }
}

③GUIクラス側(AEffGUIEditorを継承したクラス側)でテキストラベル等に文字列を反映させる
 (ここではidle関数を利用。参考→https://www39.atwiki.jp/vst_prog/pages/93.html)

void VstGui5::idle ()
{
  // まずは継承元の関数を呼び出す(必須)
  AEffGUIEditor::idle ();

  // このVSTGUI固有の処理を記載する

  // テキストラベルの文字を設定する
  // setText()処理中にfilepathが更新ないように排他処理がいるかもしれません。
  textLabel->setText (filepath);

  // テキストラベルの描画を更新する
  textLabel->setDirty();
}

193 管理人★ :2018/07/16(月) 15:49:25
>>192
誤記がありました…。

【誤】
②音声処理クラス側(AudioEffectXを継承したクラス側 MyVSTとする)で
 CTextEditコントロールなどから文字列をうけとった際に通知する処理を追加する

【正】
②音声処理クラス側(AudioEffectXを継承したクラス側 MyVSTとする)で
 ファイルパスを渡す処理を追加する。

194 猫十 :2018/07/18(水) 01:46:05
管理人様

頂きました回答をもとにさっそく試してみたところ、
AudioEffectX側からAEffGUIEditor側へ文字列を送ることができました。(その逆もできました。)

具体的には
AEffGUIEditor側でFileSelector経由で読み込んだファイルのフルパスをAudioEffectX側に送り、
AudioEffectX側でWAVを読み込んで発音部にセット、
読み込んだWAVのサンプリング周波数やビットレートその他の情報(デバッグ用にチャンク位置とか)をAEffGUIEditor側に送って表示
です。(VSTiなサンプラーとしてこのやり方が合っているのかどうかわかりませんが・・・。)

排他処理は入れていないのですが基本的な動作はOKっぽくなってきましたので
実際の曲で市販のプラグインと混ぜて使ってみようと思ってます。

いつも本当にありがとうございます。

また何かありましたら何卒よろしくお願いいたします。


猫十

195 管理人★ :2018/07/19(木) 22:37:42
猫十さん

動いてよかったです。

VSTのサンプラーとしてやり方があってるかどうかはわかりませんが、
私はプログラムに正解はないと思っています。
想定通り動いたのであればそのやり方でいいと思います。


新着レスの表示


名前: E-mail(省略可)

※書き込む際の注意事項はこちら



掲示板管理者へ連絡 無料レンタル掲示板 powered by Seesaa