Browse Source

fix debug plugin (can now be loaded in Renoise (Linux) host)

pull/1639/head
bsp2 6 years ago
parent
commit
631e62371d
1 changed files with 266 additions and 58 deletions
  1. +266
    -58
      other/vst2_lglw_debug_plugin/plugin.cpp

+ 266
- 58
other/vst2_lglw_debug_plugin/plugin.cpp View File

@@ -1,4 +1,5 @@


#define USE_LGLW defined


#include <aeffect.h> #include <aeffect.h>
#include <aeffectx.h> #include <aeffectx.h>
@@ -14,6 +15,7 @@
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/Xos.h> #include <X11/Xos.h>
#define VST_EXPORT extern #define VST_EXPORT extern
#include <dlfcn.h>
#endif #endif


#include "lglw.h" #include "lglw.h"
@@ -67,8 +69,9 @@ const VstInt32 PLUGIN_VERSION = 1000;


// extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // extern "C" LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


#ifdef USE_LGLW
void loc_mouse_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _buttonState, uint32_t _changedButtonState) { void loc_mouse_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _buttonState, uint32_t _changedButtonState) {
printf("xxx lglw_mouse_cbk: lglw=%p p=(%d; %d) bt=0x%08x changedBt=0x%08x\n", _lglw, _x, _y, _buttonState, _changedButtonState);
printf("vstgltest: lglw_mouse_cbk: lglw=%p p=(%d; %d) bt=0x%08x changedBt=0x%08x\n", _lglw, _x, _y, _buttonState, _changedButtonState);


if(LGLW_IS_MOUSE_LBUTTON_DOWN()) if(LGLW_IS_MOUSE_LBUTTON_DOWN())
{ {
@@ -82,17 +85,18 @@ void loc_mouse_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _buttonState,
} }


void loc_focus_cbk(lglw_t _lglw, uint32_t _focusState, uint32_t _changedFocusState) { void loc_focus_cbk(lglw_t _lglw, uint32_t _focusState, uint32_t _changedFocusState) {
printf("xxx lglw_focus_cbk: lglw=%p focusState=0x%08x changedFocusState=0x%08x\n", _lglw, _focusState, _changedFocusState);
printf("vstgltest: lglw_focus_cbk: lglw=%p focusState=0x%08x changedFocusState=0x%08x\n", _lglw, _focusState, _changedFocusState);
} }


lglw_bool_t loc_keyboard_cbk(lglw_t _lglw, uint32_t _vkey, uint32_t _kmod, lglw_bool_t _bPressed) { lglw_bool_t loc_keyboard_cbk(lglw_t _lglw, uint32_t _vkey, uint32_t _kmod, lglw_bool_t _bPressed) {
printf("xxx lglw_keyboard_cbk: lglw=%p vkey=0x%08x (\'%c\') kmod=0x%08x bPressed=%d\n", _lglw, _vkey, _vkey, _kmod, _bPressed);
printf("vstgltest: lglw_keyboard_cbk: lglw=%p vkey=0x%08x (\'%c\') kmod=0x%08x bPressed=%d\n", _lglw, _vkey, _vkey, _kmod, _bPressed);
return LGLW_FALSE; return LGLW_FALSE;
} }


void loc_timer_cbk(lglw_t _lglw) { void loc_timer_cbk(lglw_t _lglw) {
printf("xxx lglw_timer_cbk: tick\n");
printf("vstgltest: lglw_timer_cbk: tick\n");
} }
#endif // USE_LGLW


/** /**
* Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the * Encapsulates the plugin as a C++ class. It will keep both the host callback and the structure required by the
@@ -104,7 +108,9 @@ class VSTPluginWrapper
public: public:
ERect editor_rect; ERect editor_rect;


#ifdef USE_LGLW
lglw_t lglw; lglw_t lglw;
#endif // USE_LGLW


float clear_color = 0.0f; float clear_color = 0.0f;


@@ -136,8 +142,18 @@ public:
return _vstPlugin.numOutputs; return _vstPlugin.numOutputs;
} }


int openEffect(void) {
printf("vstgltest: openEffect()\n");
return 1;
}

void closeEffect(void) {
closeEditor();
}

void openEditor(void *_hwnd) { void openEditor(void *_hwnd) {

#ifdef USE_LGLW
(void)lglw_window_open(lglw, _hwnd, 0/*x*/, 0/*y*/, EDITWIN_W, EDITWIN_H); (void)lglw_window_open(lglw, _hwnd, 0/*x*/, 0/*y*/, EDITWIN_W, EDITWIN_H);


