Page 1 of 2

Need Help to Implement VST in PureBasic

Posted: Thu Oct 28, 2004 7:20 am
by zapman*
I try to implement the VST host support in PureBasic. My Sources in C are :

Code: Select all

00001 #ifndef __AEffect__
00002 #define __AEffect__
00003 
00004 /*
00005         to create an Audio Effect for power pc's, create a
00006         code resource
00007         file type: 'aPcs'
00008         resource type: 'aEff'
00009         ppc header: none (raw pef)
00010 
00011         for windows, it's a .dll
00012 
00013         the only symbol searched for is:
00014         AEffect *main(float (*audioMaster)(AEffect *effect, long opcode, long index,
00015                 long value, void *ptr, float opt));
00016 */
00017 
00018 #if 0
00019 
00020 /* dmazzoni: PRAGMA_ALIGN_SUPPORTED is obsolete in mwerks */
00021 #ifdef __MWERKS__
00022 #undef PRAGMA_ALIGN_SUPPORTED
00023 #endif
00024 
00025 #if PRAGMA_ALIGN_SUPPORTED || __MWERKS__
00026 #pragma options align=mac68k
00027 #elif defined CBUILDER
00028 #pragma -a8
00029 #elif defined(WIN32) || defined(__FLAT__)
00030 #pragma pack(push)
00031 #pragma pack(8)
00032 #endif
00033 
00034 #endif
00035 
00036 #if defined(WIN32) || defined(__FLAT__) || defined CBUILDER
00037 #define VSTCALLBACK __cdecl
00038 #else
00039 #define VSTCALLBACK
00040 #endif
00041 
00042 //---------------------------------------------------------------------------------------------
00043 // misc def's
00044 //---------------------------------------------------------------------------------------------
00045 
00046 typedef struct AEffect AEffect;
00047 typedef long (VSTCALLBACK * audioMasterCallback) (AEffect * effect,
00048                                                   long opcode, long index,
00049                                                   long value, void *ptr,
00050                                                   float opt);
00051 
00052 // prototype for plug-in main
00053 // AEffect *main(audioMasterCallback audioMaster);
00054 
00055 #ifdef CBUILDER
00056 #define kEffectMagic 'PtsV'
00057 #else
00058 #define kEffectMagic 'VstP'
00059 #endif
00060 
00061 //---------------------------------------------------------------------------------------------
00062 //---------------------------------------------------------------------------------------------
00063 
00064 struct AEffect {
00065    long magic;                  // must be kEffectMagic ('VstP')
00066    long (VSTCALLBACK * dispatcher) (AEffect * effect, long opCode,
00067                                     long index, long value, void *ptr,
00068                                     float opt);
00069    void (VSTCALLBACK * process) (AEffect * effect, float **inputs,
00070                                  float **outputs, long sampleframes);
00071    void (VSTCALLBACK * setParameter) (AEffect * effect, long index,
00072                                       float parameter);
00073    float (VSTCALLBACK * getParameter) (AEffect * effect, long index);
00074 
00075    long numPrograms;
00076    long numParams;              // all programs are assumed to have numParams parameters
00077    long numInputs;              //
00078    long numOutputs;             //
00079    long flags;                  // see constants
00080    long resvd1;                 // reserved, must be 0
00081    long resvd2;                 // reserved, must be 0
00082    long initialDelay;           // for algorithms which need input in the first place
00083    long realQualities;          // number of realtime qualities (0: realtime)
00084    long offQualities;           // number of offline qualities (0: realtime only)
00085    float ioRatio;               // input samplerate to output samplerate ratio, not used yet
00086    void *object;                // for class access (see AudioEffect.hpp), MUST be 0 else!
00087    void *user;                  // user access
00088    long uniqueID;               // pls choose 4 character as unique as possible.
00089    // this is used to identify an effect for save+load
00090    long version;                //
00091    void (VSTCALLBACK * processReplacing) (AEffect * effect, float **inputs,
00092                                           float **outputs,
00093                                           long sampleframes);
00094    char future[60];             // pls zero
00095 };
00096 
00097 
00098 //---------------------------------------------------------------------------------------------
00099 // flags bits
00100 //---------------------------------------------------------------------------------------------
00101 
00102 #define effFlagsHasEditor               1       // if set, is expected to react to editor messages
00103 #define effFlagsHasClip                 2       // return > 1. in getVu() if clipped
00104 #define effFlagsHasVu                   4       // return vu value in getVu(); > 1. means clipped
00105 #define effFlagsCanMono                 8       // if numInputs == 2, makes sense to be used for mono in
00106 #define effFlagsCanReplacing    16      // supports in place output (processReplacing() exsists)
00107 #define effFlagsProgramChunks   32      // program data are handled in formatless chunks
00108 
00109 //---------------------------------------------------------------------------------------------
00110 // dispatcher opCodes
00111 //---------------------------------------------------------------------------------------------
00112 
00113 enum {
00114    effOpen = 0,                 // initialise
00115    effClose,                    // exit, release all memory and other resources!
00116 
00117    effSetProgram,               // program no in <value>
00118    effGetProgram,               // return current program no.
00119    effSetProgramName,           // user changed program name (max 24 char + 0) to as passed in string 
00120    effGetProgramName,           // stuff program name (max 24 char + 0) into string 
00121 
00122    effGetParamLabel,            // stuff parameter <index> label (max 8 char + 0) into string
00123    // (examples: sec, dB, type)
00124    effGetParamDisplay,          // stuff parameter <index> textual representation into string
00125    // (examples: 0.5, -3, PLATE)
00126    effGetParamName,             // stuff parameter <index> label (max 8 char + 0) into string
00127    // (examples: Time, Gain, RoomType) 
00128    effGetVu,                    // called if (flags & (effFlagsHasClip | effFlagsHasVu))
00129 
00130    // system
00131 
00132    effSetSampleRate,            // in opt (float)
00133    effSetBlockSize,             // in value
00134    effMainsChanged,             // the user has switched the 'power on' button to
00135    // value (0 off, else on). This only switches audio
00136    // processing; you should flush delay buffers etc.
00137    // editor
00138 
00139    effEditGetRect,              // stuff rect (top, left, bottom, right) into ptr
00140    effEditOpen,                 // system dependant Window pointer in ptr
00141    effEditClose,                // no arguments
00142    effEditDraw,                 // draw method, ptr points to rect
00143    effEditMouse,                // index: x, value: y
00144    effEditKey,                  // system keycode in value
00145    effEditIdle,                 // no arguments. Be gentle!
00146    effEditTop,                  // window has topped, no arguments
00147    effEditSleep,                // window goes to background
00148 
00149    // new
00150 
00151    effIdentify,                 // returns 'NvEf'
00152    effGetChunk,                 // host requests pointer to chunk into (void**)ptr, byteSize returned
00153    effSetChunk,                 // plug-in receives saved chunk, byteSize passed
00154 
00155    effNumOpcodes
00156 };
00157 
00158 //---------------------------------------------------------------------------------------------
00159 // audioMaster opCodes
00160 //---------------------------------------------------------------------------------------------
00161 
00162 enum {
00163    audioMasterAutomate = 0,     // index, value, returns 0
00164    audioMasterVersion,          // vst version, currently 2 (0 for older)
00165    audioMasterCurrentId,        // returns the unique id of a plug that's currently
00166    // loading
00167    audioMasterIdle,             // call application idle routine (this will
00168    // call effEditIdle for all open editors too) 
00169    audioMasterPinConnected      // inquire if an input or output is beeing connected;
00170        // index enumerates input or output counting from zero,
00171        // value is 0 for input and != 0 otherwise. note: the
00172        // return value is 0 for <true> such that older versions
00173        // will always return true.
00174 };
00175 
00176 #if 0
00177 
00178 #if PRAGMA_ALIGN_SUPPORTED || __MWERKS__
00179 #pragma options align=reset
00180 #elif defined(WIN32) || defined(__FLAT__)
00181 #pragma pack(pop)
00182 #elif defined CBUILDER
00183 #pragma -a-
00184 #endif
00185 
00186 #endif
00187 
00188 #endif                          // __AEffect__


