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

波形描画ビューのその後

1sagamat:2023/08/15(火) 21:00:03
うつぼかずら様

こんばんは。お世話になっております。
波形描画ビューのページにコメントを書き込もうとしましたがうまく反映されないようですので、こちらに失礼いたします。
波形描画ビューの記事、大変参考になりました。ありがとうございます。
記事の最後に触れられている「音声処理クラスから音声波形を取得する方法」が気になります。
音声処理クラスからパラメータ操作クラスへの配列の受け渡し、ならびに、その逆ができるようになれば、いろいろとできることが増えるのではと想像しております。
是非とも続きの記事を拝見したく、今か今かと更新を期待しております。
お忙しいところ恐縮ですが、上記方法のご教示をいただければ幸いです。
今後とも何卒よろしくお願い申し上げます。

2管理人★:2023/08/17(木) 00:59:38
sagamatさん

書き込みありがとうございます。
頑張って記事を書いてみたいと思いますが、少しお時間をください。

3管理人★:2023/08/17(木) 01:01:25
とりいそぎデータ(配列)を受け渡しする方法について簡単なサンプルコードを記載します。

パラメーター操作クラスから音声処理クラスへのデータ受け渡しの方法になりますが、
逆のパターンでも同じ処理でできると思います。

// パラメーター操作クラスから音声処理クラスにメッセージを送信する関数
tresult MyVSTController::sendData(void* data, size_t _size)
{
// パラメーター操作クラスから音声処理クラスにメッセージを送るためメッセージクラスを確保する
IMessage* msg = allocateMessage();

// メッセージクラスのメッセージIDを設定する。メッセージID必ず設定する必要がある。
msg->setMessageID(u8"sendData01");

// メッセージ内にデータを設定する。データは「データID(文字列)+データ中身(ポインタ)」という形をとる
msg->getAttributes()->setBinary(u8"size", (void*)(&_size), sizeof(size_t));
msg->getAttributes()->setBinary(u8"data", (void*)data, (uint32)_size);

// メッセージクラスを音声処理クラスに送信する
sendMessage(msg);

// 確保したメッセージクラスは解放する
msg->release();

return kResultTrue;
}

4管理人★:2023/08/17(木) 01:02:34
// 音声処理クラスでメッセージを受け取った時の関数
result MyVSTProcessor::notify(IMessage* message)
{
// メッセージIDがパラメーター操作クラスから送られたものかどうかチェックする。
if (strcmp(message->getMessageID(), u8"sendData") == 0)
{
// メッセージクラスのデータ読込用の一時変数
const void* tmp;
uint32 datasize;

void* _data;
size_t _size;

// メッセージ内のデータを受け取る
// まずはデータID「size」のデータを受け取る
message->getAttributes()->getBinary("size", tmp, datasize);
memcpy(&_size, tmp, sizeof(size_t));

// 同様にデータID「data」のデータを受け取る
message->getAttributes()->getBinary("data", tmp, datasize);
_data = new char[datasize];
memcpy(_data, tmp, datasize);

// -------------------------------
// ここで何かする。
// -------------------------------

return kResultOk;
}

// メッセージIDがパラメーター操作クラスから送られたものでなければ、継承元クラスのnotifyを呼び出して終了。
return AudioEffect::notify(message);
}

5管理人★:2023/08/17(木) 01:07:58
インデントが消えてかなり見辛くなってしまいましたがご確認いただければと思います。

なお、音声処理クラスでデータの受け渡しをする場合は下記に注意してください。
・マルチスレッド処理のためprocess関数やnotify関数などは別スレッドになっている
・リアルタイムスレッド(process関数など)からデータ送信関数を呼び出さない

6sagamat:2023/08/17(木) 11:54:37
うつぼかずら様

お忙しい中での早速のご教示、感謝申し上げます!!
いくつかわからないところがありそうですが、まずは試してみます!
とりいそぎ、御礼のみにて失礼いたします。
今後とも何卒よろしくお願い申し上げます。

7sagamat:2023/09/10(日) 15:03:42
うつぼかずら様

