「VSTe VSTGUI inline UI Editor を使わないで、Viewに出力音声波形を描画する方法」の版間の差分
編集の要約なし |
編集の要約なし |
||
729行目: | 729行目: | ||
同じような仕組みを使えば、周波数成分を描画するような処理も作れると思います。自分で描画できるようになれば、もうなんでもできるようになったと考えて問題ないでしょう。あとは、これをVSTGUI inline UI Editorを使って描画できるようになればトレンド(流行)にもついていけるようになったといえるので、そこだけ考えないと駄目だな。もうちょっと工夫が必要。基本的な音声信号処理も本を買えば、ギターの基本的なペダル(EQ,マルチバンドEQ,コンプ,FAZZ,OverDrive,Distortion,コーラス,リバーブ,ノイズゲート,フランジャー,ワウ)を網羅してくれていますので、自分で音声信号処理もできるようになると思います。 | 同じような仕組みを使えば、周波数成分を描画するような処理も作れると思います。自分で描画できるようになれば、もうなんでもできるようになったと考えて問題ないでしょう。あとは、これをVSTGUI inline UI Editorを使って描画できるようになればトレンド(流行)にもついていけるようになったといえるので、そこだけ考えないと駄目だな。もうちょっと工夫が必要。基本的な音声信号処理も本を買えば、ギターの基本的なペダル(EQ,マルチバンドEQ,コンプ,FAZZ,OverDrive,Distortion,コーラス,リバーブ,ノイズゲート,フランジャー,ワウ)を網羅してくれていますので、自分で音声信号処理もできるようになると思います。 | ||
オススメの本は以下のとおりです。 | |||
*[https://amzn.to/3pbY9xN VSTプラグイン(著:うつぼかずら、版:工学社)]Webの方が詳しい。[https://www.utsbox.com/ https://www.utsbox.com/] | |||
*[https://amzn.to/43KKmgK サウンドプログラミング入門――音響合成の基本とC言語による実装(Software Design plus)(著:青木直史、版:技術評論社)] | |||
*[https://amzn.to/442LOdT C言語ではじめる音のプログラミング―サウンドエフェクトの信号処理(著:青木直史、版:OHM社)] | |||
*[https://amzn.to/463C2KF プログラム101付き 音声信号処理(ディジタル信号処理シリーズ)(著:川村新、版:CQ出版)] | |||
*[https://amzn.to/466vVoO WAVプログラミング―C言語で学ぶ音響処理(著:北山洋幸、版:カットシステム)] | |||
2023年6月17日 (土) 12:12時点における最新版
VSTeプログラミング初歩に戻る。
概要
VSTGUI inline UI Editorの使い方って、あまり情報がないので、よくわからなかったのですが、とりあえず、使わない方法はかなり情報があったので、音声波形を描画することができました。
ViewContainerというコントロールには、CViewクラスを継承したコンテキストを追加するクラスがあるので、簡単そうに思えたのでやってみたらできちゃったので、公開しておこうと思います。各コントロールの画像は自分で用意して下さい。冷たいね。
ファイル構成は以下のとおりです。
- fuid.h
- def.h
- GlobalConstants.h
- factory.cpp
- resource.rc
- proc.h
- proc.cpp
- view.h
- view.cpp
- guieditor.h
- guieditor.cpp
- fuid.h
#pragma once
#include "pluginterfaces\base\funknown.h"
#include "def.h"
namespace Steinberg{
namespace Vst {
// ============================================================================================
// FUID
// ============================================================================================
static const FUID ProcessorUID (0x6169B858, 0x954548D0, 0xBE846909, 0x7F12CEE0);
static const FUID ControllerUID(0x994440DB, 0x57DA4F70, 0xAF20CD49, 0xBE7C4EB5);
} }
- def.h
#pragma once
#ifndef __DEF_H__
#define __DEF_H__
#include <math.h>
// ============================================================================================
// PARAM TAG
// ============================================================================================
#define PARAM_DEPTH_TAG 100
#define PARAM_SPEED_TAG 101
#define PARAM_TYPE_TAG 102
#define PARAM_VIEW_TAG 103
#endif
- GlobalConstants.h
#pragma once
#include "guieditor.h"
#ifndef __CxxGUIEdit_Instance__
#define __CxxGUIEdit_Instance__
extern Steinberg::Vst::CxxGUIEdit* CxxGUIEdit_Instance;
#endif
- factory.cpp
#include "public.sdk/source/main/pluginfactory.h"
#include "fuid.h"
#include "proc.h"
#include "view.h"
// ============================================================================================
// 固有情報1
// ============================================================================================
#define MYVST_VENDOR "CxxVST"
#define MYVST_URL "https://wiki.yo-net.jp/"
#define MYVST_EMAIL "mailto:info_@yo-net.jp"
// ============================================================================================
// 固有情報2
// ============================================================================================
#define MYVST_VSTNAME "CxxVST ViewGUI"
#define MYVST_VERSION "0"
#define MYVST_SUBCATEGORIES Vst::PlugType::kFx
//bool DeinitModule() { return true; }
// ============================================================================================
// 固有情報マクロ定義
// ============================================================================================
BEGIN_FACTORY_DEF(MYVST_VENDOR, MYVST_URL, MYVST_EMAIL)
DEF_CLASS2(INLINE_UID_FROM_FUID(Steinberg::Vst::ProcessorUID),
PClassInfo::kManyInstances,
kVstAudioEffectClass,
MYVST_VSTNAME,
Vst::kDistributable,
MYVST_SUBCATEGORIES,
MYVST_VERSION,
kVstVersionString,
Steinberg::Vst::CxxProc::createInstance )
DEF_CLASS2(INLINE_UID_FROM_FUID(Steinberg::Vst::ControllerUID),
PClassInfo::kManyInstances,
kVstComponentControllerClass,
MYVST_VSTNAME " Controller",
0,
"",
MYVST_VERSION,
kVstVersionString,
Steinberg::Vst::CxxEdit::createInstance )
END_FACTORY
- resource.rc
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
background.png PNG ".\\images\\background.png"
background2.png PNG ".\\images\\background2.png"
knob.png PNG ".\\images\\knob.png"
knob_handle.png PNG ".\\images\\knob_handle.png"
slider.png PNG ".\\images\\slider.png"
slider_handle.png PNG ".\\images\\slider_handle.png"
- proc.h
#pragma once
#ifndef __PROC_H__
#define __PROC_H__
#include "public.sdk/source/vst/vstaudioeffect.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include "vstgui/lib/cview.h"
#include "def.h"
#include "guieditor.h"
#include <functional>
namespace Steinberg{
namespace Vst {
// ============================================================================================
// Procクラス
// ============================================================================================
class CxxProc : public AudioEffect
{
protected:
ParamValue depth;
ParamValue freq;
int32 type;
ParamValue theta;
public:
CxxProc();
~CxxProc();
tresult PLUGIN_API initialize(FUnknown* context);
tresult PLUGIN_API setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts);
tresult PLUGIN_API process(ProcessData& data);
static FUnknown* createInstance(void*) { return (IAudioProcessor*)new CxxProc(); }
private:
Sample32** outL3Times;
Sample32** outR3Times;
int bufferSize;
bool initialized;
int TimesCount;
};
} }
#endif
- proc.cpp
#include "def.h"
#include "fuid.h"
#include "proc.h"
namespace Steinberg{
namespace Vst {
CxxProc::CxxProc()
{
setControllerClass(ControllerUID);
outL3Times = nullptr;
outR3Times = nullptr;
bufferSize = 0;
initialized = false;
TimesCount = 0;
}
tresult PLUGIN_API CxxProc::initialize(FUnknown* context)
{
tresult result = AudioEffect::initialize(context);
if (result == kResultTrue)
{
addAudioInput(STR16("AudioInput"), SpeakerArr::kStereo);
addAudioOutput(STR16("AudioOutput"), SpeakerArr::kStereo);
addEventInput(STR16("Event Input"), 1);
depth = 1.0f;
freq = 5.0f;
type = 0;
theta = 0.0f;
}
return result;
}
tresult PLUGIN_API CxxProc::setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts)
{
if (numIns == 1 && numOuts == 1 && inputs[0] == SpeakerArr::kStereo && outputs[0] == SpeakerArr::kStereo)
{
return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts);
}
return kResultFalse;
}
CxxProc::~CxxProc() {
//3回分のdata.NumSamplesの数だけ履歴として保存しているので
//3回分の動的配列を消去
if (outL3Times != nullptr)
{
for (int i = 0; i < 3; ++i)
{
delete[] outL3Times[i];
}
delete[] outL3Times;
}
if (outR3Times != nullptr)
{
for (int i = 0; i < 3; ++i)
{
delete[] outR3Times[i];
}
delete[] outR3Times;
}
}
tresult PLUGIN_API CxxProc::process(ProcessData& data)
{
if (data.inputParameterChanges != NULL)
{
int32 paramChangeCount = data.inputParameterChanges->getParameterCount();
for (int32 i = 0; i < paramChangeCount; i++)
{
IParamValueQueue* queue = data.inputParameterChanges->getParameterData(i);
if (queue != NULL)
{
int32 tag = queue->getParameterId();
int32 valueChangeCount = queue->getPointCount();
ParamValue value;
int32 sampleOffset;
if (queue->getPoint(valueChangeCount - 1, sampleOffset, value) == kResultTrue)
{
switch (tag)
{
case PARAM_DEPTH_TAG:
depth = value;
break;
case PARAM_SPEED_TAG:
freq = (29.5f * value) + 0.5f;
break;
case PARAM_TYPE_TAG:
type = (int32)(value * 2.0f);
break;
}
}
}
}
}
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];
#if DEVELOPMENT // 必ず DEVELOPMENT で囲むこと。
// FDebugPrint("data.numSamples:%5d\n", data.numSamples);
#endif
for (int32 i = 0; i < data.numSamples; i++)
{
Sample32 a = (sin(theta) * 0.5f) + 0.5f;
Sample32 b = (1.0f - depth) + (a * depth);
Sample32 c = (1.0f - depth) + ((1.0f - a) * depth);
switch (type)
{
case 0:
outL[i] = depth * inL[i];
outR[i] = depth * inR[i];
break;
case 1:
outL[i] = (a * depth) * inL[i];
outR[i] = (a * depth) * inR[i];
break;
case 2:
outL[i] = b * inL[i];
outR[i] = c * inR[i];
break;
}
theta += (2.0f * 3.14159265f * freq) / 44100.0f;
}
if (!initialized)//初回
{
if (data.numSamples <= 0)
{
return kResultFalse;
}
bufferSize = data.numSamples;
//3回分のdata.NumSamplesの数だけ履歴として保存しているので
//3回分の動的配列を生成 履歴数も可変にできるよう変更するとViewの尺度が長時間になり蜜な描画になる。
//初期化されていないときは、[履歴数][data.NumSamples]の配列を生成する。
outL3Times = new Sample32 * [3];
outR3Times = new Sample32 * [3];
for (int i = 0; i < 3; ++i)
{
outL3Times[i] = new Sample32[bufferSize];
outR3Times[i] = new Sample32[bufferSize];
}
initialized = true;
}
else//2回目
{
if (data.numSamples != bufferSize)//サンプル数が変わったら、履歴用の配列もサイズを再調整。
{
bufferSize = data.numSamples;
Sample32** newOutL3Times = new Sample32 * [3];
Sample32** newOutR3Times = new Sample32 * [3];
for (int i = 0; i < 3; ++i)
{
newOutL3Times[i] = new Sample32[bufferSize];
newOutR3Times[i] = new Sample32[bufferSize];
}
for (int i = 0; i < 3; ++i)//古い履歴バッファ配列を消滅
{
delete[] outL3Times[i];
delete[] outR3Times[i];
}
delete[] outL3Times;
delete[] outR3Times;
outL3Times = newOutL3Times;//新しく生成したバッファ配列にポインタを設定
outR3Times = newOutR3Times;
}
}
Sample32* temp;
switch(TimesCount){
case 0:
case 2:
case 3:
case 6:
case 7:
temp = outL3Times[2];//一時的に生成したポインタを一番古い履歴から取得。
outL3Times[2] = outL3Times[1];//一番古いポインタを真ん中のデータ履歴から取得
outL3Times[1] = outL3Times[0];//真ん中のポインタを一番新しい履歴から取得
outL3Times[0] = temp;//一番新しいポインタを一時的に生成したポインタから取得
temp = outR3Times[2];
outR3Times[2] = outR3Times[1];
outR3Times[1] = outR3Times[0];
outR3Times[0] = temp;
for (int i = 0; i < bufferSize; ++i)
{
outL3Times[0][i] = outL[i];//一番新しいポインタに出力データを取得。
outR3Times[0][i] = outR[i];
}
#if DEVELOPMENT // 必ず DEVELOPMENT で囲むこと。
//FDebugPrint("outL3Times1:%5f, %5f, %5f, %5f\n", outL3Times[0][1], outL3Times[0][2], outL3Times[0][3], outL3Times[0][4]);
//FDebugPrint("outL3Times2:%5f, %5f, %5f, %5f\n", outL3Times[1][1], outL3Times[1][2], outL3Times[1][3], outL3Times[1][4]);
//FDebugPrint("outL3Times3:%5f, %5f, %5f, %5f\n\n", outL3Times[2][1], outL3Times[2][2], outL3Times[2][3], outL3Times[2][4]);
#endif
break;
default:
break;
}
TimesCount++;
if (TimesCount >= 10) {
TimesCount = 0;//履歴カウンタが10を超えたら0に 0, [1], 2, 3, [4], [5], 6, 7, [8], [9]
//0,2,3,6,7だけ更新。
}
if(CxxGUIEdit_Instance != nullptr){
CxxGUIEdit_Instance->setSingnal(outL3Times, outR3Times, data.numSamples);
}
return kResultTrue;
}
} }
- view.h
#pragma once
#ifndef __CONTROLLER_H__
#define __CONTROLLER_H__
#include "public.sdk/source/vst/vsteditcontroller.h"
#include "def.h"
#include "guieditor.h"
namespace Steinberg{
namespace Vst {
class CxxEdit : public EditController
{
public:
tresult PLUGIN_API initialize(FUnknown* context);
IPlugView* PLUGIN_API createView(const char* name);
static FUnknown* createInstance(void*) { return (IEditController*)new CxxEdit(); }
};
} }
#endif
- view.cpp
#include "def.h"
#include "fuid.h"
#include "view.h"
namespace Steinberg{
namespace Vst {
tresult PLUGIN_API CxxEdit::initialize(FUnknown* context)
{
tresult result = EditController::initialize(context);
if (result == kResultTrue)
{
parameters.addParameter(STR16("Depth"), STR16("..."), 0, 1, ParameterInfo::kCanAutomate, PARAM_DEPTH_TAG);
RangeParameter* param1 = new RangeParameter(STR16("Speed"), PARAM_SPEED_TAG, STR16("Hz"), 0.5f, 30.0f, 5.0f);
param1->setPrecision(2);
parameters.addParameter(param1);
StringListParameter* param2 = new StringListParameter(STR16("Type"), PARAM_TYPE_TAG);
param2->appendString(STR16("Volume"));
param2->appendString(STR16("Tremolo"));
param2->appendString(STR16("Panning"));
parameters.addParameter(param2);
}
result = kResultTrue;
return result;
}
IPlugView* PLUGIN_API CxxEdit::createView(const char* name)
{
if (strcmp(name, "editor") == 0)
{
CxxGUIEdit* view = new CxxGUIEdit(this);
return view;
}
return 0;
}
} }
- guieditor.h
#pragma once
#ifndef __GUIEDITOR_H__
#define __GUIEDITOR_H__
#include "public.sdk/source/vst/vstguieditor.h"
#include "pluginterfaces/vst/ivstplugview.h"
#include "def.h"
#include "proc.h"
namespace Steinberg {
namespace Vst {
using namespace VSTGUI;
class CWaveDrawView : public CView
{
protected:
CDrawContext::LineList lines;
public:
CWaveDrawView(const CRect& size);
virtual void draw(CDrawContext* pContext) override;
void setWave(float* buf[], int size);
};
class CxxGUIEdit : public VSTGUIEditor, public IControlListener
{
CWaveDrawView* waveView;
public:
void setSingnal(Sample32** pSample32inL, Sample32** pSample32inR, int32 int32NumSamples);
CxxGUIEdit(void* controller);
virtual bool PLUGIN_API open(void* parent, const PlatformType& platformType = PlatformType::kDefaultNative);
virtual void PLUGIN_API close();
void valueChanged(CControl* pControl);
void createKnob(ParamID tag, int x, int y);
void createSlider(ParamID tag, int x, int y);
void createCombobox(ParamID tag, int x, int y);
CViewContainer* createView(ParamID tag, int x, int y);
DELEGATE_REFCOUNT(VSTGUIEditor)
CMessageResult notify(CBaseObject* sender, const char* message) override;
private:
Sample32** pSample32inL_Trans;
Sample32** pSample32inR_Trans;
int32 int32NumSamples_Trans;
};
} }
#include "GlobalConstants.h"
#endif
- guieditor.cpp
#include "guieditor.h"
Steinberg::Vst::CxxGUIEdit* CxxGUIEdit_Instance = nullptr;
namespace Steinberg {
namespace Vst {
CxxGUIEdit::CxxGUIEdit(void* controller)
: VSTGUIEditor(controller)
{
ViewRect viewRect(0, 0, 800, 200);
setRect(viewRect);
CxxGUIEdit_Instance = this;
}
// ============================================================================================
// GUIの起動初期設定
// ============================================================================================
bool PLUGIN_API CxxGUIEdit::open(void* parent, const PlatformType& platformType)
{
if (frame) { return false; }//frameは継承元のVSTGUIEditorのウィンドウハンドル。
CRect size(0, 0, 800, 200);
frame = new CFrame(size, this);
if (frame == NULL) { return false; }
CBitmap* cbmp = new CBitmap("background.png");
//大きさ横幅800[px]x高さ200[px]の白系背景
frame->setBackground(cbmp);
cbmp->forget();
frame->open(parent);
//初期値用の適当な波形
waveView = new CWaveDrawView(size);
float* wav[3];
wav[0] = new float[256];
wav[1] = new float[256];
wav[2] = new float[256];
float fTheta = 0;
for (int i = 0; i < 256; i++)
{
wav[0][i] = sin(fTheta + (2.0 * 3.14159265 * (double)(i * 880) / 44100.0));
fTheta += 2.0 * 3.14159265 * (double)(i * 880) / 44100.0
wav[1][i] = sin(fTheta + (2.0 * 3.14159265 * (double)(i * 880) / 44100.0));
fTheta += 2.0 * 3.14159265 * (double)(i * 880) / 44100.0
wav[2][i] = sin(fTheta + (2.0 * 3.14159265 * (double)(i * 880) / 44100.0));
fTheta += 2.0 * 3.14159265 * (double)(i * 880) / 44100.0
}
waveView->setWave(wav, 256);
createCombobox(PARAM_TYPE_TAG, 10, 10);
createKnob(PARAM_SPEED_TAG, 20, 35);
createSlider(PARAM_DEPTH_TAG, 20, 90);
CViewContainer* control = createView(PARAM_VIEW_TAG, 80, 10);
control->addView(waveView);//CViewContainerControlにCView型の波形描画を追加
return true;
}
void PLUGIN_API CxxGUIEdit::close()
{
if (frame)
{
frame->forget();
frame = 0;
}
}
void CxxGUIEdit::valueChanged(CControl* pControl)
{
int32 index = pControl->getTag();
float value = pControl->getValueNormalized();
controller->setParamNormalized(index, value);
controller->performEdit(index, value);
}
CViewContainer* CxxGUIEdit::createView(ParamID tag, int x, int y)
{
CBitmap* backbmp = new CBitmap("background2.png");
//大きさ横幅700[px]x高さ190[px]の黒系背景
CRect size(0, 0, backbmp->getWidth(), backbmp->getHeight());
size.offset(x, y);
CRect& size2 = size;//参照型変数に代入
CViewContainer* CViewContainerControl = new CViewContainer(size2);
CViewContainerControl->setBackground(backbmp);
frame->addView(CViewContainerControl);//親ウィンドウにViewContainerコントロールを追加。
backbmp->forget();
return CViewContainerControl;
}
void CxxGUIEdit::createKnob(ParamID tag, int x, int y)
{
CBitmap *backbmp = new CBitmap("knob.png");
CBitmap *handlebmp = new CBitmap("knob_handle.png");
CRect size(0, 0, backbmp->getWidth(), backbmp->getHeight());
size.offset(x, y);
CKnob* control = new CKnob(size, this, tag, backbmp, handlebmp);
ParamValue value = controller->getParamNormalized(tag);
control->setValueNormalized(value);
frame->addView(control);
backbmp->forget();
handlebmp->forget();
}
void CxxGUIEdit::createSlider(ParamID tag, int x, int y)
{
CBitmap *backbmp = new CBitmap("slider.png");
CBitmap *handlebmp = new CBitmap("slider_handle.png");
CRect size;
size(0, 0, backbmp->getWidth(), backbmp->getHeight());
size.offset(x, y);
int bmpmargin = 1;
CVerticalSlider* control = new CVerticalSlider(size, this, tag,
y + bmpmargin,
y + backbmp->getHeight() - (handlebmp->getHeight() + bmpmargin),
handlebmp, backbmp);
ParamValue value = controller->getParamNormalized(tag);
control->setValueNormalized(value);
frame->addView(control);
backbmp->forget();
handlebmp->forget();
}
void CxxGUIEdit::createCombobox(ParamID tag, int x, int y)
{
CRect size;
size(0, 0, 60, 16);
size.offset(x, y);
COptionMenu* control = new COptionMenu(size, this, tag);
control->addEntry("Volume");
control->addEntry("Tremolo");
control->addEntry("Panning");
ParamValue value = controller->getParamNormalized(tag);
control->setValueNormalized(value);
control->setFont(kNormalFontSmaller);
control->setFontColor(kWhiteCColor);
control->setBackColor(kBlackCColor);
control->setFrameColor(kRedCColor);
frame->addView(control);
}
CWaveDrawView::CWaveDrawView(const CRect& size)
: CView(size)
{
};
void CWaveDrawView::draw(CDrawContext* pContext)
{
if (getDrawBackground())
{
getDrawBackground()->draw(pContext, getViewSize());
}
pContext->setFrameColor(kBlueCColor);
pContext->setLineStyle(kLineSolid);
pContext->setLineWidth(4.0);
pContext->drawLines(lines);
setDirty(false);
}
void CWaveDrawView::setWave(float* buf[], int size)
{
lines.clear();
CRect viewSize = getViewSize();
CCoord leftX = viewSize.left;
CCoord centerY = viewSize.getCenter().y;
CPoint from(leftX, centerY);
for (int j = 0; j < 3; j++){
for (int i = 0; i < size; i++)
{
CCoord width = viewSize.getWidth();
CCoord height = viewSize.getHeight();
CPoint to(leftX + width * ((double)(j * size + i + 1) / (double)size * 3), centerY - height / 2.0 * buf[j][i]);
#if DEVELOPMENT // 必ず DEVELOPMENT で囲むこと。
// FDebugPrint("data.numSamples:%5f, %5f\n", leftX + width * ((double)(j * size + i + 1) / (double)size * 3), centerY - height / 2.0 * buf[j][i]);
#endif
CDrawContext::LinePair line(from, to);
lines.push_back(line);
from = to;
}
}
}
CMessageResult CxxGUIEdit::notify(CBaseObject* sender, const char* message)
{
if (message == CVSTGUITimer::kMsgTimer)
{
if (waveView != nullptr)
{
waveView->setWave(pSample32inL_Trans, int32NumSamples_Trans);
waveView->setDirty();
}
}
return VSTGUIEditor::notify(sender, message);
}
void CxxGUIEdit::setSingnal(Sample32** pSample32inL, Sample32** pSample32inR, int32 int32NumSamples) {
pSample32inL_Trans = pSample32inL;
pSample32inR_Trans = pSample32inR;
int32NumSamples_Trans = int32NumSamples;
}
} }
ASIO設定で256サンプル毎に処理する場合、サンプリング周波数44100では、5.78ミリ秒分のデータごとに処理され、遅延で音が鳴ります。5.78ミリ秒分のデータで波形を描くとC4の音は440Hzで1周期が2.27ミリ秒ですので、2周期分くらいが1回の処理でとらえられます。もう少し、とらえたいので、履歴3個分くらいのデータを描画すればそれなりに波形をとらえることができると思って3履歴にしました。可変にした方が使いやすいと思うので、コントロールを追加して、履歴の個数を選べるようにするという改良をすると良いと思っています。あとは、間引きして描画するため、データを描画用のデータ配列に写しとる処理と履歴配列の調整をする処理を0~9までをカウントしつづける中で0, 2, 3, 6, 7だけ更新するという仕組みにしています。気分です気分。処理を軽減する部分はもうちょっと考えないと駄目ですね。
同じような仕組みを使えば、周波数成分を描画するような処理も作れると思います。自分で描画できるようになれば、もうなんでもできるようになったと考えて問題ないでしょう。あとは、これをVSTGUI inline UI Editorを使って描画できるようになればトレンド(流行)にもついていけるようになったといえるので、そこだけ考えないと駄目だな。もうちょっと工夫が必要。基本的な音声信号処理も本を買えば、ギターの基本的なペダル(EQ,マルチバンドEQ,コンプ,FAZZ,OverDrive,Distortion,コーラス,リバーブ,ノイズゲート,フランジャー,ワウ)を網羅してくれていますので、自分で音声信号処理もできるようになると思います。
オススメの本は以下のとおりです。
- VSTプラグイン(著:うつぼかずら、版:工学社)Webの方が詳しい。https://www.utsbox.com/
- サウンドプログラミング入門――音響合成の基本とC言語による実装(Software Design plus)(著:青木直史、版:技術評論社)
- C言語ではじめる音のプログラミング―サウンドエフェクトの信号処理(著:青木直史、版:OHM社)
- プログラム101付き 音声信号処理(ディジタル信号処理シリーズ)(著:川村新、版:CQ出版)
- WAVプログラミング―C言語で学ぶ音響処理(著:北山洋幸、版:カットシステム)
あとはVSTiの作り方とかサンプラーみたいなVSTiをつくるというユーザインタフェイスの処理部分の勉強が必要かもしれません。自分も足りない部分を記事として書き起こすようにはしてみたいです。
VSTeプログラミング初歩に戻る。