VSTX Library


VSTX is a powerful C++ Windows library for pro audio developers to host VST plugins.

VSTX supports:
  • x86 and x64 plugins
  • Supports plugins with unlimited inputs and outputs, both midi and audio (effects and instruments)
  • VST 2.x and VST 3.x with a common API
  • Use as a static library
  • Use as a DLL
  • Use as a COM component for hosting VST plugins outside your process (Plugin sandboxing with same API)
  • VST Programs and Units
  • VST Automation Parameters
  • Preset Load/Save
  • 32 or 64 bit processing, any sample rate, any block size
  • Menu, ribbon or your own interface for the editor
  • Use it without Steinberg SDK
  • Use it with the Steinberg SDK if you require access to the low level details




Prepare your project by including the VSTx library:

#ifdef _WIN64
#import "vstx64.dll" raw_interfaces_only
#pragma comment(lib,"vstx64.lib")
#else
#import "vstx32.dll" raw_interfaces_only
#pragma comment(lib,"vstx32.lib")
#endif
#include "vstx.h"
								


To test a DLL for VST interfaces, you can use the inline provided function:

HMODULE LoadTry(const wchar_t* f, int& v)
								
Returns the HMODULE handle and in variable v:
  • 0 - The file is not a DLL
  • 1 - The file is a DLL but not a VST
  • 2 - The file is a VST 2.x
  • 3 - The file is a VST 3.x
  • 4 - The file is a VST that supports both VST 2.x and VST 3.x
  • 5 - The file is a plugin of different architecture (x64 if running the x86 version of the library or vice versa)
When using VSTX as a dynamic library, you can create a VST2 or VST3 object using the exported function CreateVST:

extern "C" HRESULT __stdcall CreateVST(int V,IUnknown** u);
int main()
{
	CComPtr<vstxlib::VSTZ> vv;
	CreateVST(3,(IUnknown**)&vv); // Creates a VST3, use 2 to create a VST 2.x
}
								
When using VSTX as a static library, you can create a VST2 or VST3 object using the vstxlib functions:

void Create2(IUnknown** z);
void Create3(IUnknown** z);

int main()
{
	CComPtr<vstxlib::VSTZ> vv;
	vstxlib::Create2((IUnknown**)&vv); // Or Create3 to create a VST3
}
								
When the library is registered through inproc (DLL) or outproc (EXE) or remote proc server, you create the object:

int main()
{
	CComPtr<vstxlib::VSTZ> vv1;
	CComPtr<vstxlib::VSTZ> vv2;
	CoCreateInstance(vstxlib::CLSID_VSTXLIBINPROC, 0, CLSCTX_INPROC_SERVER, __uuidof(vstxlib::VSTZ3), (LPVOID*)&vv1); // Creates an inproc VST3
	CoCreateInstance(vstxlib::CLSID_VSTXLIBOUTPROC, 0, CLSCTX_LOCAL_SERVER, __uuidof(vstxlib::VSTZ2), (LPVOID*)&vv2); // Creates an external proc VST2
}
								


Before loading, you can use the properties (put_X functions):

vv->put_Bitrate(32); // or 64
vv->put_BufferSize(BS); // Buffer Size
vv->put_SamplingRate(SR); // Sample Rate
								

Changing these options after the VST has been loaded results in buffer re-creation. You can use Reconfigure() to change all of them at once.

Load the DLL with function Load(). You can pass a preloaded DLL instance if you have used LoadTry() to test the VST interface.

HRESULT Load(BSTR File,ULONG_PTR PreloadInstance);

int main()
{
	auto hr = vv->Load(_bstr_t(L"plugin.dll"),0);
}
								
Load() executes the following functions:
  • Loads the DLL (if not already loaded using PreloadInstance)
  • Checks if the DLL is actually a VST and initializes it
  • Activates the VST
  • Enumerates and activates all programs, units, inputs and outputs


In addition to Bitrate, BlockSize, SampleRate, you can set/get:


// Program
HRESULT Program([out, retval] INT* pPr);
HRESULT Program([in] INT pPr);

// Unit (VST 3.x)
HRESULT Unit([out, retval] INT* pPr);
HRESULT Unit([in] INT pPr);