lglw_mouse_callback_set(lglw, &loc_mouse_cbk); lglw_mouse_callback_set(lglw, &loc_mouse_cbk);
@@ -146,6 +162,7 @@ public:
lglw_timer_callback_set(lglw, &loc_timer_cbk); lglw_timer_callback_set(lglw, &loc_timer_cbk);


lglw_timer_start(lglw, 200); lglw_timer_start(lglw, 200);
#endif // USE_LGLW


window_to_wrapper = this; window_to_wrapper = this;
} }
@@ -153,7 +170,9 @@ public:
void closeEditor(void) { void closeEditor(void) {
if(NULL != window_to_wrapper) if(NULL != window_to_wrapper)
{ {
#ifdef USE_LGLW
lglw_window_close(lglw); lglw_window_close(lglw);
#endif // USE_LGLW


window_to_wrapper = NULL; window_to_wrapper = NULL;
} }
@@ -165,6 +184,7 @@ public:


void redrawWindow(void) { void redrawWindow(void) {
#if 0 #if 0
#ifdef USE_LGLW
// Save host GL context // Save host GL context
lglw_glcontext_push(lglw); lglw_glcontext_push(lglw);


@@ -180,6 +200,7 @@ public:


// Restore host GL context // Restore host GL context
lglw_glcontext_pop(lglw); lglw_glcontext_pop(lglw);
#endif // USE_LGLW
#endif #endif
} }


@@ -209,6 +230,7 @@ VSTPluginWrapper *VSTPluginWrapper::window_to_wrapper = NULL;
* @param outputs an array of array of output samples. You write to it. First dimension is for outputs, second dimension is for samples: outputs[numOuputs][sampleFrames] * @param outputs an array of array of output samples. You write to it. First dimension is for outputs, second dimension is for samples: outputs[numOuputs][sampleFrames]
* @param sampleFrames the number of samples (second dimension in both arrays) * @param sampleFrames the number of samples (second dimension in both arrays)
*/ */
extern "C" {
void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float **outputs, VstInt32 sampleFrames) void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float **outputs, VstInt32 sampleFrames)
{ {
// we can get a hold to our C++ class since we stored it in the `object` field (see constructor) // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
@@ -226,6 +248,7 @@ void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float
} }
} }
} }
}


/** /**
* This is the callback that will be called to process the samples in the case of double precision. This is where the * This is the callback that will be called to process the samples in the case of double precision. This is where the
@@ -236,6 +259,7 @@ void VSTPluginProcessSamplesFloat32(VSTPlugin *vstPlugin, float **inputs, float
* @param outputs an array of array of output samples. You write to it. First dimension is for outputs, second dimension is for samples: outputs[numOuputs][sampleFrames] * @param outputs an array of array of output samples. You write to it. First dimension is for outputs, second dimension is for samples: outputs[numOuputs][sampleFrames]
* @param sampleFrames the number of samples (second dimension in both arrays) * @param sampleFrames the number of samples (second dimension in both arrays)
*/ */
extern "C" {
void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, double **outputs, VstInt32 sampleFrames) void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, double **outputs, VstInt32 sampleFrames)
{ {
// we can get a hold to our C++ class since we stored it in the `object` field (see constructor) // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
@@ -253,6 +277,7 @@ void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, doubl
} }
} }
} }
}