こんにちは。お世話になっております。
パラメータ処理クラスから音声処理クラスへのデータの受け渡しも逆のパターンで可能とのご教示をいただいておきながら、うまく実装できておりません。大変お恥ずかしいのですが、追加で質問させていただきたく存じます。
「図形・波形描画ビューの実装」を行った上で下記のコードを書きました。やりたいことは音声データの波形をリアルタイムでGUIに表示することです。
「process関数からデータ送信関数を呼び出さない」とヒントをいただいておりますが、書きようがわからず今のところ音声処理クラスのprocess関数内でsendadta関数を呼び出してしまっております。
下記にprocessor.cppに記載しているsendadata関数、guieditor.cppに記載しているnotify関数、processor.cppに記載しているprocess関数を投稿させていただきます。
当方、Fortran, C#, MATLAB, Pythonを少し触ったことがある程度です。下記、いろいろトンチンカンなことをしているのだろうと想像いたします。
お時間の許す際にご指南を頂戴できますと大変助かります。何卒よろしくお願い申し上げます。

8sagamat:2023/09/10(日) 15:05:44
【processor.cpp】

tresult MyVSTProcessor::senddata(void* data, size_t _size)
{
// 音声処理クラスからパラメータ操作クラスにメッセージを送るためメッセージクラスを確保する
IMessage* msg = allocateMessage();

// メッセージクラスのメッセージIDを設定する。メッセージID必ず設定する必要がある。
msg->setMessageID("sendData");

// メッセージ内にデータを設定する。データは「データID(文字列)+データ中身(ポインタ)」という形をとる
msg->getAttributes()->setBinary("size", (void*)(&_size), sizeof(size_t));
msg->getAttributes()->setBinary("data", (void*)data, (uint32)_size);

// メッセージクラスを音声処理クラスに送信する
sendMessage(msg);

// 確保したメッセージクラスは解放する
msg->release();

return kResultTrue;
}

9sagamat:2023/09/10(日) 15:07:59
【guieditor.cpp】

tresult MyVSTGUIEditor::notify(IMessage* message)
{
// メッセージIDがパラメーター操作クラスから送られたものかどうかチェックする。
if (strcmp(message->getMessageID(), "sendData") == 0)
{
// メッセージクラスのデータ読込用の一時変数
const void* tmp;
uint32 datasize;

//void* _data;
float* _data;
size_t _size;

// メッセージ内のデータを受け取る
// まずはデータID「size」のデータを受け取る
message->getAttributes()->getBinary("size", tmp, datasize);
memcpy(&_size, tmp, sizeof(size_t));

// 同様にデータID「data」のデータを受け取る
message->getAttributes()->getBinary("data", tmp, datasize);
_data = new float[datasize];
memcpy(_data, tmp, datasize);

// 波形情報を設定
waveView->setWave(_data, _size);

// フレームに追加する
frame->addView(waveView);

return kResultOk;
}

// メッセージIDがパラメーター操作クラスから送られたものでなければ、継承元クラスのnotifyを呼び出して終了。
return kResultOk;
}

10sagamat:2023/09/10(日) 15:09:05
【processor.cpp】

tresult PLUGIN_API MyVSTProcessor::process(ProcessData& data)
{
// 入力・出力バッファのポインタをわかりやすい変数に格納
// inputs[]、outputs[]はAudioBusの数だけある(addAudioInput()、addAudioOutput()で追加した分だけ)
// 今回はAudioBusは1つだけなので 0 のみとなる
// channelBuffers32は32bit浮動小数点型のバッファで音声信号のチャンネル数分ある
// モノラル(kMono)なら 0 のみで、ステレオ(kStereo)なら 0(Left) と 1(Right) となる
Sample32* inL = data.inputs[0].channelBuffers32[0];
Sample32* inR = data.inputs[0].channelBuffers32[1];
Sample32* outL = data.outputs[0].channelBuffers32[0];
Sample32* outR = data.outputs[0].channelBuffers32[1];

// numSamplesで示されるサンプル分、音声を処理する
for (int32 i = 0; i < data.numSamples; i++)
{
outL[i] = inL[i];
outR[i] = inR[i];
}

senddata((void*)outL, (size_t)data.numSamples);

// 問題なければkResultTrueを返す(おそらく必ずkResultTrueを返す)
return kResultTrue;
}