// Bypass 
HRESULT Bypass([out, retval] INT* pI);
HRESULT Bypass([in] INT p);

// Silence
HRESULT Silence([out, retval] INT* pI);
HRESULT Silence([in] INT p);

// Parameter
HRESULT Parameter([in] INT idx,[out, retval] FLOAT* pPr);
HRESULT Parameter([in] INT idx,[in] FLOAT val);

// Dirty
HRESULT Dirty([out, retval] INT* pPr);

// Names [Typ 0 = Programs, 1 = Units, 2 = Audio Input names, 3 = Audio Output names, 4 = Midi Input names, 5 = Midi Output names]
HRESULT Names([in] INT Typ,[out, retval] SAFEARRAY(BSTR)* pPr);

// State
HRESULT State([in] INT IsPreset,[out, retval] BSTR* pPr);
HRESULT State([in] INT IsPreset, [in] BSTR pPr);

// Parameter
HRESULT Parameter([in] INT idx,[out, retval] FLOAT* pPr);
HRESULT Parameter([in] INT idx,[in] FLOAT val);

											
And also you have:

HRESULT  Suspend();
HRESULT  Resume();
											



HRESULT  CreateBuffers1();
HRESULT  CreateBuffers2(BSTR* SharedMap);
HRESULT  CreateBuffers3(void** inb,void** outb);
HRESULT  GetDirectBuffers(char** inb, char** outb);
HRESULT  InputDirect(const char** b);
HRESULT  Process();
HRESULT  OutputDirect(char** b);
HRESULT  InputArray(SAFEARRAY(BSTR) s);
HRESULT  OutputArray(SAFEARRAY(BSTR)* s);
HRESULT  SendMidiDirect(void* p,INT n);
HRESULT  SendMidiArray(SAFEARRAY* p,INT n);
HRESULT  DeleteBuffers();
HRESULT  ClearIn();
HRESULT  ClearOut();
HRESULT  CopyIO();
											
If your library is static or in-proc server and you don't want direct access to the buffers, you do CreateBuffers1() and then in your loop
  • InputDirect() passing a float** or a double**
  • Process()
  • OutputDirect() to get the output data


If your library is static or in-proc server and you want direct access to the buffers, you do CreateBuffers3() with the parametets pointing to variables that will receive the direct input/output buffers. Then you write to them manually, call Process(), then read again manually from them in your loop.



For an in-process VST, you can always call GetDirectBuffers() to get the buffers as well.

If your library is in another address space but in the same PC, you do CreateBuffers2() with the first parameter passing a pointer to BSTR. This receives a string which you can use in OpenFileMapping() function, to open a file mapping handle which contains direct memory to read/write the data (shared between processes). You write/read this memory before/after calling Process() in your loop.

If your library is in another PC, you do CreateBuffers1() and you use the SAFEARRAY Input/Output functions to pass/receive a SAFEARRAY of VT_R4 or VT_R8 with the data.

Use SendMidiDirect() or SendMidiArray() before calling Process() with a vstxlib::MIDI array to send midi events to be processed at the next Process() call.



Call Show(ULONG_PTR hwnd,INT Rib) to show the editor, passing an optional parent and 0/1 depending on if you want a menu interface or a ribbon interface.

Call ShowCustom(ULONG_PTR hwnd) to show the VST in your own custom HWND without messing with the low level SDK details and to quickly show the VST in your own Window. Call ShowCustom(nullptr) to destroy the VST view before destroying your window.

If you want to use the low level SDK functions, you can use RequestInterface() to get the Steinberg::Vst::IEditController pointer, and then use it to create a View of the VST. For VST 2.x, you use RequestInterface() to get the AEffect and then dispatch an effEditOpen message.


You can use InstallHandler() / RemoveHandler() to install/remove notification handlers of type VSTNOTIFICATION:

virtual HRESULT __stdcall ParameterChanged(INT pidx,FLOAT v) = 0; // Called when an automation parameter is changed

virtual HRESULT __stdcall BeforeDelete(IUnknown *__MIDL__VSTNOTIFICATION0000) = 0; // Called just before the VST is released (increase the passed object's reference count to keep it live)

virtual HRESULT __stdcall SetDirty(INT); // Called when the plugin is or is not dirty (i.e. settings changed since last save).