/** /**
* This is the plugin called by the host to communicate with the plugin, mainly to request information (like the * This is the plugin called by the host to communicate with the plugin, mainly to request information (like the
@@ -267,6 +292,7 @@ void VSTPluginProcessSamplesFloat64(VSTPlugin *vstPlugin, double **inputs, doubl
* @param opt depend on the opcode * @param opt depend on the opcode
* @return depend on the opcode (0 is ok when you don't implement an opcode...) * @return depend on the opcode (0 is ok when you don't implement an opcode...)
*/ */
extern "C" {
VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt) VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt)
{ {
// printf("vstgltest: called VSTPluginDispatcher(%d)\n", opCode); // printf("vstgltest: called VSTPluginDispatcher(%d)\n", opCode);
@@ -278,14 +304,31 @@ VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 in
// see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them // see aeffect.h/AEffectOpcodes and aeffectx.h/AEffectXOpcodes for details on all of them
switch(opCode) switch(opCode)
{ {
default:
printf("vstgltest: unhandled VSTPluginDispatcher opcode=%d\n", opCode);
break;

case effGetVstVersion: /*58*/
r = 0;
break;

// request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed // request for the category of the plugin: in this case it is an effect since it is modifying the input (as opposed
// to generating sound) // to generating sound)
case effGetPlugCategory: case effGetPlugCategory:
// return kPlugCategEffect; // return kPlugCategEffect;
return kPlugCategSynth; return kPlugCategSynth;


case effOpen:
// called by the host after it has obtained the effect instance (but _not_ during plugin scans)
// (note) any heavy-lifting init code should go here
printf("vstgltest: effOpen\n");
r = wrapper->openEffect();
break;

// called by the host when the plugin was called... time to reclaim memory! // called by the host when the plugin was called... time to reclaim memory!
case effClose: case effClose:
printf("vstgltest: effClose\n");
wrapper->closeEffect();
delete wrapper; delete wrapper;
break; break;


@@ -294,6 +337,11 @@ VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 in
r = 1; r = 1;
break; break;


case effGetProductString:
::strncpy((char*)ptr, "VST GL Test ProdStr", kVstMaxProductStrLen);
r = 1;
break;

// request for the vendor string (usually used in the UI for plugin grouping) // request for the vendor string (usually used in the UI for plugin grouping)
case effGetVendorString: case effGetVendorString:
strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen); strncpy(static_cast<char *>(ptr), "bsp", kVstMaxVendorStrLen);
@@ -304,19 +352,142 @@ VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 in
case effGetVendorVersion: case effGetVendorVersion:
return PLUGIN_VERSION; return PLUGIN_VERSION;


case effGetNumMidiInputChannels:
r = 16;
break;

case effGetNumMidiOutputChannels:
r = 0;
break;

case effCanDo:
// ptr:
// "sendVstEvents"
// "sendVstMidiEvent"
// "sendVstTimeInfo"
// "receiveVstEvents"
// "receiveVstMidiEvent"
// "receiveVstTimeInfo"
// "offline"
// "plugAsChannelInsert"
// "plugAsSend"
// "mixDryWet"
// "noRealTime"
// "multipass"
// "metapass"
// "1in1out"
// "1in2out"
// "2in1out"
// "2in2out"
// "2in4out"
// "4in2out"
// "4in4out"
// "4in8out"
// "8in4out"
// "8in8out"
// "midiProgramNames"
// "conformsToWindowRules"
if(!strcmp((char*)ptr, "receiveVstEvents"))
r = 1;
else if(!strcmp((char*)ptr, "receiveVstMidiEvent")) // (note) required by Jeskola Buzz
r = 1;
else if(!strcmp((char*)ptr, "noRealTime"))
r = 1;
else
r = 0;
break;

case effGetInputProperties:
{
VstPinProperties *pin = (VstPinProperties*)ptr;
::snprintf(pin->label, kVstMaxLabelLen, "Input #%d", index);
pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "in%d", index);
memset((void*)pin->future, 0, 48);
r = 1;
}
break;

case effGetOutputProperties:
{
VstPinProperties *pin = (VstPinProperties*)ptr;
::snprintf(pin->label, kVstMaxLabelLen, "Output #%d", index);
pin->flags = kVstPinIsActive | ((0 == (index & 1)) ? kVstPinIsStereo : 0);
pin->arrangementType = ((0 == (index & 1)) ? kSpeakerArrStereo : kSpeakerArrMono);
::snprintf(pin->shortLabel, kVstMaxShortLabelLen, "out%d", index);
memset((void*)pin->future, 0, 48);
r = 1;
}
break;

case effSetSampleRate:
printf("vstgltest: effSetSampleRate(%f)\n", opt);
r = 1;////wrapper->setSampleRate(opt) ? 1 : 0;
break;

case effSetBlockSize:
printf("vstgltest: effSetBlockSize(%u)\n", uint32_t(value));
r = 1;////wrapper->setBlockSize(uint32_t(value)) ? 1 : 0;
break;

case effMainsChanged:
printf("vstgltest: effMainsChanged(%d)\n", value);
// value = 0=suspend, 1=resume
// wrapper->setEnableProcessingActive((value > 0) ? true : false);
r = 1;
break;

case effSetProgram:
r = 1;
break;

case effGetProgram:
r = 0;
break;

case effGetProgramName:
::snprintf((char*)ptr, kVstMaxProgNameLen, "default");
r = 1;
break;

case effSetProgramName:
r = 1;
break;

case effGetProgramNameIndexed:
::sprintf((char*)ptr, "default");
r = 1;
break;

case effGetParamName: case effGetParamName:
strncpy(static_cast<char *>(ptr), "myparam", kVstMaxParamStrLen); strncpy(static_cast<char *>(ptr), "myparam", kVstMaxParamStrLen);
r = 1; r = 1;
break; break;


case effCanBeAutomated:
// fix Propellerhead Reason VST parameter support
r = 1;
break;

case effStartProcess:
r = 1;
break;

case effStopProcess:
r = 1;
break;

case effEditIdle: case effEditIdle:
printf("xxx vstgltest: redraw window\n");
printf("vstgltest: redraw window\n");
// (void)::RedrawWindow(wrapper->hwnd, NULL, NULL, RDW_INTERNALPAINT); // (void)::RedrawWindow(wrapper->hwnd, NULL, NULL, RDW_INTERNALPAINT);
//(void)::UpdateWindow(wrapper->hwnd); //(void)::UpdateWindow(wrapper->hwnd);
#ifdef USE_LGLW
if(lglw_window_is_visible(wrapper->lglw)) if(lglw_window_is_visible(wrapper->lglw))
{ {
wrapper->redrawWindow(); wrapper->redrawWindow();
} }
#endif // USE_LGLW
break; break;


case effEditGetRect: case effEditGetRect:
@@ -358,35 +529,36 @@ VstIntPtr VSTPluginDispatcher(VSTPlugin *vstPlugin, VstInt32 opCode, VstInt32 in
r = 1; r = 1;
break; break;


// ignoring all other opcodes
default:
// printf("Unknown opCode %d [ignored] \n", opCode);
break;
} }


return r; return r;
} }
}


