/* vst~ - VST plugin object for PD based on the work of Jarno Seppänen and Mark Williamson Copyright (c)2003-2004 Thomas Grill (xovo@gmx.net) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. */ #include "Editor.h" #include "VstHost.h" #include "vst\aeffeditor.h" #include "vst\aeffectx.h" using namespace std; VstTimeInfo VSTPlugin::_timeInfo; float VSTPlugin::sample_rate = 44100; //////////////////// // ///////////////////// VSTPlugin::VSTPlugin(): posx(0),posy(0), hwnd(NULL), _editor(false) { queue_size=0; _sDllName = NULL; h_dll=NULL; instantiated=false; // Constructin' with no instance overwrite = false; // wantidle = false; // show_params = false; _midichannel = 0; } VSTPlugin::~VSTPlugin() { Free(); // Call free delete _sDllName; // if _sDllName = NULL , the operation does nothing -> it's safe. } int VSTPlugin::Instance( const char *dllname) { h_dll = LoadLibrary(dllname); if(!h_dll) { return VSTINSTANCE_ERR_NO_VALID_FILE; } // post("Loaded library %s" , dllname); PVSTMAIN main = (PVSTMAIN)GetProcAddress(h_dll,"main"); if(!main) { FreeLibrary(h_dll); _pEffect=NULL; instantiated=false; return VSTINSTANCE_ERR_NO_VST_PLUGIN; } //post("Found main function - about to call it"); //This calls the "main" function and receives the pointer to the AEffect structure. _pEffect = main((audioMasterCallback)&(this->Master)); if(!_pEffect) { post("VST plugin : unable to create effect"); FreeLibrary(h_dll); _pEffect=NULL; instantiated=false; return VSTINSTANCE_ERR_REJECTED; } if( _pEffect->magic!=kEffectMagic) { post("VST plugin : Instance query rejected by 0x%.8X\n",(int)_pEffect); FreeLibrary(h_dll); _pEffect=NULL; instantiated=false; return VSTINSTANCE_ERR_REJECTED; } //post("VST plugin : Instanced at (Effect*): %.8X\n",(int)_pEffect); //init plugin _pEffect->user = this; Dispatch( effOpen , 0, 0, NULL, 0.0f); Dispatch( effSetProgram , 0, 0, NULL, 0.0f); // Dispatch( effMainsChanged, 0, 1, NULL, 0.0f); //************************************set samplerate and stream size here // we get it when we init our DSP // Dispatch( effSetSampleRate, 0, 0, NULL, (float)Global::pConfig->_pOutputDriver->_samplesPerSec); // Dispatch( effSetBlockSize, 0, STREAM_SIZE, NULL, 0.0f); if (!Dispatch( effGetProductString, 0, 0, &_sProductName, 0.0f)) { string str1(dllname); string str2 = str1.substr(str1.rfind('\\')+1); int snip = str2.find('.'); if( snip != string::npos ) str1 = str2.substr(0,snip); else str1 = str2; strcpy(_sProductName,str1.c_str()); } if (!_pEffect->dispatcher(_pEffect, effGetVendorString, 0, 0, &_sVendorName, 0.0f)) { strcpy(_sVendorName, "Unknown vendor"); } _version = _pEffect->version; _isSynth = (_pEffect->flags & effFlagsIsSynth)?true:false; overwrite = (_pEffect->flags & effFlagsCanReplacing)?true:false; _editor = (_pEffect->flags & effFlagsHasEditor)?true:false; if ( _sDllName != NULL ) delete _sDllName; _sDllName = new char[strlen(dllname)+1]; sprintf(_sDllName,dllname); //keep plugin name instantiated=true; return VSTINSTANCE_NO_ERROR; } int VSTPlugin::getNumInputs( void ) { return _pEffect->numInputs; } int VSTPlugin::getNumOutputs( void ) { return _pEffect->numOutputs; } void VSTPlugin::Create(VSTPlugin *plug) { h_dll=plug->h_dll; _pEffect=plug->_pEffect; _pEffect->user=this; Dispatch( effMainsChanged, 0, 1, NULL, 0.0f); // strcpy(_editName,plug->_editName); On current implementation, this replaces the right one. strcpy(_sProductName,plug->_sProductName); strcpy(_sVendorName,plug->_sVendorName); _sDllName = new char[strlen(plug->_sDllName)+1]; strcpy(_sDllName,plug->_sDllName); _isSynth=plug->_isSynth; _version=plug->_version; plug->instantiated=false; // We are "stoling" the plugin from the "plug" object so this // is just a "trick" so that when destructing the "plug", it // doesn't unload the Dll. instantiated=true; } void VSTPlugin::Free() // Called also in destruction { if(IsEdited()) StopEditor(this); if(instantiated) { instantiated=false; post("VST plugin : Free query 0x%.8X\n",(int)_pEffect); _pEffect->user = NULL; Dispatch( effMainsChanged, 0, 0, NULL, 0.0f); Dispatch( effClose, 0, 0, NULL, 0.0f); // delete _pEffect; // <- Should check for the necessity of this command. _pEffect=NULL; FreeLibrary(h_dll); } } void VSTPlugin::Init( float samplerate , float blocksize ) { sample_rate = samplerate; Dispatch(effOpen , 0, 0, NULL, 0.f); Dispatch(effMainsChanged, 0, 1, NULL, 0.f); Dispatch(effSetSampleRate, 0, 0, 0, (float) sample_rate ); Dispatch(effSetBlockSize, 0, blocksize, NULL, 0.f ); } bool VSTPlugin::DescribeValue(int p,char* psTxt) { int parameter = p; if(instantiated) { if(parameter<_pEffect->numParams) { // char par_name[64]; char par_display[64]; char par_label[64]; // Dispatch(effGetParamName,parameter,0,par_name,0.0f); Dispatch(effGetParamDisplay,parameter,0,par_display,0.0f); Dispatch(effGetParamLabel,parameter,0,par_label,0.0f); // sprintf(psTxt,"%s:%s%s",par_name,par_display,par_label); sprintf(psTxt,"%s%s",par_display,par_label); return true; } else sprintf(psTxt,"NumParams Exeeded"); } else sprintf(psTxt,"Not loaded"); return false; } bool VSTPlugin::SetParameter(int parameter, float value) { if(instantiated) { if (( parameter >= 0 ) && (parameter<=_pEffect->numParams)) { _pEffect->setParameter(_pEffect,parameter,value); return true; } } return false; } bool VSTPlugin::SetParameter(int parameter, int value) { return SetParameter(parameter,value/65535.0f); } int VSTPlugin::GetCurrentProgram() { if(instantiated) return Dispatch(effGetProgram,0,0,NULL,0.0f); else return 0; } void VSTPlugin::SetCurrentProgram(int prg) { if(instantiated) Dispatch(effSetProgram,0,prg,NULL,0.0f); } bool VSTPlugin::AddMIDI(unsigned char data0,unsigned char data1,unsigned char data2) { if (instantiated) { VstMidiEvent* pevent=&midievent[queue_size]; pevent->type = kVstMidiType; pevent->byteSize = 24; pevent->deltaFrames = 0; pevent->flags = 0; pevent->detune = 0; pevent->noteLength = 0; pevent->noteOffset = 0; pevent->reserved1 = 0; pevent->reserved2 = 0; pevent->noteOffVelocity = 0; pevent->midiData[0] = data0; pevent->midiData[1] = data1; pevent->midiData[2] = data2; pevent->midiData[3] = 0; if ( queue_size < MAX_EVENTS ) queue_size++; SendMidi(); return true; } else return false; } void VSTPlugin::SendMidi() { if(/*instantiated &&*/ queue_size>0) { // Prepare MIDI events and free queue dispatching all events events.numEvents = queue_size; events.reserved = 0; for(int q=0;qprocessReplacing( _pEffect , inputs , outputs , sampleframes ); } void VSTPlugin::process( float **inputs, float **outputs, long sampleframes ) { _pEffect->process( _pEffect , inputs , outputs , sampleframes ); } // Host callback dispatcher long VSTPlugin::Master(AEffect *effect, long opcode, long index, long value, void *ptr, float opt) { #if 0 if(!effect) { FLEXT_LOG("effect = NULL"); return 0; } #endif #ifdef FLEXT_DEBUG if(opcode != audioMasterGetTime) post("VST plugin call to host dispatcher: Eff: 0x%.8X, Opcode = %d, Index = %d, Value = %d, PTR = %.8X, OPT = %.3f\n",(int)effect, opcode,index,value,(int)ptr,opt); //st( "audioMasterWantMidi %d " , audioMasterWantMidi); #endif // Support opcodes switch(opcode) { case audioMasterAutomate: return 0; // index, value, returns 0 case audioMasterVersion: return 2; // vst version, currently 7 (0 for older) case audioMasterCurrentId: return 'AASH'; // returns the unique id of a plug that's currently loading case audioMasterIdle: effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); return 0; // call application idle routine (this will call effEditIdle for all open editors too) case audioMasterPinConnected: if (value == 0) //input { if ( index < 2) return 0; else return 1; } else //output { if ( index < 2) return 0; else return 1; } return 0; // inquire if an input or output is beeing connected; /* case audioMasterWantMidi: return 0; case audioMasterProcessEvents: return 0; // Support of vst events to host is not available */ case audioMasterGetTime: memset(&_timeInfo, 0, sizeof(_timeInfo)); _timeInfo.samplePos = 0; _timeInfo.sampleRate = sample_rate; return (long)&_timeInfo; case audioMasterTempoAt: return 0; case audioMasterNeedIdle: // effect->dispatcher(effect, effIdle, 0, 0, NULL, 0.0f); return 1; case audioMasterGetSampleRate: return sample_rate; case audioMasterGetVendorString: // Just fooling version string strcpy((char*)ptr,"Steinberg"); return 0; case audioMasterGetVendorVersion: return 5000; // HOST version 5000 case audioMasterGetProductString: // Just fooling product string strcpy((char*)ptr,"Cubase 5.0"); return 0; case audioMasterVendorSpecific: { return 0; } case audioMasterGetLanguage: return kVstLangEnglish; case audioMasterUpdateDisplay: #ifdef FLEXT_DEBUG post("audioMasterUpdateDisplay"); #endif effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); return 0; case audioMasterCanDo: if (!strcmp((char*)ptr,"sendVstEvents")) return 1; if (!strcmp((char*)ptr,"sendVstMidiEvent")) return 1; if (!strcmp((char*)ptr,"sendVstTimeInfo")) return 1; // "receiveVstEvents", // "receiveVstMidiEvent", // "receiveVstTimeInfo", // "reportConnectionChanges", // "acceptIOChanges", if (!strcmp((char*)ptr,"sizeWindow")) return 1; if (!strcmp((char*)ptr,"supplyIdle")) return 1; return -1; case audioMasterSetTime: FLEXT_LOG("VST master dispatcher: Set Time");break; case audioMasterGetNumAutomatableParameters: FLEXT_LOG("VST master dispatcher: GetNumAutPar");break; case audioMasterGetParameterQuantization: FLEXT_LOG("VST master dispatcher: ParamQuant");break; case audioMasterIOChanged: FLEXT_LOG("VST master dispatcher: IOchanged");break; case audioMasterSizeWindow: FLEXT_LOG("VST master dispatcher: Size Window");break; case audioMasterGetBlockSize: FLEXT_LOG("VST master dispatcher: GetBlockSize");break; case audioMasterGetInputLatency: FLEXT_LOG("VST master dispatcher: GetInLatency");break; case audioMasterGetOutputLatency: FLEXT_LOG("VST master dispatcher: GetOutLatency");break; case audioMasterGetPreviousPlug: FLEXT_LOG("VST master dispatcher: PrevPlug");break; case audioMasterGetNextPlug: FLEXT_LOG("VST master dispatcher: NextPlug");break; case audioMasterWillReplaceOrAccumulate: FLEXT_LOG("VST master dispatcher: WillReplace"); break; case audioMasterGetCurrentProcessLevel: return 0; break; case audioMasterGetAutomationState: FLEXT_LOG("VST master dispatcher: GetAutState");break; case audioMasterOfflineStart: FLEXT_LOG("VST master dispatcher: Offlinestart");break; case audioMasterOfflineRead: FLEXT_LOG("VST master dispatcher: Offlineread");break; case audioMasterOfflineWrite: FLEXT_LOG("VST master dispatcher: Offlinewrite");break; case audioMasterOfflineGetCurrentPass: FLEXT_LOG("VST master dispatcher: OfflineGetcurrentpass");break; case audioMasterOfflineGetCurrentMetaPass: FLEXT_LOG("VST master dispatcher: GetGetCurrentMetapass");break; case audioMasterSetOutputSampleRate: FLEXT_LOG("VST master dispatcher: Setsamplerate");break; case audioMasterGetSpeakerArrangement: FLEXT_LOG("VST master dispatcher: Getspeaker");break; case audioMasterSetIcon: FLEXT_LOG("VST master dispatcher: seticon");break; case audioMasterOpenWindow: FLEXT_LOG("VST master dispatcher: OpenWindow");break; case audioMasterCloseWindow: FLEXT_LOG("VST master dispatcher: CloseWindow");break; case audioMasterGetDirectory: FLEXT_LOG("VST master dispatcher: GetDirectory");break; // case audioMasterUpdateDisplay: post("VST master dispatcher: audioMasterUpdateDisplay");break; default: post("VST master dispatcher: undefed: %d , %d",opcode , effKeysRequired ) ;break; } return 0; } bool VSTPlugin::AddNoteOn( unsigned char note,unsigned char speed,unsigned char midichannel) { return AddMIDI((char)MIDI_NOTEON | midichannel,note,speed); } bool VSTPlugin::AddNoteOff( unsigned char note,unsigned char midichannel) { return AddMIDI((char)MIDI_NOTEOFF | midichannel,note,0); } bool VSTPlugin::replace() { return overwrite; } void VSTPlugin::edit(bool open) { if(instantiated) { if(open) { if(HasEditor() && !IsEdited()) StartEditor(this); } else if(IsEdited()) StopEditor(this); } } void VSTPlugin::visible(bool vis) { if(instantiated && IsEdited()) ShowEditor(this,vis); } void VSTPlugin::EditorIdle() { FLEXT_ASSERT(hwnd != NULL); Dispatch(effEditIdle,0,0, hwnd,0.0f); } RECT VSTPlugin::GetEditorRect() { RECT ret; ERect *r; Dispatch(effEditGetRect,0,0, &r,0.0f); ret.top = r->top; ret.bottom = r->bottom; ret.left = r->left; ret.right = r->right; return ret; } void VSTPlugin::SetEditWindow(HWND h) { hwnd = h; FLEXT_ASSERT(hwnd != NULL); Dispatch(effEditOpen,0,0, hwnd,0.0f); } void VSTPlugin::OnEditorClose() { FLEXT_ASSERT(hwnd != NULL); Dispatch(effEditClose,0,0, hwnd,0.0f); } void VSTPlugin::StopEditing() { hwnd = NULL; } /* void VSTPlugin::SetShowParameters(bool s) { show_params = s; } bool VSTPlugin::ShowParams() { return show_params; } */ void VSTPlugin::AddAftertouch(int value) { if (value < 0) value = 0; else if (value > 127) value = 127; AddMIDI( (char)MIDI_NOTEOFF | _midichannel , value ); } void VSTPlugin::AddPitchBend(int value) { AddMIDI( MIDI_PITCHBEND + (_midichannel & 0xf) , ((value>>7) & 127), (value & 127)); } void VSTPlugin::AddProgramChange(int value) { if (value < 0) value = 0; else if (value > 127) value = 127; AddMIDI( MIDI_PROGRAMCHANGE + (_midichannel & 0xf), value, 0); } void VSTPlugin::AddControlChange(int control, int value) { if (control < 0) control = 0; else if (control > 127) control = 127; if (value < 0) value = 0; else if (value > 127) value = 127; AddMIDI( MIDI_CONTROLCHANGE + (_midichannel & 0xf), control, value); } bool VSTPlugin::GetProgramName( int cat , int p, char *buf) { int parameter = p; if(instantiated) { if(parameter