virtual HRESULT __stdcall ReceiveMidi(SAFEARRAY * s) = 0; // Called when midi is received, a SAFEARRAY of BSTR. On VST 2.x, this is raw bytes of data. On VST 3.x, this is a Steinberg::Vst::Event structure.
								


You can use RequestInterface() to get a pointer to an internal interface.
  • For VST 2.x, pass an empty BSTR string. The library returns a pointer to AEffect.
  • For VST 3.x, pass an IID of the object to return. The library returns a pointer (if there is one) and calls addRef() on it. Call release() on it when done.
VST 3.x interfaces supported: IPluginFactory, IComponent, IAudioProcessor, IEditController, IPlugView, IUnitInfo. If you want another interface, then you can use IPluginFactory to create it.



[
	uuid(FE14BE28-C287-454C-9E5E-BD69FA312F03),
	version(1.0),
	helpstring("VSTX")
]
library vstxlib
{
	importlib("stdole2.tlb");

	interface VSTNOTIFICATION;
	[
		uuid(F193EFC3-2D9A-4B39-9AF8-EA7A6868C61D), odl, dual, oleautomation
	]
	interface VSTNOTIFICATION : IUnknown
	{
		[id(1001)] HRESULT __stdcall ParameterChanged(INT pidx, FLOAT v);
		[id(1002)] HRESULT __stdcall SetDirty(INT);
		[id(1003)] HRESULT __stdcall BeforeDelete(IUnknown*);
		[id(1004)] HRESULT __stdcall ReceiveMidi(SAFEARRAY(BSTR) s);
	};

	interface VSTZ;
	[
		uuid(F193EFC3-2D9A-4B39-9AF8-EA7A6868C61A), odl, dual, oleautomation
	]
	interface VSTZ : IDispatch
	{

		// BitRate
		[id(101), propget] HRESULT Bitrate([out, retval] INT* pBr);
		[id(101), propput] HRESULT Bitrate([in] INT pBr);

		// SampleRate
		[id(102), propget] HRESULT SamplingRate([out, retval] INT* pSr);
		[id(102), propput] HRESULT SamplingRate([in] INT pSr);

		// BufferSize
		[id(103), propget] HRESULT BufferSize([out, retval] INT* pBs);
		[id(103), propput] HRESULT BufferSize([in] INT pBs);

		// Program
		[id(104), propget] HRESULT Program([out, retval] INT* pPr);
		[id(104), propput] HRESULT Program([in] INT pPr);

		// State
		[id(105), propget] HRESULT State([in] INT IsPreset,[out, retval] BSTR* pPr);
		[id(105), propput] HRESULT State([in] INT IsPreset, [in] BSTR pPr);

		// Bypass
		[id(106), propget] HRESULT Bypass([out, retval] INT* pI);
		[id(106), propput] HRESULT Bypass([in] INT p);

		// Silence
		[id(107), propget] HRESULT Silence([out, retval] INT* pI);
		[id(107), propput] HRESULT Silence([in] INT p);

		// Parameter
		[id(108), propget] HRESULT Parameter([in] INT idx,[out, retval] FLOAT* pPr);
		[id(108), propput] HRESULT Parameter([in] INT idx,[in] FLOAT val);

		// Dirty
		[id(109), propget] HRESULT Dirty([out, retval] INT* pPr);

		// Unit
		[id(110), propget] HRESULT Unit([out, retval] INT* pPr);
		[id(110), propput] HRESULT Unit([in] INT pPr);

		// Names
		[id(111), propget] HRESULT Names([in] INT Typ,[out, retval] SAFEARRAY(BSTR)* pPr);

		// Ports
		[id(112), propget] HRESULT Ports([out] INT* nwi, [out] INT* nwo, [out] INT* nmi, [out] INT* nmo);



		// Functions
		[id(1501)] HRESULT __stdcall InstallHandler(VSTNOTIFICATION*);
		[id(1502)] HRESULT __stdcall RemoveHandler(VSTNOTIFICATION*);

		[id(2001)] HRESULT __stdcall Load(BSTR File,ULONG_PTR hDLL);
		[id(2002)] HRESULT __stdcall Show(ULONG_PTR hwnd,INT Rib);
		[id(2003)] HRESULT __stdcall GetVSTVersion(INT* v);
		[id(2004)] HRESULT __stdcall Unload();
		[id(2005)] HRESULT __stdcall Suspend(INT killAp);
		[id(2006)] HRESULT __stdcall Resume(INT restoreAP);
		[id(2007)] HRESULT __stdcall GetNameAndVendor(BSTR* p1,BSTR* p2);
		[id(2008)] HRESULT __stdcall KeyDown(INT ww,INT ll);
		[id(2009)] HRESULT __stdcall KeyUp(INT ww, INT ll);
		[id(2011)] HRESULT __stdcall DeleteBuffers();
		[id(2012)] HRESULT __stdcall ClearIn();
		[id(2013)] HRESULT __stdcall ClearOut();
		[id(2014)] HRESULT __stdcall CopyIO();
		[id(2015)] HRESULT __stdcall ListOfParametersDirect(char* dr);
		[id(2016)] HRESULT __stdcall ListOfParametersArray(SAFEARRAY(BSTR)* sr);
		[id(2071)] HRESULT __stdcall CreateBuffers1();
		[id(2072)] HRESULT __stdcall CreateBuffers2(BSTR* SharedMap);
		[id(2073)] HRESULT __stdcall CreateBuffers3(char** inb, char** outb);
		[id(2074)] HRESULT __stdcall Reconfigure(INT sr, INT bs, INT br, INT buffrecreate);
		[id(2075)] HRESULT __stdcall ShowCustom(ULONG_PTR target);
		[id(2076)] HRESULT __stdcall GetDirectBuffers(char** inb, char** outb);
		[id(2077)] HRESULT __stdcall CanBitrate(INT br);


		[id(3001)] HRESULT __stdcall InputDirect(const char** b);
		[id(3002)] HRESULT __stdcall Process();
		[id(3003)] HRESULT __stdcall OutputDirect(char** b);
		[id(3004)] HRESULT __stdcall InputArray(SAFEARRAY(BSTR) s);
		[id(3005)] HRESULT __stdcall OutputArray(SAFEARRAY(BSTR)* s);
		[id(3006)] HRESULT __stdcall SendMidiDirect(char* p,INT n);
		[id(3007)] HRESULT __stdcall SendMidiArray(SAFEARRAY(CHAR), INT n);
		[id(3008)] HRESULT __stdcall RequestInterface([in] BSTR iid,[out] ULONG_PTR* r);

	};


	interface VSTZ2;
	[
		uuid(F193EFC3-2D9A-4B39-9AF8-EA7A6868C61B), odl, dual, oleautomation
	]
	interface VSTZ2 : VSTZ
	{

	};

	interface VSTZ3;
	[
		uuid(F193EFC3-2D9A-4B39-9AF8-EA7A6868C61C), odl, dual, oleautomation
	]
	interface VSTZ3 : VSTZ
	{

	};


}
									