11管理人★:2023/09/10(日) 23:47:11
パラメーター操作クラスのnotify()で受け取った後、GUIクラスで描画を更新する必要があります。
(残念ながらいきなりGUIクラスのnotify()で受け取ることはできません。)

別スレッドでのsendMessage関数の実行も含めてメモを下記に保存しましたので、
良ければご確認ください。
https://www.utsbox.com/wp-content/uploads/2023/09/memo20230910.zip

なお、上記のメモはミューテックスを使用しロックをかけていますが、本来はlock-free出なければならないようです。
lock-freeなどについては下記に詳しく書かれていますので、参考にしていただければと思います。

https://qiita.com/aike@github/items/0ebfc60faee3ced98f96

12sagamat:2023/09/11(月) 09:19:52
うつぼかずら様

早速のご指南、ありがとうございます!!
実装を試みておりますが、いくつかコンパイルエラーが出ておりまして、追加で質問させていただきたく存じます。

1. wavlenの値はどこに書けばよいでしょうか?
今のところ、processor.hに
public:
int wavlen = 256;
と書いてみておりますが、guieditor.cppのnotify関数内の
int size = wavlen;
で、識別子"wavlen"が定義されていませんというエラーが生じております。

2. minの引数?
controller.cpp内の
datasize = std::min(datasize, (uint32)(sizeof(float) * wavlen));
で、オーバーロードされた関数"std::min"のインスタンスが引数リストと一致しませんというエラーが生じております。

3. MyVSTControllerの参照ができていない?
guieditor.cppのnotify関数内で、
float* tmp = ((MyVSTController*)controller)->wavdata;
がありますが、識別子"MyVSTController"が定義されていませんというエラーが生じております。

4. copiedという値は何に使っているのでしょうか?
processor.cpp内のprocess関数内に記載する
copied += size;
ですが、このcopiedという変数はどこで定義して、何に使えばよろしいでしょうか?

ご面倒をおかけいたしまして大変恐縮です。
お時間の許す時に助けていただけますと幸いです。
何卒よろしくお願い申し上げます。

13管理人★:2023/09/11(月) 21:56:45
sagamatさん

あくまでサンプルですので、「どのような処理をしているか?」の理解をしていただくためのものになります。
いくつかのコードから継ぎ接ぎで持ってきているので、そのままではコンパイルが通らない可能性があるのはご了承ください。

1.
それぞれで定義したり、共通のヘッダファイルなどを用意すればよいと思います。

2.
配列サイズを超えないようにしているだけです。
datasizeとsizeof(float)*wavelenのうち、小さい方の値をdatasizeに入れているだけなので
std::min関数でなくても大丈夫です。

3.
guieditor.cppからパラメーター操作クラスの定義が見えるようにするだけです。
パラメーター操作クラスのヘッダファイルをguieditor.cppでもインクルードするなどして
対応できます。

4.
誤記になります。
サンプルを作るときに消し忘れましたので無視してください。

14sagamat:2023/09/12(火) 08:16:37
うつぼかずら様

ありがとうございます!!!4点、解決いたしました。

ただ、新たにもうひとつエラーが生じておりまして、guieditor.cppのnotiry関数内にある
float* tmp = ((MyVSTController*)controller)->wavdata;
で、「'型キャスト': 'Steinberg::lPtr<Sterinberg::Vst::EditController>'から'Steinberg::Vst::MyVSTController*'に変換できません。」と表示されます。

この文でやろうとしていることは、音声処理クラスから送られた波形データが格納されているパラメータ操作クラスのメンバ変数wavdataにアクセスし、tmpという名前で参照できるようにすることだと思います。
当方がわかっていないことは、controller.hでパラメータ操作クラスにpublicでwavdataを定義しているのに、なぜguieditor.cppの変数"controller"からwavdataが参照できないのか、なぜ"controller"をMyVSTControllerにキャストしなければならないのか、なぜそのキャストがうまくいかないのか、というあたりです。
「クラスの継承」とかが関連した内容でしょうか。。。そのあたり、あまり理解できていない自覚はあります。

大変大変ご面倒をおかけいたしまして申し訳ございません。あと一歩、お力添えをいただけますと幸いです。何卒よろしくお願い申し上げます。

