図形・波形描画ビューの実装
VST GUIにおいて音声波形などを描画するビューための方法を説明します。
波形描画とも記載しましたが、基本的には図形を描くための方法となります。
なお、VST GUIの基本的な作成方法として下記をご理解いただいている前提で進めさせていただきます。
(下記のVST GUIクラスをベースに説明を進めさせていただきます。)
また、波形描画ビューの定期更新については、「VST GUIにおけるコントロールの定期更新」も参考にしてください。
CWaveDrawViewクラスの作成
まず、波形描画するためのクラスをCViewクラスを継承して作成します。
【guieditor.h】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class CWaveDrawView : public CView { protected: // 描画する直線のリスト(配列) CDrawContext::LineList lines; public: CWaveDrawView (const CRect& size); // ビューの描画を行う際に呼び出される関数 // CViewクラスから継承する。 virtual void draw(CDrawContext* pContext) override; // 波形情報を設定するためのCWaveDrawViewクラス独自の関数 // 第1引数が波形のバッファで、第2引数がバッファのサイズとする。 void setWave(float buf[], int size); }; |
【guieditor.cpp】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
CWaveDrawView::CWaveDrawView (const CRect& size) : CView(size) // 継承元のコンストラクタを呼び出す。 { // ここでは特に何もすることはない。 }; void CWaveDrawView::draw(CDrawContext* pContext) { // まずは背景を描画する。 // setBackGround()関数で登録された画像ファイルがあるか確認し、描画する。 if (getDrawBackground()) { getDrawBackground()->draw(pContext, getViewSize()); } // ここから波形の描画 // まずは描画する線の色と見た目(スタイル)を設定する。 pContext->setFrameColor(kGreenCColor); // 線の色を設定 pContext->setLineStyle(kLineSolid); // 線のスタイル(終端の丸み、点線かどうか等)を設定 pContext->setLineWidth(2.0); // 線の太さを設定 // メンバー変数のlinesを使用して、複数の直線を一気に描画する。 pContext->drawLines(lines); // 描画後は必ずsetDirtyにfalseを設定して更新済みとする。 setDirty(false); } void CWaveDrawView::setWave(float buf[], int size) { lines.clear(); // まずは直線のリストをクリアしておく。 // buf[]の内容をもとに、描画する直線のリスト(配列)を作成する。 // 描画する直線には始点座標(x,y)と終点座標(x,y)が必要となる。 // なお、座標はウィンドウの左上を座標(0,0)とした絶対座標で指定する必要がある。 // まずはCWaveDrawViewクラスの座標情報(CRect型)を取得する。 CRect viewSize = getViewSize(); // CWaveDrawViewクラスの左・真ん中を最初の始点座標とする。 CCoord leftX = viewSize.left; // viewSizeから左端の座標を取得する CCoord centerY = viewSize.getCenter().y; // viewSizeから中央の座標(xとy)を取得し、そのy座標のみをcenterYとする。 CPoint from(leftX, centerY); // 直線の始点座標の変数 for (int i = 0; i < size; i++) { // 描画する直線用にCWaveDrawViewクラスの幅と高さを取得する CCoord width = viewSize.getWidth(); CCoord height = viewSize.getHeight(); // 終点座標を計算する。 // x座標は左端から始まって、最終的には右端(leftX + width)になるようにする。 // y座標はcenterYを中心に、buf[i]の振幅に合わせて上端から下端の間になるようにする。 CPoint to(leftX + width * ((double)(i+1) / (double)size) , centerY- height / 2.0 * buf[i]); // 始点座標と終点座標から直線を作成し、直線リスト(配列)に加える。 CDrawContext::LinePair line(from, to); lines.push_back(line); // 今回の終点座標を次の直線の始点座標とする。 from = to; } } |
コンストラクタ
CWaveDrawViewのコンストラクタでは特に継承元のCViewクラスの引数にビューのサイズを渡しているだけで特に何もしていません。
draw()関数
draw()関数は、継承元のCViewクラスから継承した関数で描画が必要になったタイミングで呼び出されます。
描画が必要なタイミングとは、「ウィンドウが開いた」「ウィンドウが移動した」「setDirty(true)で明示的に描画指示があった」などのタイミングになります。
draw()関数ではまず、ビューの背景画像の有無を確認して背景画像を描画しています。この背景画像の描画はほぼ定型文のような形なので説明は省略します。
【guieditor.cpp】
1 2 3 4 5 6 |
// まずは背景を描画する。 // setBackGround()関数で登録された画像ファイルがあるか確認し、描画する。 if (getDrawBackground()) { getDrawBackground()->draw(pContext, getViewSize()); } |
次に、引数のCDrawContextクラスを使用して描画をします。CDrawContextクラスは文字や線・図形を描くためのクラスになります。
setFrameColor()関数・setLineStyle()関数・setLineWidth()関数で描画する波形の直線の色や見た目(スタイル)を設定し、drawLines()関数で直線を描画しています。
【guieditor.cpp】
1 2 3 4 5 6 7 8 |
// ここから波形の描画 // まずは描画する線の色と見た目(スタイル)を設定する。 pContext->setFrameColor(kGreenCColor); // 線の色を設定 pContext->setLineStyle(kLineSolid); // 線のスタイル(終端の丸み、点線かどうか等)を設定 pContext->setLineWidth(2.0); // 線の太さを設定 // メンバー変数のlinesを使用して、複数の直線を一気に描画する。 pContext->drawLines(lines); |
CDrawContextクラスでは下記のような関数があります。詳細は割愛しますのでマニュアルなどでご確認ください。
関数 | 概要 | |
---|---|---|
図形描画 | DrawLine()関数 | 直線を描画する関数です。 |
DrawLines()関数 | 複数の直線を描画する関数です。 | |
DrawRect()関数 | 四角形を描画する関数です。 | |
DrawPolygon()関数 | 多角形(ポリゴン)を描画する関数です。 | |
DrawArc()関数 | 弧を描画する関数です。 | |
DrawEllipse()関数 | 円・楕円を描画する関数です。 | |
setFrameColor()関数 | 直線や図形の枠線の色を設定する関数です。 | |
setFillColor()関数 | 図形の塗りつぶし色を設定する関数です。 | |
文字列描画 | drawString()関数 | 文字列を描画する関数です。 |
setFontColor()関数 | 文字列の色を設定する関数です。 | |
setFont()関数 | 文字列のフォントを設定する関数です。 |
setWave()関数
setWave()関数は、CWaveDrawViewクラスの独自の関数で、音声波形データから描画に必要な直線リスト(配列)を作成する関数になります。
今回は波形を描画するクラスのためこのような関数を用意しました。
直線リスト(配列)はメンバー変数として定義したCDrawContext::LineList linesになります。
直線は始点座標(x,y)と終点座標(x,y)がペアとなったLinePair型の変数で、これを動的配列としたvector
注意点として、直線に設定する座標はウィンドウの左上を座標(0,0)とした絶対座標となる点です。(ビューの左上座標ではありません。)
【guieditor.h】
1 2 3 4 5 |
class CWaveDrawView : public CView { protected: // 描画する直線のリスト(配列) CDrawContext::LineList lines; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
void CWaveDrawView::setWave(float buf[], int size) { lines.clear(); // まずは直線のリストをクリアしておく。 // buf[]の内容をもとに、描画する直線のリスト(配列)を作成する。 // 描画する直線には始点座標(x,y)と終点座標(x,y)が必要となる。 // なお、座標はウィンドウの左上を座標(0,0)とした絶対座標で指定する必要がある。 // まずはCWaveDrawViewクラスの座標情報(CRect型)を取得する。 CRect viewSize = getViewSize(); // CWaveDrawViewクラスの左・真ん中を最初の始点座標とする。 CCoord leftX = viewSize.left; // viewSizeから左端の座標を取得する CCoord centerY = viewSize.getCenter().y; // viewSizeから中央の座標(xとy)を取得し、そのy座標のみをcenterYとする。 CPoint from(leftX, centerY); // 直線の始点座標の変数 for (int i = 0; i < size; i++) { // 描画する直線用にCWaveDrawViewクラスの幅と高さを取得する CCoord width = viewSize.getWidth(); CCoord height = viewSize.getHeight(); // 終点座標を計算する。 // x座標は左端から始まって、最終的には右端(leftX + width)になるようにする。 // y座標はcenterYを中心に、buf[i]の振幅に合わせて上端から下端の間になるようにする。 CPoint to(leftX + width * ((double)(i+1) / (double)size) , centerY- height / 2.0 * buf[i]); // 始点座標と終点座標から直線を作成し、直線リスト(配列)に加える。 CDrawContext::LinePair line(from, to); lines.push_back(line); // 今回の終点座標を次の直線の始点座標とする。 from = to; } } |
VST GUIへの組み込み
CWaveDrawViewクラスが実装できれば、実際にVST GUIに組み込みます。
VST GUIクラスのopen()関数内でCWaveDrawViewクラスを作成・初期化し、登録します。
【guieditor.h】
1 2 3 4 5 6 7 8 |
class MyVSTGUIEditor : public VSTGUIEditor, public IControlListener { protected: CWaveDrawView* waveView = nullptr; public: ~~ 中略 ~~ } |
【guieditor.cpp】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
bool PLUGIN_API MyVSTGUIEditor::open(void* parent, const PlatformType& platformType) { // GUIウィンドウが開かれたときに、UIを作成する ~~ 中略 ~~ // --------------------------------------------- // ここから各コントロールの作成 // CWaveDrawViewを作成する waveView = new CWaveDrawView(size); // CWaveDrawViewに設定する波形情報 // とりあえず適当な周波数のサイン波を設定 float wav[256]; for (int i = 0; i < 256; i++) { wav[i] = sin((2.0 * 3.14159265 * (double)(i * 880 ) / 44100.0); } // 波形情報を設定 waveView->setWave(wav, 256); // フレームに追加する frame->addView(myView); ~~ 中略 ~~ // GUIウィンドウのオープンに成功した場合はtrueを返す return true; } |
補足
ここまでで波形を描画するビューについては作成することができますが、実際のエフェクタやシンセサイザーなどでは、定期的に波形を更新する必要があるかと思います。
定期的な波形の更新は、VST GUIのnotify()関数で行うことができます。
notify()関数については、「VST GUIにおけるコントロールの定期更新」や「「ファイルを開く」ダイアログの実装」も参考にしてください。
まず、VST GUIクラスにnotfy()関数を定義します。(継承元のVSTGUIEditorクラスからのオーバーライドになります。)
【guieditor.h】
1 2 3 4 5 6 7 |
class MyVSTGUIEditor : public VSTGUIEditor, public IControlListener { public: ~~ 中略 ~~ CMessageResult notify(CBaseObject *sender, const char *message) override; } |
つづいて、notify()関数内で、メッセージがkMsgTimerであることを確認します。
そして、作成したCWaveDrawViewクラスに音声波形をsetWave()関数で設定し、setDirty()関数で描画更新指示を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
CMessageResult MyVSTGUIEditor::notify(CBaseObject *sender, const char *message) { if (message == CVSTGUITimer::kMsgTimer) { if (waveView != nullptr) { float wav[256]; // ここで波形を更新する。更新の方法はここでは割愛 waveView->setWave(wav, 256); waveView->setDirty(); } } return VSTGUIEditor::notify(sender, message); } |
上記により、波形描画ビューを定期的に更新することができます。
音声波形の更新内容(音声処理クラスから音声波形を取得する方法)についてはいずれ記載する予定ですが、ここでは割愛いたします。
おわりに
以上で波形描画ビューが実装できます。
上記以外にもVST3についての情報があります。下記をご参照ください。
また、質問やご指摘はコメント欄や掲示板、Twitterでいただけばとおもいます。
■掲示板
■Twitterアカウント:@vstcpp URL:https://twitter.com/vstcpp