/** /**
* Used for parameter setting (not used by this plugin) * Used for parameter setting (not used by this plugin)
*/ */
extern "C" {
void VSTPluginSetParameter(VSTPlugin *vstPlugin, VstInt32 index, float parameter) void VSTPluginSetParameter(VSTPlugin *vstPlugin, VstInt32 index, float parameter)
{ {
printf("called VSTPluginSetParameter(%d, %f)\n", index, parameter);
printf("vstgltest: VSTPluginSetParameter(%d, %f)\n", index, parameter);
// we can get a hold to our C++ class since we stored it in the `object` field (see constructor) // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
} }
}


/** /**
* Used for parameter (not used by this plugin) * Used for parameter (not used by this plugin)
*/ */
extern "C" {
float VSTPluginGetParameter(VSTPlugin *vstPlugin, VstInt32 index) float VSTPluginGetParameter(VSTPlugin *vstPlugin, VstInt32 index)
{ {
printf("called VSTPluginGetParameter(%d)\n", index);
printf("vstgltest: VSTPluginGetParameter(%d)\n", index);
// we can get a hold to our C++ class since we stored it in the `object` field (see constructor) // we can get a hold to our C++ class since we stored it in the `object` field (see constructor)
VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object); VSTPluginWrapper *wrapper = static_cast<VSTPluginWrapper *>(vstPlugin->object);
return 0; return 0;
} }
}