15管理人★:2023/09/12(火) 23:09:05
sagamatさん

GUIクラスの継承元で定義されている変数controllerは「EditControllerクラスのポインタ」として定義されています。
(厳密にはEditControllerクラスのポインタをVST SDKで独自拡張したIPtr<EditController>になります。
どこかのVST SDKバージョンで定義変更されたのだと思います。)

EditControllerクラスには当然 メンバ変数 wavdataはありませんので、
「controller->wavedata」のようにアクセスしようとしてもコンパイルエラーとなります。

なので、「EditControllerクラスのポインタ」から継承後の「MyVSTControllerクラスのポインタ」にキャストする必要があります。

サンプルのVST SDKバージョンが古いようで、「EditControllerクラスのポインタ」がVST SDK独自拡張のIPtr<EditController>となっており、
キャストができず「'型キャスト':〜〜〜に変換できません。」というコンパイルエラーが出たのだと思います。

試してはいませんが、下記のようにMyVSTController*ではなく、IPtr<MyVSTController>にキャストすれば
動くのではないでしょうか。

 float* tmp = ((IPtr<MyVSTController>)controller)->wavdata;

16sagamat:2023/09/12(火) 23:53:48
うつぼかずら様

連日のご対応、心より深謝申し上げます。ありがとうございます。

ご提案いただいた
float* tmp = ((IPtr<MyVSTController>)controller)->wavdata;
を試してみたところ、smartpointer.hにおいて、
「C2440 '初期化中': 'I*'から'I*'に変換できません。」
「C2439 'Steinberg::IPtr<Steinberg::Vst::MyVSTController>::ptr': 指定されたメンバーは初期化できません。」
という二つのエラーが発生しました。
smartpointer.hはなんとなくいじっちゃいけないファイルのような気がしており、どう対処してよいかわからずにいます。

あまりにお手数をおかけしておりますので、これ以上のご対応をお願いするのに気が引けております。どうかご無理の無い程度でお付き合いいただけますと幸いです。

17sagamat:2023/09/13(水) 21:48:58
うつぼかずら様

少しクラスの継承についてほんのちょっとだけ勉強してみました。気になったことがあったので追記させていただきます。

controllerからMyVSTControllerにキャストを試みているわけですが、controllerのクラスがIPtr<EditController>(基底クラス)、キャスト先がIPtr<MyVSTController>(派生クラス)ということで、これはネットで見る限り「ダウンキャスト」と呼ばれるものかと理解しました。基本的にはやっちゃダメと書いてある記事が多く、確かに素人の当方から見てもアクセス違反などが起こりやすいのかなぁという印象です。
パラメータ操作クラスMyVSTControllerはEditControllerを継承して定義していると思いますが、インスタンスcontrollerは基底クラスEditControllerで生成されているのだろうと思います。このcontrollerをそもそもMyVSTControllerクラスで生成できていればキャストの必要はなく、wavdataにアクセスできるのではないかと思います。

インスタンスcontrollerがEditControllerで生成されることはVSTの仕様でしょうか?もしそうだとすると、今回の例に限らずGUIクラスからパラメータ操作クラスの追加メンバ関数や変数にアクセスする際に、いつもダウンキャストが必要になるように思います。なんだかまずい気がします。

付け焼刃の情報ですので、間違った認識があろうかと思います。ご容赦いただけますと幸いです。

18管理人★:2023/09/13(水) 23:32:35
sagamatさん

少しIPtrの確認をしてみましたが下記でキャストできるのではないかと思います。
(試したわけではありませんのでご了承ください。)

 // IPtrからEditControllerのポインタを取得する
 EditController* tmpctrl = controller.get();

 // キャストしてwavdataを取得する。(dynamic_castを使ってキャストしてもよいかも?)
 float* tmp = ((MyVSTController*)tmpctrl)->wavdata;

私もVST開発でC++を使っていますがそこまでに詳しいわけではないので、ダウンキャストの良し悪しはわかりません。
processorクラスからGUIクラスに情報を渡す方法の一つの例を提示しているだけです。

なお、controllerがEditControllerで定義されているのはVST SDKの仕様です。