AND


Code: Select all

//host.cpp
//attempts to load and make use of a VST plugin

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream.h>
#include <fstream.h>
#include <string.h>

#include "aeffectx.h"

const int blocksize=512;
const float samplerate=44100.0f;

long VSTCALLBACK host(AEffect *effect, long opcode, long index, long value, void *ptr, float opt);

void main()
{
	////////////////////////////////////////////////////////////////////////////
	//
	//	Loading a plugin
	//
	////////////////////////////////////////////////////////////////////////////

	//create a pointer for the plugin we're going to load
	AEffect* ptrPlug=NULL;
	bool editor;

	//find and load the DLL and get a pointer to its main function
	//this has a protoype like this: AEffect *main (audioMasterCallback audioMaster)

	HINSTANCE libhandle=LoadLibrary("c:\\program files\\steinberg\\vstplugins\\muon\\cm101.dll");

	if (libhandle!=NULL)
	{
		//DLL was loaded OK
		AEffect* (__cdecl* getNewPlugInstance)(audioMasterCallback);
		getNewPlugInstance=(AEffect*(__cdecl*)(audioMasterCallback))GetProcAddress(libhandle, "main");

		if (getNewPlugInstance!=NULL)
		{
			//main function located OK
			ptrPlug=getNewPlugInstance(host);

			if (ptrPlug!=NULL)
			{
				//plugin instantiated OK
				cout << "Plugin was loaded OK" << endl;

				if (ptrPlug->magic==kEffectMagic)
				{
					cout << "It's a valid VST plugin" << endl;
				}
				else
				{
					cout << "Not a VST plugin" << endl;
					FreeLibrary(libhandle);
					Sleep(5000);
					return;
				}
			}
			else
			{
				cout << "Plugin could not be instantiated" << endl;
				FreeLibrary(libhandle);
				Sleep(5000);
				return;
			}
		}
		else
		{
			cout << "Plugin main function could not be located" << endl;
			FreeLibrary(libhandle);
			Sleep(5000);
			return;
		}
	}
	else
	{
		cout << "Plugin DLL could not be loaded" << endl;
		Sleep(5000);
		return;
	};


	////////////////////////////////////////////////////////////////////////////
	//
	//	Examining the plugin
	//
	////////////////////////////////////////////////////////////////////////////

	if (ptrPlug->dispatcher(ptrPlug,effGetVstVersion,0,0,NULL,0.0f)==2)
	{
		cout << "This is a VST 2 plugin" << endl;
	}
	else
	{
		cout << "This is a VST 1 plugin" << endl;
	}

	//Member variables:
	cout << "numPrograms " << ptrPlug->numPrograms << endl;
	cout << "numParams " << ptrPlug->numParams << endl;
	cout << "numInputs " << ptrPlug->numInputs << endl;
	cout << "numOutputs " << ptrPlug->numOutputs << endl;
	cout << "resvd1 " << ptrPlug->resvd1 << endl;
	cout << "resvd2 " << ptrPlug->resvd2 << endl;
	cout << "initialDelay " << ptrPlug->initialDelay << endl;
	cout << "realQualities " << ptrPlug->realQualities << endl;
	cout << "offQualities " << ptrPlug->offQualities << endl;
	cout << "ioRatio (float)" << ptrPlug->ioRatio << endl;
	cout << "object (ptr)" << ptrPlug->object << endl;
	cout << "user (ptr)" << ptrPlug->user << endl;
	cout << "uniqueID " << ptrPlug->uniqueID << endl;
	cout << "version " << ptrPlug->version << endl;

	//VST 1.0 flags
	if (ptrPlug->flags & effFlagsHasEditor)
	{
		//Meaning: plugin has its own GUI editor
		cout << "plug has the effFlagsHasEditor flag" << endl;
		editor=true;
	}

	if (ptrPlug->flags & effFlagsHasClip)
	{
		//Meaning: plugin can provide information to drive a clipping display
		cout << "plug has the effFlagsHasClip flag" << endl;
	}

	if (ptrPlug->flags & effFlagsHasVu)
	{
		//Meaning: plugin can provide information to drive a VU display
		cout << "plug has the effFlagsHasVu flag" << endl;
	}

	if (ptrPlug->flags & effFlagsCanMono)
	{
		//Meaning: plugin can accept mono input

		//NB - if the plugin has two input buffers they can both be the same
		cout << "plug has the effFlagsCanMono flag" << endl;
	}

	if (ptrPlug->flags & effFlagsCanReplacing)
	{
		//Meaning: plugin supports a replacing process
		//The values in any output buffer given to the plug will be overwritten
		//however, due to glitches in a few VST instruments it's advisable to
		//zero these buffers anyway before handing them over.
		cout << "plug has the effFlagsCanReplacing flag" << endl;
	}

	if (ptrPlug->flags & effFlagsProgramChunks)
	{
		//Meaning: plugin receives program and bank information via chunk method
		cout << "plug has the effFlagsProgramChunks flag" << endl;
	}

	if (ptrPlug->dispatcher(ptrPlug,effGetVstVersion,0,0,NULL,0.0f)==2)
	{
		//VST 2.0 flags
		if (ptrPlug->flags & effFlagsIsSynth)
		{
			//Meaning: plugin will require MIDI events, and processEvents must be
			//called before process or processReplacing. Also, plugin cannot be
			//used as an insert or send effect and will declare its own number of
			//outputs, which should be assigned by the host as mixer channels
			cout << "plug has the effFlagsIsSynth flag" << endl;
		}

		if (ptrPlug->flags & effFlagsNoSoundInStop)
		{
			//Meaning: if the transport is stopped the host need not call process
			//or processReplacing - or if no input available?
			cout << "plug has the effFlagsNoSoundInStop flag" << endl;
		}

		//also effFlagsExtIsAsync and effFlagsExtHasBuffer - refers to plugin that
		//supports external DSP

		//VST 2.0 Plugin Can Do's
		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"sendVstEvents",0.0f)>0)
		{
			cout << "plug can sendVstEvents" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"sendVstMidiEvent",0.0f)>0)
		{
			cout << "plug can sendVstMidiEvent" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"sendVstTimeInfo",0.0f)>0)
		{
			cout << "plug can sendVstTimeInfo" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"receiveVstEvents",0.0f)>0)
		{
			cout << "plug can receiveVstEvents" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"receiveVstMidiEvent",0.0f)>0)
		{
			cout << "plug can receiveVstMidiEvent" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"receiveVstTimeInfo",0.0f)>0)
		{
			cout << "plug can receiveVstTimeInfo" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"offline",0.0f)>0)
		{
			cout << "plug can offline" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"plugAsChannelInsert",0.0f)>0)
		{
			cout << "plug can plugAsChannelInsert" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"plugAsSend",0.0f)>0)
		{
			cout << "plug can plugAsSend" << endl;
		}

		if (ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"mixDryWet",0.0f)>0)
		{
			cout << "plug can mixDryWet" << endl;
		}

	}	//end VST2 specific

	////////////////////////////////////////////////////////////////////////////
	//
	//	Using the plugin's C Interface - an example
	//
	////////////////////////////////////////////////////////////////////////////

	long rc=0;

	//try some dispatcher functions...
	ptrPlug->dispatcher(ptrPlug,effOpen,0,0,NULL,0.0f);

	//switch the plugin off (calls Suspend)
	ptrPlug->dispatcher(ptrPlug,effMainsChanged,0,0,NULL,0.0f);

	//set sample rate and block size
	ptrPlug->dispatcher(ptrPlug,effSetSampleRate,0,0,NULL,samplerate);
	ptrPlug->dispatcher(ptrPlug,effSetBlockSize,0,blocksize,NULL,0.0f);

	if ((ptrPlug->dispatcher(ptrPlug,effGetVstVersion,0,0,NULL,0.0f)==2) &&
		(ptrPlug->flags & effFlagsIsSynth))
	{
		//get I/O configuration for synth plugins - they will declare their
		//own output and input channels
		for (int i=0; i<ptrPlug->numInputs+ptrPlug->numOutputs;i++)
		{
			if (i<ptrPlug->numInputs)
			{
				//input pin
				VstPinProperties temp;

				if (ptrPlug->dispatcher(ptrPlug,effGetInputProperties,i,0,&temp,0.0f)==1)
				{
					cout << "Input pin " << (i+1) << " label " << temp.label << endl;
					//cout << "Input pin " << (i+1) << " shortLabel " << temp.shortLabel << endl;

					if (temp.flags & kVstPinIsActive)
					{
						cout << "Input pin " << (i+1) << " is active" << endl;
					}

					if (temp.flags & kVstPinIsStereo)
					{
						// is index even or zero?
						if (i%2==0 || i==0)
						{
							cout << "Input pin " << (i+1) << " is left channel of a stereo pair" << endl;
						}
						else
						{
							cout << "Input pin " << (i+1) << " is right channel of a stereo pair" << endl;
						}
					}
				}
			}
			else
			{
				//output pin
				VstPinProperties temp;

				if (ptrPlug->dispatcher(ptrPlug,effGetOutputProperties,i,0,&temp,0.0f)==1)
				{
					cout << "Output pin " << (i-ptrPlug->numInputs+1) << " label " << temp.label << endl;
					//cout << "Output pin " << (i-ptrPlug->numInputs+1) << " shortLabel " << temp.shortLabel << endl;

					if (temp.flags & kVstPinIsActive)
					{
						cout << "Output pin " << (i-ptrPlug->numInputs+1) << " is active" << endl;
					}
					else
					{
						cout << "Output pin " << (i-ptrPlug->numInputs+1) << " is inactive" << endl;
					}

					if (temp.flags & kVstPinIsStereo)
					{
						// is index even or zero?
						if ((i-ptrPlug->numInputs)%2==0 || (i-ptrPlug->numInputs)==0)
						{
							cout << "Output pin " << (i+1) << " is left channel of a stereo pair" << endl;
						}
						else
						{
							cout << "Output pin " << (i+1) << " is right channel of a stereo pair" << endl;
						}
					}
					else
					{
						cout <<  "Output pin " << (i+1) << " is mono" << endl;
					}
				}
			}
		}
	}	//end VST2 specific

	//switch the plugin back on (calls Resume)
	ptrPlug->dispatcher(ptrPlug,effMainsChanged,0,1,NULL,0.0f);

	////////////////////////////////////////////////////////////////////////////

	//set to program zero and fetch back the name
	ptrPlug->dispatcher(ptrPlug,effSetProgram,0,0,NULL,0.0f);

	char strProgramName[24+2];
	ptrPlug->dispatcher(ptrPlug,effGetProgramName,0,0,strProgramName,0.0f);

	cout << "Set plug to program zero, name is " <<  strProgramName << endl;

	//get the parameter strings
	char strName[24];
	char strDisplay[24];
	char strLabel[24];

	ptrPlug->dispatcher(ptrPlug,effGetParamName,0,0,strName,0.0f);
	cout << "Parameter name is " << strName << endl;

	ptrPlug->dispatcher(ptrPlug,effGetParamLabel,0,0,strLabel,0.0f);
	cout << "Parameter label is " << strLabel << endl;

	ptrPlug->dispatcher(ptrPlug,effGetParamDisplay,0,0,strDisplay,0.0f);
	cout << "Parameter display is " << strDisplay << endl;

	//call the set and get parameter functions
	ptrPlug->setParameter(ptrPlug,0,0.7071f);
	float newval=ptrPlug->getParameter(ptrPlug,0);

	cout << "Parameter 0 was changed to " << newval << endl;
	ptrPlug->dispatcher(ptrPlug,effGetParamDisplay,0,0,strDisplay,0.0f);
	cout << "Parameter display is now " << strDisplay << endl;

	////////////////////////////////////////////////////////////////////////////

	VstEvent* ptrEvents=NULL;
	char* ptrEventBuffer=NULL;
	int nEvents=4;

	if ((ptrPlug->dispatcher(ptrPlug,effGetVstVersion,0,0,NULL,0.0f)==2) &&
	   ((ptrPlug->flags & effFlagsIsSynth) ||
		(ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"receiveVstEvents",0.0f)>0)))
	{
		//create some MIDI messages to send to the plug if it is a synth or can
		//receive MIDI messages

		//create a block of appropriate size
		int pointersize=sizeof(VstEvent*);
		int bufferSize=sizeof(VstEvents)-(pointersize*2);
		bufferSize+=pointersize*(nEvents);

		//create the buffer
		ptrEventBuffer=new char[bufferSize+1];

		//now, create some memory for the events themselves
		VstMidiEvent* ptrEvents=new VstMidiEvent[nEvents];
		VstMidiEvent* ptrWrite=ptrEvents;

		//Create first event
		ptrWrite->type=kVstMidiType;
		ptrWrite->byteSize=24L;
		ptrWrite->deltaFrames=0L;
		ptrWrite->flags=0L;
		ptrWrite->noteLength=0L;
		ptrWrite->noteOffset=0L;

		ptrWrite->midiData[0]=(char)0x90;	//status & channel
		ptrWrite->midiData[1]=(char)0x3C;	//MIDI byte #2
		ptrWrite->midiData[2]=(char)0xFF;	//MIDI byte #3
		ptrWrite->midiData[3]=(char)0x00;	//MIDI byte #4 - blank

		ptrWrite->detune=0x00;
		ptrWrite->noteOffVelocity=0x00;
		ptrWrite->reserved1=0x00;
		ptrWrite->reserved2=0x00;

		//Create second event
		ptrWrite++;
		ptrWrite->type=kVstMidiType;
		ptrWrite->byteSize=24L;
		ptrWrite->deltaFrames=128L;
		ptrWrite->flags=0L;
		ptrWrite->noteLength=0L;
		ptrWrite->noteOffset=0L;

		ptrWrite->midiData[0]=(char)0x90;	//status & channel
		ptrWrite->midiData[1]=(char)0x3C;	//MIDI byte #2
		ptrWrite->midiData[2]=(char)0x00;	//MIDI byte #3
		ptrWrite->midiData[3]=(char)0x00;	//MIDI byte #4 - blank

		ptrWrite->detune=0x00;
		ptrWrite->noteOffVelocity=0x00;
		ptrWrite->reserved1=0x00;
		ptrWrite->reserved2=0x00;

		//Create third event
		ptrWrite++;
		ptrWrite->type=kVstMidiType;
		ptrWrite->byteSize=24L;
		ptrWrite->deltaFrames=256L;
		ptrWrite->flags=0L;
		ptrWrite->noteLength=0L;
		ptrWrite->noteOffset=0L;

		ptrWrite->midiData[0]=(char)0x90;	//status & channel
		ptrWrite->midiData[1]=(char)0x3C;	//MIDI byte #2
		ptrWrite->midiData[2]=(char)0xFF;	//MIDI byte #3
		ptrWrite->midiData[3]=(char)0x00;	//MIDI byte #4 - blank

		ptrWrite->detune=0x00;
		ptrWrite->noteOffVelocity=0x00;
		ptrWrite->reserved1=0x00;
		ptrWrite->reserved2=0x00;

		//Create fourth event
		ptrWrite++;
		ptrWrite->type=kVstMidiType;
		ptrWrite->byteSize=24L;
		ptrWrite->deltaFrames=384L;
		ptrWrite->flags=0L;
		ptrWrite->noteLength=0L;
		ptrWrite->noteOffset=0L;

		ptrWrite->midiData[0]=(char)0x90;	//status & channel
		ptrWrite->midiData[1]=(char)0x3C;	//MIDI byte #2
		ptrWrite->midiData[2]=(char)0x00;	//MIDI byte #3
		ptrWrite->midiData[3]=(char)0x00;	//MIDI byte #4 - blank

		ptrWrite->detune=0x00;
		ptrWrite->noteOffVelocity=0x00;
		ptrWrite->reserved1=0x00;
		ptrWrite->reserved2=0x00;

		//copy the addresses of our events into the eventlist structure
		VstEvents* ev=(VstEvents*)ptrEventBuffer;
		for (int i=0;i<nEvents;i++)
		{
			ev->events[i]=(VstEvent*)(ptrEvents+i);
		}

		//do the block header
		ev->numEvents=nEvents;
		ev->reserved=0L;

	}

	////////////////////////////////////////////////////////////////////////////
	float** ptrInputBuffers=NULL;
	float** ptrOutputBuffers=NULL;

	if (ptrPlug->numInputs)
	{

		if (ptrPlug->flags & effFlagsCanMono)
		{
			//only one input buffer required, others are just copies?
			//This could be an incorrect interpretation

			ptrInputBuffers=new float*[ptrPlug->numInputs];

			//create the input buffers
			for (int i=0;i<ptrPlug->numInputs;i++)
			{
				if (i==0)
				{
					cout << "Input buffer " << (i+1) << " created" << endl;
					ptrInputBuffers[i]=new float[blocksize];
				}
				else
				{
					cout << "Input buffer " << (i+1) << " is a copy of input buffer 1" << endl;
					ptrInputBuffers[i]=ptrInputBuffers[0];
				}
			}
		}
		else
		{
			//Plug requires independent input signals
			ptrInputBuffers=new float*[ptrPlug->numInputs];

			//create the input buffers
			for (int i=0;i<ptrPlug->numInputs;i++)
			{
				cout << "Input buffer " << (i+1) << " created" << endl;
				ptrInputBuffers[i]=new float[blocksize];
			}
		}
	}

	if (ptrPlug->numOutputs)
	{
		ptrOutputBuffers=new float*[ptrPlug->numOutputs];

		//create the output buffers
		for (int i=0;i<ptrPlug->numOutputs;i++)
		{
			cout << "Output buffer " << (i+1) << " created" << endl;
			ptrOutputBuffers[i]=new float[blocksize];
		}
	}

	if (ptrPlug->numInputs>0)
	{
		//fill the input buffers with ones to simulate some input having been
		//received

		//We don't have to do this for synths, obviously
		for (int i=0;i<ptrPlug->numInputs;i++)
		{
			for (int j=0;j<blocksize;j++)
			{
				*(ptrInputBuffers[i]+j)=1.0f;
			}
		}
	}

	if (ptrPlug->numOutputs>0)
	{
		//fill the output buffers with ones to simulate some prior output to be
		//accumulated on
		for (int i=0;i<ptrPlug->numOutputs;i++)
		{
			for (int j=0;j<blocksize;j++)
			{
				//*(ptrOutputBuffers[i]+j)=-1.0f;

				//note that VSTXsynth, when replacing, requires the host
				//to clear the buffer first!
				*(ptrOutputBuffers[i]+j)=0.0f;
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////

	ofstream outfile;
	outfile.open("hello.txt");

	//ProcessEvents
	if ((ptrPlug->dispatcher(ptrPlug,effGetVstVersion,0,0,NULL,0.0f)==2) &&
	   ((ptrPlug->flags & effFlagsIsSynth) ||
		(ptrPlug->dispatcher(ptrPlug,effCanDo,0,0,"receiveVstEvents",0.0f)>0)))
	{
		if (ptrPlug->dispatcher(ptrPlug,effProcessEvents,0,0,(VstEvents*)ptrEventBuffer,0.0f)==1)
		{
			cout << "plug processed events OK and wants more" << endl;
		}
		else
		{
			cout << "plug does not want any more events" << endl;
		}
	}

	//process (replacing)
	if (ptrPlug->flags & effFlagsCanReplacing)
	{
		cout << "Process (replacing)" << endl;
		ptrPlug->processReplacing(ptrPlug,ptrInputBuffers,ptrOutputBuffers,blocksize);
	}
/*
	//process (accumulating)
	cout << "Process (accumulating)" << endl;
	ptrPlug->process(ptrPlug,ptrInputBuffers,ptrOutputBuffers,sampleframes);
*/

	//Dump output to disk
	for (int i=0;i<ptrPlug->numOutputs;i++)
	{
		for (int j=0;j<blocksize;j++)
		{
			outfile << i << "," << j << "," << *(ptrOutputBuffers[i]+j) << endl;
		}
	}

	outfile.close();


	
	////////////////////////////////////////////////////////////////////////////

	ptrPlug->dispatcher(ptrPlug,effMainsChanged,0,0,NULL,0.0f);	//calls suspend

	////////////////////////////////////////////////////////////////////////////
	
	//delete the MIDI data
	if (ptrEventBuffer!=NULL)
	{
		delete[] ptrEventBuffer;
	}

	if (ptrEvents!=NULL)
	{
		delete[] ptrEvents;
	}

	//delete the input buffers
	if (ptrPlug->numInputs>0)
	{
		if (!(ptrPlug->flags & effFlagsCanMono))
		{
			//independent buffers have been assigned, delete them
			for (i=0;i<ptrPlug->numInputs;i++)
			{
				delete[] ptrInputBuffers[i];
			}
		}
		else
		{
			//there's only one buffer, delete it
			delete[] ptrInputBuffers[0];
		}

		//remove the pointers to the buffers
		delete[] ptrInputBuffers;
	}


	//delete the output buffers
	if (ptrPlug->numOutputs>0)
	{
		for (i=0;i<ptrPlug->numOutputs;i++)
		{
			delete[] ptrOutputBuffers[i];
		}

		//remove the pointers to the buffers
		delete[] ptrOutputBuffers;
	}


	//Shut the plugin down and free the library (this deletes the C++ class
	//memory and calls the appropriate destructors...)
	ptrPlug->dispatcher(ptrPlug,effClose,0,0,NULL,0.0f);
	FreeLibrary(libhandle);

	////////////////////////////////////////////////////////////////////////////

	cout << "Done!" << endl;

	Sleep(10000);
}



//host callback function
//this is called directly by the plug-in!!
//
long VSTCALLBACK host(AEffect *effect, long opcode, long index, long value, void *ptr, float opt)
{
	long retval=0;

	switch (opcode)
	{
		//VST 1.0 opcodes
		case audioMasterVersion:
			//Input values:
			//none

			//Return Value:
			//0 or 1 for old version
			//2 or higher for VST2.0 host?
			cout << "plug called audioMasterVersion" << endl;
			retval=2;
			break;

		case audioMasterAutomate:
			//Input values:
			//<index> parameter that has changed
			//<opt> new value

			//Return value:
			//not tested, always return 0

			//NB - this is called when the plug calls
			//setParameterAutomated

			cout << "plug called audioMasterAutomate" << endl;
			break;

		case audioMasterCurrentId:
			//Input values:
			//none

			//Return Value
			//the unique id of a plug that's currently loading
			//zero is a default value and can be safely returned if not known
			cout << "plug called audioMasterCurrentId" << endl;
			break;

		case audioMasterIdle:
			//Input values:
			//none

			//Return Value
			//not tested, always return 0

			//NB - idle routine should also call effEditIdle for all open editors
			Sleep(1);
			cout << "plug called audioMasterIdle" << endl;
			break;

		case audioMasterPinConnected:
			//Input values:
			//<index> pin to be checked
			//<value> 0=input pin, non-zero value=output pin

			//Return values:
			//0=true, non-zero=false
			cout << "plug called audioMasterPinConnected" << endl;
			break;

		//VST 2.0 opcodes
		case audioMasterWantMidi:
			//Input Values:
			//<value> filter flags (which is currently ignored, no defined flags?)

			//Return Value:
			//not tested, always return 0
			cout << "plug called audioMasterWantMidi" << endl;
			break;

		case audioMasterGetTime:
			//Input Values:
			//<value> should contain a mask indicating which fields are required
			//...from the following list?
			//kVstNanosValid
			//kVstPpqPosValid
			//kVstTempoValid
			//kVstBarsValid
			//kVstCyclePosValid
			//kVstTimeSigValid
			//kVstSmpteValid
			//kVstClockValid

			//Return Value:
			//ptr to populated const VstTimeInfo structure (or 0 if not supported)

			//NB - this structure will have to be held in memory for long enough
			//for the plug to safely make use of it
			cout << "plug called audioMasterGetTime" << endl;
			break;

		case audioMasterProcessEvents:
			//Input Values:
			//<ptr> Pointer to a populated VstEvents structure

			//Return value:
			//0 if error
			//1 if OK
			cout << "plug called audioMasterProcessEvents" << endl;
			break;

		case audioMasterSetTime:
			//IGNORE!
			break;

		case audioMasterTempoAt:
			//Input Values:
			//<value> sample frame location to be checked

			//Return Value:
			//tempo (in bpm * 10000)
			cout << "plug called audioMasterTempoAt" << endl;
			break;

		case audioMasterGetNumAutomatableParameters:
			//Input Values:
			//None

			//Return Value:
			//number of automatable parameters
			//zero is a default value and can be safely returned if not known

			//NB - what exactly does this mean? can the host set a limit to the
			//number of parameters that can be automated?
			cout << "plug called audioMasterGetNumAutomatableParameters" << endl;
			break;

		case audioMasterGetParameterQuantization:
			//Input Values:
			//None

			//Return Value:
			//integer value for +1.0 representation,
			//or 1 if full single float precision is maintained
			//in automation.

			//NB - ***possibly bugged***
			//Steinberg notes say "parameter index in <value> (-1: all, any)"
			//but in aeffectx.cpp no parameters are taken or passed
			cout << "plug called audioMasterGetParameterQuantization" << endl;
			break;

		case audioMasterIOChanged:
			//Input Values:
			//None

			//Return Value:
			//0 if error
			//non-zero value if OK
			cout << "plug called audioMasterIOChanged" << endl;
			break;

		case audioMasterNeedIdle:
			//Input Values:
			//None

			//Return Value:
			//0 if error
			//non-zero value if OK

			//NB plug needs idle calls (outside its editor window)
			//this means that effIdle must be dispatched to the plug
			//during host idle process and not effEditIdle calls only when its
			//editor is open
			//Check despatcher notes for any return codes from effIdle
			cout << "plug called audioMasterNeedIdle" << endl;
			break;

		case audioMasterSizeWindow:
			//Input Values:
			//<index> width
			//<value> height

			//Return Value:
			//0 if error
			//non-zero value if OK
			cout << "plug called audioMasterSizeWindow" << endl;
			break;

		case audioMasterGetSampleRate:
			//Input Values:
			//None

			//Return Value:
			//not tested, always return 0

			//NB - Host must despatch effSetSampleRate to the plug in response
			//to this call
			//Check despatcher notes for any return codes from effSetSampleRate
			cout << "plug called audioMasterGetSampleRate" << endl;
			effect->dispatcher(effect,effSetSampleRate,0,0,NULL,samplerate);
			break;

		case audioMasterGetBlockSize:
			//Input Values:
			//None

			//Return Value:
			//not tested, always return 0

			//NB - Host must despatch effSetBlockSize to the plug in response
			//to this call
			//Check despatcher notes for any return codes from effSetBlockSize
			cout << "plug called audioMasterGetBlockSize" << endl;
			effect->dispatcher(effect,effSetBlockSize,0,blocksize,NULL,0.0f);

			break;

		case audioMasterGetInputLatency:
			//Input Values:
			//None

			//Return Value:
			//input latency (in sampleframes?)
			cout << "plug called audioMasterGetInputLatency" << endl;
			break;

		case audioMasterGetOutputLatency:
			//Input Values:
			//None

			//Return Value:
			//output latency (in sampleframes?)
			cout << "plug called audioMasterGetOutputLatency" << endl;
			break;

		case audioMasterGetPreviousPlug:
			//Input Values:
			//None

			//Return Value:
			//pointer to AEffect structure or NULL if not known?

			//NB - ***possibly bugged***
			//Steinberg notes say "input pin in <value> (-1: first to come)"
			//but in aeffectx.cpp no parameters are taken or passed
			cout << "plug called audioMasterGetPreviousPlug" << endl;
			break;

		case audioMasterGetNextPlug:
			//Input Values:
			//None

			//Return Value:
			//pointer to AEffect structure or NULL if not known?

			//NB - ***possibly bugged***
			//Steinberg notes say "output pin in <value> (-1: first to come)"
			//but in aeffectx.cpp no parameters are taken or passed
			cout << "plug called audioMasterGetNextPlug" << endl;
			break;

		case audioMasterWillReplaceOrAccumulate:
			//Input Values:
			//None

			//Return Value:
			//0: not supported
			//1: replace
			//2: accumulate
			cout << "plug called audioMasterWillReplaceOrAccumulate" << endl;
			break;

		case audioMasterGetCurrentProcessLevel:
			//Input Values:
			//None

			//Return Value:
			//0: not supported,
			//1: currently in user thread (gui)
			//2: currently in audio thread (where process is called)
			//3: currently in 'sequencer' thread (midi, timer etc)
			//4: currently offline processing and thus in user thread
			//other: not defined, but probably pre-empting user thread.
			cout << "plug called audioMasterGetCurrentProcessLevel" << endl;
			break;

		case audioMasterGetAutomationState:
			//Input Values:
			//None

			//Return Value:
			//0: not supported
			//1: off
			//2:read
			//3:write
			//4:read/write
			cout << "plug called audioMasterGetAutomationState" << endl;
			break;

		case audioMasterGetVendorString:
			//Input Values:
			//<ptr> string (max 64 chars) to be populated

			//Return Value:
			//0 if error
			//non-zero value if OK
			cout << "plug called audioMasterGetVendorString" << endl;
			break;

		case audioMasterGetProductString:
			//Input Values:
			//<ptr> string (max 64 chars) to be populated

			//Return Value:
			//0 if error
			//non-zero value if OK
			cout << "plug called audioMasterGetProductString" << endl;
			break;

		case audioMasterGetVendorVersion:
			//Input Values:
			//None

			//Return Value:
			//Vendor specific host version as integer
			cout << "plug called audioMasterGetVendorVersion" << endl;
			break;

		case audioMasterVendorSpecific:
			//Input Values:
			//<index> lArg1
			//<value> lArg2
			//<ptr> ptrArg
			//<opt>	floatArg

			//Return Values:
			//Vendor specific response as integer
			cout << "plug called audioMasterVendorSpecific" << endl;
			break;

		case audioMasterSetIcon:
			//IGNORE
			break;

		case audioMasterCanDo:
			//Input Values:
			//<ptr> predefined "canDo" string

			//Return Value:
			//0 = Not Supported
			//non-zero value if host supports that feature

			//NB - Possible Can Do strings are:
			//"sendVstEvents",
			//"sendVstMidiEvent",
			//"sendVstTimeInfo",
			//"receiveVstEvents",
			//"receiveVstMidiEvent",
			//"receiveVstTimeInfo",
			//"reportConnectionChanges",
			//"acceptIOChanges",
			//"sizeWindow",
			//"asyncProcessing",
			//"offline",
			//"supplyIdle",
			//"supportShell"
			cout << "plug called audioMasterCanDo" << endl;

			if (strcmp((char*)ptr,"sendVstEvents")==0 ||
				strcmp((char*)ptr,"sendVstMidiEvent")==0 ||
				strcmp((char*)ptr,"supplyIdle")==0)
			{
				retval=1;
			}
			else
			{
				retval=0;
			}

			break;

		case audioMasterGetLanguage:
			//Input Values:
			//None

			//Return Value:
			//kVstLangEnglish
			//kVstLangGerman
			//kVstLangFrench
			//kVstLangItalian
			//kVstLangSpanish
			//kVstLangJapanese
			cout << "plug called audioMasterGetLanguage" << endl;
			retval=1;
			break;
/*
		MAC SPECIFIC?

		case audioMasterOpenWindow:
			//Input Values:
			//<ptr> pointer to a VstWindow structure

			//Return Value:
			//0 if error
			//else platform specific ptr
			cout << "plug called audioMasterOpenWindow" << endl;
			break;

		case audioMasterCloseWindow:
			//Input Values:
			//<ptr> pointer to a VstWindow structure

			//Return Value:
			//0 if error
			//Non-zero value if OK
			cout << "plug called audioMasterCloseWindow" << endl;
			break;
*/
		case audioMasterGetDirectory:
			//Input Values:
			//None

			//Return Value:
			//0 if error
			//FSSpec on MAC, else char* as integer

			//NB Refers to which directory, exactly?
			cout << "plug called audioMasterGetDirectory" << endl;
			break;

		case audioMasterUpdateDisplay:
			//Input Values:
			//None

			//Return Value:
			//Unknown
			cout << "plug called audioMasterUpdateDisplay" << endl;
			break;
	}

	return retval;
};


I began to do this :

Code: Select all

Structure AEffect
  magic.l;         must be kEffectMagic ('VstP')
  dispatcher.l;    long (VSTCALLBACK * dispatcher) (AEffect * effect, long opCode,long index, long value, void *ptr,float opt)
  process.l;       void (VSTCALLBACK * process) (AEffect * effect, float **inputs,float **outputs, long sampleframes);
  setParameter.l;  void (VSTCALLBACK * setParameter) (AEffect * effect, long index, float parameter);
  getParameter.l;  float (VSTCALLBACK * getParameter) (AEffect * effect, long index);
  numPrograms.l
  numParams.l;              // all programs are assumed to have numParams parameters
  numInputs.l;              //
  numOutputs.l;             //
  flags.l;                  // see constants
  resvd1.l;                 // reserved, must be 0
  resvd2.l;                 // reserved, must be 0
  initialDelay.l;           // for algorithms which need input in the first place
  realQualities.l;          // number of realtime qualities (0: realtime)
  offQualities.l;           // number of offline qualities (0: realtime only)
  ioRatio.f;                // input samplerate to output samplerate ratio, not used yet
  *object.l;                // for class access (see AudioEffect.hpp), MUST be 0 else!
  *user.l;                  // user access
  uniqueID.l;               // pls choose 4 character as unique as possible.
                           ;// this is used To identify an effect For save+load
  version.l;                //
  processReplacing.l;    void (VSTCALLBACK * processReplacing) (AEffect * effect, float **inputs,
  *outputs.f
  sampleframes.l
  future.b[60] ;             // pls zero
EndStructure
;
Procedure host(*effect.AEffect, opcode.l, index.l, value.l, *ptr.l, opt.f)
  Debug "Opcode : "+Str(opcode)
  ProcedureReturn 0
EndProcedure



LibName$ = OpenFileRequester("Choose a VST (.dll file)", "", ".DLL|*.dll",0)
If LibName$
  If OpenLibrary(0, LibName$)
    If ExamineLibraryFunctions(0)
      Result = IsFunction(0, "main")
      If Result
        *ptrPlug.AEffect = CallFunctionFast(Result,@host,0)
      Else
        MessageRequester("Error","This library is not a VST",0)
      EndIf
    Else
      MessageRequester("Error","Can't examine this library.",0)
    EndIf
    CloseLibrary(0)
  Else
    MessageRequester("Error","Can't open this library.",0)
  EndIf
EndIf
but it crashes when trying to open a VST DLL. I just need to understand why it crashes. If anybody can help me, I thinck that I can go alone over the rest of the job.

Re: Need Help to Implement VST in PureBasic

Posted: Thu Oct 28, 2004 9:07 am
by traumatic
what's your question?

Posted: Thu Oct 28, 2004 10:07 am
by zapman*
oops! :oops: the end of the message was missing. I corrected it.

Posted: Sun Oct 31, 2004 12:18 am
by KarLKoX

Code: Select all

Structure AEffect
  magic.l;         must be kEffectMagic ('VstP')
  dispatcher.l;    long (VSTCALLBACK * dispatcher) (AEffect * effect, long opCode,long index, long value, void *ptr,float opt)
  process.l;       void (VSTCALLBACK * process) (AEffect * effect, float **inputs,float **outputs, long sampleframes);
  setParameter.l;  void (VSTCALLBACK * setParameter) (AEffect * effect, long index, float parameter);
  getParameter.l;  float (VSTCALLBACK * getParameter) (AEffect * effect, long index);
  numPrograms.l
  numParams.l;              // all programs are assumed to have numParams parameters
  numInputs.l;              //
  numOutputs.l;             //
  flags.l;                  // see constants
  resvd1.l;                 // reserved, must be 0
  resvd2.l;                 // reserved, must be 0
  initialDelay.l;           // for algorithms which need input in the first place
  realQualities.l;          // number of realtime qualities (0: realtime)
  offQualities.l;           // number of offline qualities (0: realtime only)
  ioRatio.f;                // input samplerate to output samplerate ratio, not used yet
  *object.l;                // for class access (see AudioEffect.hpp), MUST be 0 else!
  *user.l;                  // user access
  uniqueID.l;               // pls choose 4 character as unique as possible.
  ;// this is used To identify an effect For save+load
  version.l;                //
  processReplacing.l;    void (VSTCALLBACK * processReplacing) (AEffect * effect, float **inputs,
  ;*outputs.f               // en trop : fait partis des paramèttres de la fonction callback
  ;sampleframes.l           // en trop : fait partis des paramèttres de la fonction callback
  future.b[60] ;            // pls zero
EndStructure
;
ProcedureCDLL host(*effect.AEffect, opcode.l, index.l, Value.l, *ptr.l, opt.f)
  Debug "Opcode : "+Str(opcode)
  ProcedureReturn 0
EndProcedure



LibName$ = OpenFileRequester("Choose a VST (.dll file)", "", ".DLL|*.dll",0)
If LibName$
  If OpenLibrary(0, LibName$)
    If ExamineLibraryFunctions(0)
      *ptrFunc = IsFunction(0, "main")
      If *ptrFunc
        *ptrPlug.AEffect = CallFunctionFast(*ptrFunc, @host())  ; oublie des parenthèses ^_^
      Else
        MessageRequester("Error","This library is not a VST",0)
      EndIf
    Else
      MessageRequester("Error","Can't examine this library.",0)
    EndIf
    CloseLibrary(0)
  Else
    MessageRequester("Error","Can't open this library.",0)
  EndIf
EndIf

Posted: Sun Oct 31, 2004 3:17 am
by KarLKoX

Posted: Sun Oct 31, 2004 9:48 am
by traumatic
OT: Sorry, just wondering, are you the same KarLKoX as the one on the skale tracker forum?

Posted: Sun Oct 31, 2004 3:47 pm
by KarLKoX
Yes i am :wink:

Posted: Sun Oct 31, 2004 11:43 pm
by zapman*
Thanks a lot, KarLKox!!!!!! My problem come from a very stupid mistake!

I did examine your rar and I've seen that you've done a big part of the job. Thanks again!

Posted: Sun Oct 31, 2004 11:46 pm
by KarLKoX
No prob :)
The archive is often updated so if you want to see the progress, don't forget to download it from time to time :)

Posted: Fri Nov 05, 2004 12:00 am
by eriansa
If you need any help just contact me.

I've made a VST-Host with PB :
(and it took me 1 month to make it stable) :oops:

http://www.sq4.be

Posted: Wed Dec 29, 2004 12:11 am
by soerenkj
good for you eriansa!
I once tried to make a vst host myself, but failed completely, and wasted a lot of time..
I would be very interested to see how you did that..
could you post code for a minimal vst host, or is it too much code..?!

Posted: Sat Feb 04, 2006 2:12 am
by sonicfire
soerenkj wrote:good for you eriansa!
I once tried to make a vst host myself, but failed completely, and wasted a lot of time..
I would be very interested to see how you did that..
could you post code for a minimal vst host, or is it too much code..?!
would also love to know that ... the topic is a bit old, but who cares :lol: :wink:

Posted: Sat Feb 04, 2006 10:41 am
by thefool
eriansa wrote:If you need any help just contact me.

I've made a VST-Host with PB :
(and it took me 1 month to make it stable) :oops:

http://www.sq4.be
Very nice! Im waiting for the release :) Btw do you support gear like midi keyboards or the U
UC-33?

Posted: Sat Feb 04, 2006 1:28 pm
by eriansa
the successor of SQ4² is almost finished AND this time it's stable as a rock! (PB 3.94).

The VST Host now supports 16 VSTI's/VSTe's+Rewire.
It has real time midiIn (+support for MidiControllers (I use UC33))

The mixer and audio engine are mostly written in fasm and are SSE optimized.
(did some comparisons with Tracktion/Acid/FLStudio/Energy/Orion (I own them all -Great Coders!/Fantastic software) and, to be honest, I can go as low as 48samples latency! (Asio))

I've planned to release a beta within a couple of weeks. I am especially looking for people owning :

1/ PC's with AMD processors.
2/ Asio cards/drivers that support INT32MSB/INT32LSB/Int32Float/96Khz etc...

But today PB4.0 is available : damn! :wink:
(35.000lines of code need to be re-checked)
So the release date will be postponed a little... :roll:

Anyway, PB+Fasm can beat C/C++/Delphi, and I really hope that other audio-developers start looking for this great language (I do not need OOP).

Posted: Sun Feb 05, 2006 4:57 pm
by coma
is it possible to create VST/VSTi in pb ?