/** /**
* Main constructor for our C++ class * Main constructor for our C++ class
@@ -400,38 +572,45 @@ VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback,
VstInt32 numOutputs) : VstInt32 numOutputs) :
_vstHostCallback(vstHostCallback) _vstHostCallback(vstHostCallback)
{ {
// Make sure that the memory is properly initialized
memset(&_vstPlugin, 0, sizeof(_vstPlugin));

// this field must be set with this constant...
_vstPlugin.magic = kEffectMagic;

// storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use)
_vstPlugin.object = this;

// specifying that we handle both single and double precision (there are other flags see aeffect.h/VstAEffectFlags)
_vstPlugin.flags = effFlagsCanReplacing | effFlagsCanDoubleReplacing | effFlagsHasEditor;

// initializing the plugin with the various values
_vstPlugin.uniqueID = vendorUniqueID;
_vstPlugin.version = vendorVersion;
_vstPlugin.numParams = numParams;
_vstPlugin.numPrograms = numPrograms;
_vstPlugin.numInputs = numInputs;
_vstPlugin.numOutputs = numOutputs;

// setting the callbacks to the previously defined functions
_vstPlugin.dispatcher = VSTPluginDispatcher;
_vstPlugin.getParameter = VSTPluginGetParameter;
_vstPlugin.setParameter = VSTPluginSetParameter;
_vstPlugin.processReplacing = VSTPluginProcessSamplesFloat32;
_vstPlugin.processDoubleReplacing = VSTPluginProcessSamplesFloat64;

printf("xxx debug_plugin: calling lglw_init()\n");

lglw = lglw_init(EDITWIN_W, EDITWIN_H);

printf("xxx debug_plugin: lglw_init() returned lglw=%p\n", lglw);
// Make sure that the memory is properly initialized
memset(&_vstPlugin, 0, sizeof(_vstPlugin));

// this field must be set with this constant...
_vstPlugin.magic = kEffectMagic;

// storing this object into the VSTPlugin so that it can be retrieved when called back (see callbacks for use)
_vstPlugin.object = this;

// specifying that we handle both single and double precision (there are other flags see aeffect.h/VstAEffectFlags)
_vstPlugin.flags =
effFlagsIsSynth |
effFlagsCanReplacing |
effFlagsCanDoubleReplacing |
effFlagsHasEditor
;

// initializing the plugin with the various values
_vstPlugin.uniqueID = vendorUniqueID;
_vstPlugin.version = vendorVersion;
_vstPlugin.numParams = numParams;
_vstPlugin.numPrograms = numPrograms;
_vstPlugin.numInputs = numInputs;
_vstPlugin.numOutputs = numOutputs;

// setting the callbacks to the previously defined functions
_vstPlugin.dispatcher = VSTPluginDispatcher;
_vstPlugin.getParameter = VSTPluginGetParameter;
_vstPlugin.setParameter = VSTPluginSetParameter;
_vstPlugin.processReplacing = VSTPluginProcessSamplesFloat32;
_vstPlugin.processDoubleReplacing = VSTPluginProcessSamplesFloat64;

#ifdef USE_LGLW
printf("vstgltest: calling lglw_init()\n");

lglw = lglw_init(EDITWIN_W, EDITWIN_H);

printf("vstgltest: lglw_init() returned lglw=%p\n", lglw);
#endif // USE_LGLW
} }


/** /**
@@ -440,7 +619,9 @@ VSTPluginWrapper::VSTPluginWrapper(audioMasterCallback vstHostCallback,
* memory leak which may end up slowing down and/or crashing the host * memory leak which may end up slowing down and/or crashing the host
*/ */
VSTPluginWrapper::~VSTPluginWrapper() { VSTPluginWrapper::~VSTPluginWrapper() {
#ifdef USE_LGLW
lglw_exit(lglw); lglw_exit(lglw);
#endif // USE_LGLW
} }


/** /**
@@ -448,18 +629,45 @@ VSTPluginWrapper::~VSTPluginWrapper() {
*/ */
VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback) VST_EXPORT VSTPlugin *VSTPluginMain(VSTHostCallback vstHostCallback)
{ {
printf("called VSTPluginMain... \n");

// simply create our plugin C++ class
VSTPluginWrapper *plugin =
new VSTPluginWrapper(vstHostCallback,
CCONST('u', 's', 'a', '§'), // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm)
PLUGIN_VERSION, // version
2, // no params
0, // no programs
2, // 2 inputs
2); // 2 outputs

// return the plugin per the contract of the API
return plugin->getVSTPlugin();
printf("vstgltest: entering VSTPluginMain... \n");
{
FILE *fh = fopen("/tmp/debug_lglw.txt", "w");
fprintf(fh, "hello\n");
fflush(fh);
fclose(fh);
}

{
Dl_info dlInfo;
char dllnameraw[1024];
char *dllnamerawp = dllnameraw;
char oldCWD[1024];
getcwd(oldCWD, 1024);
::dladdr((void*)VSTPluginMain, &dlInfo);
if('/' != dlInfo.dli_fname[0])
{
// (note) 'dli_fname' can be a relative path (e.g. when loaded from vst2_debug_host)
sprintf(dllnameraw, "%s/%s", oldCWD, dlInfo.dli_fname);
}
else
{
// Absolute path (e.g. when loaded from Renoise host)
dllnamerawp = (char*)dlInfo.dli_fname;
}
printf("vstgltest: dllname=\"%s\"\n", dllnamerawp);
}

// simply create our plugin C++ class
VSTPluginWrapper *plugin =
new VSTPluginWrapper(vstHostCallback,
//CCONST('u', 's', 'a', '§'), // registered with Steinberg (http://service.steinberg.de/databases/plugin.nsf/plugIn?openForm)
CCONST('t', 'e', 's', 't'), // unregistered
PLUGIN_VERSION, // version
0, // no params
1, // no programs
0, // 2 inputs
2); // 2 outputs

// return the plugin per the contract of the API
return plugin->getVSTPlugin();
} }

Loading…
Cancel
Save