19sagamat:2023/09/14(木) 19:23:36
うつぼかずら様

すごいっ!動きましたっ!!ありがとうございます!!!連日のご対応に深く感謝申し上げます。
最終的にVSTを使ってやりたいことの土台ができあがりましたので、これからご教授いただいた内容を組み合わせてカスタマイズしていきたいと思います。
取り急ぎ、御礼申し上げます。

20sagamat:2023/12/26(火) 15:46:01
うつぼかずら様

先般は多数のご教示をいただきまして、まことにありがとうございました。やりたかったことはほぼ出来上がったのですが、まだ少しやり残していることがございまして、追加で質問させていただきたく存じます。

やり残していることは、「GUIクラスでwavファイルから配列を読み込み、それを音声処理クラスに渡すこと」です。現状、パラメータ操作クラスに「パラメータ操作クラスから音声処理クラスに配列を渡す」ためのsendData関数、音声処理クラスに「パラメータ操作クラスから送られた配列を受け取る」ためのnotify関数を記述しています。また、GUIクラスにはキックボタンを定義し、キックボタンを押せばwavファイルを読み込めるようにしています。
これらはご教示いただいた方法でおそらく問題なく実装できていると思うのですが、「GUIクラスで読み込んだ配列をパラメータ操作クラスに渡す、その後、パラメータ操作クラスのsendData関数を実行する」という処理をどこにどう書けばよいかがわかっておりません。

年末のあわただしい時期に大変恐縮ですが、お時間の許す時がございましたらご指南をいただけますと幸いです。

21管理人★:2023/12/26(火) 16:14:39
sagamatさん

多分 >>18 の方法の応用でできると思います。
まず、自作のパラメーター操作クラスにwav配列送信関数を用意してその中でsendData関数を呼び出せばよいと思います。

void MyVSTController::sendWavData(/*引数は適当に*/)
{
 引数の波形データ(配列)をsendData関数に渡す。
}

そして、GUIクラスで、>>18 の方法と同じようにキャストして呼び出します。

 // IPtrからEditControllerのポインタを取得する
 EditController* tmpctrl = controller.get();

 // キャストしてwavdataを取得する。(dynamic_castを使ってキャストしてもよいかも?)
 ((MyVSTController*)tmpctrl)->sendWavData(/*wavの配列などを指定*/);

22sagamat:2023/12/27(水) 14:54:49
うつぼかずら様

いつも的確なご指南をいただき、心より感謝申し上げます。無事にGUIクラスから音声処理クラスに配列を送ることができました!

一難去ってまた一難なのですが、音声処理クラスに送った配列をVST終了時に保存、開始時に読込したいと思っております。「パラメータの保存方法」の記事を拝見し、真似てみておりますが、うまくいきません(クラッシュします)。
保存・読込したいのは音声処理クラスの配列サイズ(int N)、配列(float* data)、GUIクラスのテキスト(UTF8StringPtr str)の三つです。とりあえずGUIクラスのものは置いておいて、「パラメータの保存方法」を参考にし、
state->write(&N, sizeof(int));
state->write(&data, sizeof(float)*N);
など(readも同様)と記載してみましたが、上述の通り、VST開始時にホストごと落ちます。writeの記載の部分がクラッシュの原因なのかはまだはっきりしておりませんが、何か間違いや注意点などございましたらご教示いただけますと幸いです。

23管理人★:2023/12/27(水) 16:28:24
sagamatさん

解決してよかったです。
サンプルのコードがそのままだとすると、
「state->write(&data, sizeof(float)* N)」の第1引数(&data)がfloat**になっており、
想定外のメモリを参照しクラッシュしている気がします。
「state->write(data, sizeof(float)* N)」でよいかと思います。

24sagamat:2023/12/27(水) 20:22:49
うつぼかずら様

初歩的なミスで大変恐縮です。ご指摘の修正で保存・読込ができるようになりました。ありがとうございます!

あとはGUIクラスなんですが、テキストの保存に関して、サイズの指定の仕方がわかっておりません。strのサイズ数はどのようにして取得できますでしょうか?また、GUIクラスの配列についても音声処理クラスと同様に保存・読込しようとしてみましたが、うまくいかないようです。GUIクラスでの保存・読込について何か注意すべき点などございますでしょうか?