The library is available for testing. Download Here.

Contains:
  • x86/x64 DLLs
  • x86/x64 COM Servers
  • Five projects with Visual Studio 2019 source:
    • A VST Scanner project
    • A MP3 Player project
    • A Synth project
    • An advanced test project (with ability to create various VST instances)
    • A Pianoroll synth with VstI and save/load/play abilities
  • The vstx.h header and the vstx.idl file.
Evaluate the library, then please obtain a license.


Project: Scan
This project scans a directory for all VST files, then uses LoadTry() (to actually verify it's a VST), then loads the VST and displays port information. Included in the download (both binary and sources).


Project: MP3 Player
This project allows you to load any number of VST effects, then loads an MP3 and plays it through them. Included in the download (both binary and sources).


Project: Synth
This project allows you to load any number of VST instruments, then allows you either to use the keyboard to produce notes, or to use a MIDI input. Included in the download (both binary and sources).


Project: Roll
This project uses my free PianoRoll control (https://www.codeproject.com/Articles/4042343/A-flexible-Direct2D-Pianoroll-for-your-music-apps) to create music with save/load/play capabilities.




If you want to obtain a license that allows you to use VSTX in your apps, contact us via the Business Support here. Individual developers which develop free software will get the in-process DLL version for no charge.


Do mention what you want: VST 2.x or VST 3.x or both, Static or DLL versions or COM servers or all, including or not the C++ source code.