25管理人★:2023/12/27(水) 21:47:37
sagamatさん

GUIクラスの状態の保存は、パラメータ操作クラスのgetState()で保存するしかありません。
ですので、文字列や波形データの配列などはGUIクラスで保持するのではなく、パラメータ操作クラスに保持するようにします。

GUIクラスのデータをパラメーター操作クラスに設定・保持する方法は >>18 や >> 21 の方法の応用でほとんど対応できると思います。

そのうえで、”パラメーター操作クラス”のgetState()で保存・setState()で読込します。
(音声処理クラスではなくパラメーター操作クラスにもsetState()・getState()があります。)

【参考】
https://steinbergmedia.github.io/vst3_dev_portal/pages/FAQ/Persistence.html
https://www.utsbox.com/?p=1060

なお、文字列ですが、UTF8StringPtr型であれば、const char*の再定義なので、strlen()あたりで文字列の長さを取得できると思います。

26sagamat:2023/12/29(金) 22:03:03
うつぼかずら様

ありがとうございます!おかげ様でGUIクラスのデータ(の一部)は保存・読込できるようになりました。ただ、文字列の保存・読込に苦戦しております。

controller.hで
std::string dataText = "test";
int dataTextLen = dataText.length();
と宣言し、パラメータ操作クラスのgetstate()で
state->write(&dataTextLen, sizeof(int));
state->write(&dataText, dataTextLen);
と記載、setstate()で、
res = state->read(&dataTextLen, sizeof(int));
res = state->read(&dataText, dataTextLen);
と記載するとクラッシュします(正確には、state->read(&dataText, dataTextLen)でクラッシュします)。

std::stringで保存しようとしていることがそもそも間違いなのか、参照の仕方に間違いがあるのか、いろいろ試しておりますが、どうもうまくいきません。
もし、お気づきの点があればご指摘頂けますと幸いです。

27管理人★:2023/12/30(土) 13:21:21
sagamatさん

書き込みがstringクラスのポインタなので、最初に文字列のバッファがあるとは限らないと思います。
シンプルに文字列そのものを保存した方がわかりやすいかもしれません。

state->write(dataText.c_str(), dataTextLen);

読み込みも、上記で文字列を保存したのであれば、一度文字列で読み込んでstring型に設定する形でよいかと思います。

char *tmpText = new char[dataTextLen];
res = state->read(tmpText, dataTextlen);
dataText = tmpText;
delete[] tmpText;

(stringクラスの文字列バッファのポインタを直接取得できるのであれば、上記のtmpTextは使う必要がありません。)

28sagamat:2023/12/30(土) 14:10:35
うつぼかずら様

年末の慌ただしい時期にも関わらず迅速にご対応いただき、ありがとうございます。
的確なご指導のおかげで、無事に目的のプラグインを作成することができました!
大変勉強になりました。重ねて感謝申し上げます。

もしお許しいただけるのであれば、少しでも何かお礼をしたく、メールででもご連絡いただけますと幸いです。

29管理人★:2023/12/30(土) 15:08:46
sagamatさん

無事解決してよかったです。
調べればいずれわかることだと思うので、特にお礼などは不要です。

30sagamat:2023/12/30(土) 19:54:50
うつぼかずら様

調べに調べればいずれわかることかもなのかもしれませんが、ご教示いただいたことで多大な時間と労力を削減できました。ありがとうございます。
ちなみにこのスレッドで質問させていただいて作成したのはチューナーとコンボルバーです。
特にコンボルバーですでに出回っているものはいろんな機能が追加されており、根本の部分がブラックボックス化しているものが多く、ネイティブに作成できたことで安心して使用することができます。
もしかすると今後もまたご質問させていただくことがあるかもしれませんが、できる限り自分で解決できるよう引き続きC++、ならびに、VSTのことを勉強するようにいたします。
取り急ぎ、深く御礼を申し上げます。良いお年をお迎えください。


新着レスの表示


名前: E-mail(省略可)

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

※画像アップローダーはこちら

(画像を表示できるのは「画像リンクのサムネイル表示」がオンの掲示板に限ります)

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