VstIntPtr VSTCALLBACK IPlugVST::VSTDispatcher(AEffect *pEffect, VstInt32 opCode, VstInt32 idx, VstIntPtr value, void *ptr, float opt) { // VSTDispatcher is an IPlugVST class member, we can access anything in IPlugVST from here. IPlugVST* _this = (IPlugVST*) pEffect->object; if (!_this) { return 0; } IPlugBase::IMutexLock lock(_this); // Handle a couple of opcodes here to make debugging easier. switch (opCode) { case effEditIdle: case __effIdleDeprecated: #ifdef USE_IDLE_CALLS _this->OnIdle(); #endif return 0; } Trace(TRACELOC, "%d(%s):%d:%d", opCode, VSTOpcodeStr(opCode), idx, (int) value); switch (opCode) { case effOpen: { _this->HostSpecificInit(); _this->OnParamReset(); return 0; } case effClose: { lock.Destroy(); DELETE_NULL(_this); return 0; } case effGetParamLabel: { if (idx >= 0 && idx < _this->NParams()) { strcpy((char*) ptr, _this->GetParam(idx)->GetLabelForHost()); } return 0; } case effGetParamDisplay: { if (idx >= 0 && idx < _this->NParams()) { _this->GetParam(idx)->GetDisplayForHost((char*) ptr); } return 0; } case effGetParamName: { if (idx >= 0 && idx < _this->NParams()) { strcpy((char*) ptr, _this->GetParam(idx)->GetNameForHost()); } return 0; } //could implement effGetParameterProperties to group parameters, but can't find a host that supports it // case effGetParameterProperties: // { // if (idx >= 0 && idx < _this->NParams()) // { // VstParameterProperties* props = (VstParameterProperties*) ptr; // // props->flags = kVstParameterSupportsDisplayCategory; // props->category = idx+1; // props->numParametersInCategory = 1; // strcpy(props->categoryLabel, "test"); // } // return 1; // } case effGetParameterProperties: { if (idx >= 0 && idx < _this->NParams()) { VstParameterProperties* props = (VstParameterProperties*) ptr; props->flags = 0; IParam* pParam = _this->GetParam(idx); if (pParam->Type() == IParam::kTypeBool) { props->flags |= kVstParameterIsSwitch; } if (pParam->Type() == IParam::kTypeEnum || pParam->Type() == IParam::kTypeInt) { props->flags |= kVstParameterUsesFloatStep; int possibleValuesCount = (int) (pParam->GetMax() - pParam->GetMin()); props->stepFloat = 1.0 / possibleValuesCount; props->smallStepFloat = props->stepFloat; props->largeStepFloat = props->stepFloat; } } return 1; } case effString2Parameter: { if (idx >= 0 && idx < _this->NParams()) { if (ptr) { double v; IParam* pParam = _this->GetParam(idx); if (pParam->GetNDisplayTexts()) { int vi; if (!pParam->MapDisplayText((char*)ptr, &vi)) return 0; v = (double)vi; } else { v = atof((char*)ptr); if (pParam->DisplayIsNegated()) v = -v; } if (_this->GetGUI()) _this->GetGUI()->SetParameterFromPlug(idx, v, false); pParam->Set(v); _this->OnParamChange(idx); } return 1; } return 0; } case effSetSampleRate: { _this->SetSampleRate(opt); _this->Reset(); return 0; } case effSetBlockSize: { _this->SetBlockSize(value); _this->Reset(); return 0; } case effMainsChanged: { if (!value) { _this->OnActivate(false); _this->Reset(); } else { _this->OnActivate(true); } return 0; } case effEditGetRect: { if (ptr && _this->GetGUI()) { *(ERect**) ptr = &(_this->mEditRect); return 1; } ptr = 0; return 0; } case effEditOpen: { IGraphics* pGraphics = _this->GetGUI(); if (pGraphics) { #ifdef _WIN32 if (!pGraphics->OpenWindow(ptr)) pGraphics=0; #else // OSX, check if we are in a Cocoa VST host #if defined(__LP64__) if (!pGraphics->OpenWindow(ptr)) pGraphics=0; #else bool iscocoa = (_this->mHasVSTExtensions&VSTEXT_COCOA); if (iscocoa && !pGraphics->OpenWindow(ptr)) pGraphics=0; if (!iscocoa && !pGraphics->OpenWindow(ptr, 0)) pGraphics=0; #endif #endif if (pGraphics) { _this->OnGUIOpen(); return 1; } } return 0; } case effEditClose: { if (_this->GetGUI()) { _this->OnGUIClose(); _this->GetGUI()->CloseWindow(); return 1; } return 0; } case __effIdentifyDeprecated: { return 'NvEf'; // Random deprecated magic. } case effGetChunk: { BYTE** ppData = (BYTE**) ptr; if (ppData) { bool isBank = (!idx); ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState)); _this->InitChunkWithIPlugVer(pChunk); bool savedOK = true; if (isBank) { _this->ModifyCurrentPreset(); savedOK = _this->SerializePresets(pChunk); } else { savedOK = _this->SerializeState(pChunk); } if (savedOK && pChunk->Size()) { *ppData = pChunk->GetBytes(); return pChunk->Size(); } } return 0; } case effSetChunk: { if (ptr) { bool isBank = (!idx); ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState)); pChunk->Resize(value); memcpy(pChunk->GetBytes(), ptr, value); int pos = 0; int iplugVer = _this->GetIPlugVerFromChunk(pChunk, &pos); isBank &= (iplugVer >= 0x010000); if (isBank) { pos = _this->UnserializePresets(pChunk, pos); } else { pos = _this->UnserializeState(pChunk, pos); _this->ModifyCurrentPreset(); } if (pos >= 0) { _this->RedrawParamControls(); return 1; } } return 0; } case effProcessEvents: { VstEvents* pEvents = (VstEvents*) ptr; if (pEvents && pEvents->events) { for (int i = 0; i < pEvents->numEvents; ++i) { VstEvent* pEvent = pEvents->events[i]; if (pEvent) { if (pEvent->type == kVstMidiType) { VstMidiEvent* pME = (VstMidiEvent*) pEvent; IMidiMsg msg(pME->deltaFrames, pME->midiData[0], pME->midiData[1], pME->midiData[2]); _this->ProcessMidiMsg(&msg); //#ifdef TRACER_BUILD // msg.LogMsg(); //#endif } else if (pEvent->type == kVstSysExType) { VstMidiSysexEvent* pSE = (VstMidiSysexEvent*) pEvent; ISysEx sysex(pSE->deltaFrames, (const BYTE*)pSE->sysexDump, pSE->dumpBytes); _this->ProcessSysEx(&sysex); } } } return 1; } return 0; } case effCanBeAutomated: { return 1; } case effGetInputProperties: { if (ptr && idx >= 0 && idx < _this->NInChannels()) { VstPinProperties* pp = (VstPinProperties*) ptr; pp->flags = kVstPinIsActive; if (!(idx%2) && idx < _this->NInChannels()-1) { pp->flags |= kVstPinIsStereo; } if (_this->GetInputLabel(idx)->GetLength()) { sprintf(pp->label, "%s", _this->GetInputLabel(idx)->Get()); } else { sprintf(pp->label, "Input %d", idx + 1); } return 1; } return 0; } case effGetOutputProperties: { if (ptr && idx >= 0 && idx < _this->NOutChannels()) { VstPinProperties* pp = (VstPinProperties*) ptr; pp->flags = kVstPinIsActive; if (!(idx%2) && idx < _this->NOutChannels()-1) { pp->flags |= kVstPinIsStereo; } if (_this->GetOutputLabel(idx)->GetLength()) { sprintf(pp->label, "%s", _this->GetOutputLabel(idx)->Get()); } else { sprintf(pp->label, "Output %d", idx + 1); } return 1; } return 0; } case effGetPlugCategory: { if (_this->IsInst()) return kPlugCategSynth; return kPlugCategEffect; } case effProcessVarIo: { // VstVariableIo* pIO = (VstVariableIo*) ptr; // For offline processing (of audio files?) return 0; } case effSetSpeakerArrangement: { VstSpeakerArrangement* pInputArr = (VstSpeakerArrangement*) value; VstSpeakerArrangement* pOutputArr = (VstSpeakerArrangement*) ptr; if (pInputArr) { int n = pInputArr->numChannels; _this->SetInputChannelConnections(0, n, true); _this->SetInputChannelConnections(n, _this->NInChannels() - n, false); } if (pOutputArr) { int n = pOutputArr->numChannels; _this->SetOutputChannelConnections(0, n, true); _this->SetOutputChannelConnections(n, _this->NOutChannels() - n, false); } return 1; } case effGetSpeakerArrangement: { VstSpeakerArrangement** ppInputArr = (VstSpeakerArrangement**) value; VstSpeakerArrangement** ppOutputArr = (VstSpeakerArrangement**) ptr; if (ppInputArr) { *ppInputArr = &(_this->mInputSpkrArr); } if (ppOutputArr) { *ppOutputArr = &(_this->mOutputSpkrArr); } return 1; } case effGetEffectName: { if (ptr) { strcpy((char*) ptr, _this->GetEffectName()); return 1; } return 0; } case effGetProductString: { if (ptr) { strcpy((char*) ptr, _this->GetProductName()); return 1; } return 0; } case effGetVendorString: { if (ptr) { strcpy((char*) ptr, _this->GetMfrName()); return 1; } return 0; } case effCanDo: { if (ptr) { Trace(TRACELOC, "VSTCanDo(%s)", (char*) ptr); if (!strcmp((char*) ptr, "receiveVstTimeInfo")) { return 1; } if (_this->DoesMIDI()) { if (!strcmp((char*) ptr, "sendVstEvents") || !strcmp((char*) ptr, "sendVstMidiEvent") || !strcmp((char*) ptr, "receiveVstEvents") || !strcmp((char*) ptr, "receiveVstMidiEvent")) // || { //!strcmp((char*) ptr, "midiProgramNames")) { return 1; } } // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/ if (!strcmp((char*) ptr, "hasCockosExtensions")) { _this->mHasVSTExtensions |= VSTEXT_COCKOS; return 0xbeef0000; } else if (!strcmp((char*) ptr, "hasCockosViewAsConfig")) { _this->mHasVSTExtensions |= VSTEXT_COCOA; return 0xbeef0000; } } return 0; } case effGetTailSize: { return _this->GetTailSize(); } case effVendorSpecific: { // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/ if (idx == effGetParamDisplay && ptr) { if (value >= 0 && value < _this->NParams()) { _this->GetParam(value)->GetDisplayForHost((double) opt, true, (char*) ptr); } return 0xbeef; } if (idx == kVstParameterUsesIntStep) { if (value >= 0 && value < _this->NParams()) { if (_this->GetParam(value)->Type() != IParam::kTypeDouble) { return 0xbeef; } } } return 0; } case effGetProgram: { return _this->GetCurrentPresetIdx(); } case effSetProgram: { if (_this->DoesStateChunks() == false) { _this->ModifyCurrentPreset(); // TODO: test, something is funny about this http://forum.cockos.com/showpost.php?p=485113&postcount=22 } _this->RestorePreset((int) value); return 0; } case effGetProgramNameIndexed: { strcpy((char*) ptr, _this->GetPresetName(idx)); return (CSTR_NOT_EMPTY((char*) ptr) ? 1 : 0); } case effSetProgramName: { if (ptr) { _this->ModifyCurrentPreset((char*) ptr); _this->PresetsChangedByHost(); } return 0; } case effGetProgramName: { if (ptr) { int idx = _this->GetCurrentPresetIdx(); strcpy((char*) ptr, _this->GetPresetName(idx)); } return 0; } case effGetMidiKeyName: { if (ptr) { MidiKeyName* pMKN = (MidiKeyName*) ptr; pMKN->keyName[0] = '\0'; if (_this->MidiNoteName(pMKN->thisKeyNumber, pMKN->keyName)) { return 1; } } return 0; } case effGetVstVersion: { return VST_VERSION; } case effEndSetProgram: case effBeginSetProgram: case effGetMidiProgramName: case effHasMidiProgramsChanged: case effGetMidiProgramCategory: case effGetCurrentMidiProgram: case effSetBypass: default: { return 0; } } }
VstIntPtr VSTCALLBACK IPlugVST::VSTDispatcher(AEffect *pEffect, VstInt32 opCode, VstInt32 idx, VstIntPtr value, void *ptr, float opt) { // VSTDispatcher is an IPlugVST class member, we can access anything in IPlugVST from here. IPlugVST* _this = (IPlugVST*) pEffect->object; if (!_this) { return 0; } IPlugBase::IMutexLock lock(_this); // Handle a couple of opcodes here to make debugging easier. switch (opCode) { case effEditIdle: case __effIdleDeprecated: #ifdef USE_IDLE_CALLS _this->OnIdle(); #endif return 0; } Trace(TRACELOC, "%d(%s):%d:%d", opCode, VSTOpcodeStr(opCode), idx, (int) value); switch (opCode) { case effOpen: { _this->HostSpecificInit(); _this->OnParamReset(); return 0; } case effClose: { lock.Destroy(); DELETE_NULL(_this); return 0; } case effGetParamLabel: { if (idx >= 0 && idx < _this->NParams()) { strcpy((char*) ptr, _this->GetParam(idx)->GetLabelForHost()); } return 0; } case effGetParamDisplay: { if (idx >= 0 && idx < _this->NParams()) { _this->GetParam(idx)->GetDisplayForHost((char*) ptr); } return 0; } case effGetParamName: { if (idx >= 0 && idx < _this->NParams()) { strcpy((char*) ptr, _this->GetParam(idx)->GetNameForHost()); } return 0; } case effString2Parameter: { if (idx >= 0 && idx < _this->NParams()) { if (ptr) { IParam* pParam = _this->GetParam(idx); double v = VSTString2Parameter(pParam, (char*)ptr); if (_this->GetGUI()) _this->GetGUI()->SetParameterFromPlug(idx, v, false); pParam->Set(v); _this->OnParamChange(idx); } return 1; } return 0; } case effSetSampleRate: { _this->SetSampleRate(opt); _this->Reset(); return 0; } case effSetBlockSize: { _this->SetBlockSize(value); _this->Reset(); return 0; } case effMainsChanged: { if (!value) { _this->OnActivate(false); _this->Reset(); } else { _this->OnActivate(true); } return 0; } case effEditGetRect: { if (ptr && _this->GetGUI()) { *(ERect**) ptr = &(_this->mEditRect); return 1; } ptr = 0; return 0; } case effEditOpen: { IGraphics* pGraphics = _this->GetGUI(); if (pGraphics) { #if defined(_WIN32) || defined(IPLUG_NO_CARBON_SUPPORT) if (!pGraphics->OpenWindow(ptr)) pGraphics=0; #else // OSX, check if we are in a Cocoa VST host bool iscocoa = (_this->mHasVSTExtensions&VSTEXT_COCOA); if (iscocoa && !pGraphics->OpenWindow(ptr)) pGraphics=0; if (!iscocoa && !pGraphics->OpenWindow(ptr, 0)) pGraphics=0; #endif if (pGraphics) { _this->OnGUIOpen(); return 1; } } return 0; } case effEditClose: { if (_this->GetGUI()) { _this->OnGUIClose(); _this->GetGUI()->CloseWindow(); return 1; } return 0; } case __effIdentifyDeprecated: { return 'NvEf'; // Random deprecated magic. } case effGetChunk: { BYTE** ppData = (BYTE**) ptr; if (ppData) { bool isBank = (!idx); ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState)); InitializeVSTChunk(pChunk); bool savedOK = true; if (isBank) { _this->ModifyCurrentPreset(); savedOK = _this->SerializePresets(pChunk); //savedOK = _this->SerializeState(pChunk); } else { savedOK = _this->SerializeState(pChunk); } if (savedOK && pChunk->Size()) { *ppData = pChunk->GetBytes(); return pChunk->Size(); } } return 0; } case effSetChunk: { if (ptr) { bool isBank = (!idx); ByteChunk* pChunk = (isBank ? &(_this->mBankState) : &(_this->mState)); pChunk->Resize(value); memcpy(pChunk->GetBytes(), ptr, value); int pos = 0; int iplugVer = GetIPlugVerFromChunk(pChunk, &pos); isBank &= (iplugVer >= 0x010000); if (isBank) { pos = _this->UnserializePresets(pChunk, pos); //pos = _this->UnserializeState(pChunk, pos); } else { pos = _this->UnserializeState(pChunk, pos); _this->ModifyCurrentPreset(); } if (pos >= 0) { _this->RedrawParamControls(); return 1; } } return 0; } case effProcessEvents: { VstEvents* pEvents = (VstEvents*) ptr; if (pEvents && pEvents->events) { for (int i = 0; i < pEvents->numEvents; ++i) { VstEvent* pEvent = pEvents->events[i]; if (pEvent) { if (pEvent->type == kVstMidiType) { VstMidiEvent* pME = (VstMidiEvent*) pEvent; IMidiMsg msg(pME->deltaFrames, pME->midiData[0], pME->midiData[1], pME->midiData[2]); _this->ProcessMidiMsg(&msg); //#ifdef TRACER_BUILD // msg.LogMsg(); //#endif } else if (pEvent->type == kVstSysExType) { VstMidiSysexEvent* pSE = (VstMidiSysexEvent*) pEvent; ISysEx sysex(pSE->deltaFrames, (const BYTE*)pSE->sysexDump, pSE->dumpBytes); _this->ProcessSysEx(&sysex); } } } return 1; } return 0; } case effCanBeAutomated: { return 1; } case effGetInputProperties: { if (ptr && idx >= 0 && idx < _this->NInChannels()) { VstPinProperties* pp = (VstPinProperties*) ptr; pp->flags = kVstPinIsActive; if (!(idx%2) && idx < _this->NInChannels()-1) { pp->flags |= kVstPinIsStereo; } sprintf(pp->label, "Input %d", idx + 1); return 1; } return 0; } case effGetOutputProperties: { if (ptr && idx >= 0 && idx < _this->NOutChannels()) { VstPinProperties* pp = (VstPinProperties*) ptr; pp->flags = kVstPinIsActive; if (!(idx%2) && idx < _this->NOutChannels()-1) { pp->flags |= kVstPinIsStereo; } sprintf(pp->label, "Output %d", idx + 1); return 1; } return 0; } case effGetPlugCategory: { if (_this->IsInst()) return kPlugCategSynth; return kPlugCategEffect; } case effProcessVarIo: { // VstVariableIo* pIO = (VstVariableIo*) ptr; // For offline processing (of audio files?) return 0; } case effSetSpeakerArrangement: { VstSpeakerArrangement* pInputArr = (VstSpeakerArrangement*) value; VstSpeakerArrangement* pOutputArr = (VstSpeakerArrangement*) ptr; if (pInputArr) { int n = pInputArr->numChannels; _this->SetInputChannelConnections(0, n, true); _this->SetInputChannelConnections(n, _this->NInChannels() - n, false); } if (pOutputArr) { int n = pOutputArr->numChannels; _this->SetOutputChannelConnections(0, n, true); _this->SetOutputChannelConnections(n, _this->NOutChannels() - n, false); } return 1; } case effGetSpeakerArrangement: { VstSpeakerArrangement** ppInputArr = (VstSpeakerArrangement**) value; VstSpeakerArrangement** ppOutputArr = (VstSpeakerArrangement**) ptr; if (ppInputArr) { *ppInputArr = &(_this->mInputSpkrArr); } if (ppOutputArr) { *ppOutputArr = &(_this->mOutputSpkrArr); } return 1; } case effGetEffectName: { if (ptr) { strcpy((char*) ptr, _this->GetEffectName()); return 1; } return 0; } case effGetProductString: { if (ptr) { strcpy((char*) ptr, _this->GetProductName()); return 1; } return 0; } case effGetVendorString: { if (ptr) { strcpy((char*) ptr, _this->GetMfrName()); return 1; } return 0; } case effGetVendorVersion: { return _this->GetEffectVersion(true); } case effCanDo: { if (ptr) { Trace(TRACELOC, "VSTCanDo(%s)", (char*) ptr); if (!strcmp((char*) ptr, "receiveVstTimeInfo")) { return 1; } if (_this->DoesMIDI()) { if (_this->DoesMIDI() & 1) { if (!strcmp((char*) ptr, "sendVstEvents") || !strcmp((char*) ptr, "sendVstMidiEvent")) { return 1; } } if (_this->DoesMIDI() <= 2) { if (!strcmp((char*) ptr, "receiveVstEvents") || !strcmp((char*) ptr, "receiveVstMidiEvent")) { return 1; } } //if (!strcmp((char*) ptr, "midiProgramNames")) { // return 1; //} } // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/ if (!strcmp((char*) ptr, "hasCockosExtensions")) { _this->mHasVSTExtensions |= VSTEXT_COCKOS; return 0xbeef0000; } else if (!strcmp((char*) ptr, "hasCockosViewAsConfig")) { _this->mHasVSTExtensions |= VSTEXT_COCOA; return 0xbeef0000; } } return 0; } case effVendorSpecific: { switch (idx) { // Mouse wheel case 0x73744341: { if (value == 0x57686565) { IGraphics* pGraphics = _this->GetGUI(); if (pGraphics) { return pGraphics->ProcessMouseWheel(opt); } } break; } // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/ case effGetParamDisplay: { if (ptr) { if (value >= 0 && value < _this->NParams()) { _this->GetParam(value)->GetDisplayForHost((double) opt, true, (char*) ptr); } return 0xbeef; } break; } case effString2Parameter: { if (ptr && value >= 0 && value < _this->NParams()) { if (*(char*) ptr != '\0') { IParam* pParam = _this->GetParam(value); sprintf((char*) ptr, "%.17f", pParam->GetNormalized(VSTString2Parameter(pParam, (char*) ptr))); } return 0xbeef; } break; } case kVstParameterUsesIntStep: { if (value >= 0 && value < _this->NParams()) { IParam* pParam = _this->GetParam(value); switch (pParam->Type()) { case IParam::kTypeBool: { return 0xbeef; } case IParam::kTypeInt: case IParam::kTypeEnum: { double min, max; pParam->GetBounds(&min, &max); if (fabs(max - min) < 1.5) { return 0xbeef; } break; } } } break; } } return 0; } case effGetProgram: { return _this->GetCurrentPresetIdx(); } case effSetProgram: { //if (!(_this->DoesStateChunks())) { _this->ModifyCurrentPreset(); //} _this->RestorePreset((int) value); return 0; } case effGetProgramNameIndexed: { strcpy((char*) ptr, _this->GetPresetName(idx)); return (CSTR_NOT_EMPTY((char*) ptr) ? 1 : 0); } case effSetProgramName: { if (ptr) { _this->ModifyCurrentPreset((char*) ptr); } return 0; } case effGetProgramName: { if (ptr) { int idx = _this->GetCurrentPresetIdx(); strcpy((char*) ptr, _this->GetPresetName(idx)); } return 0; } case effGetMidiKeyName: { if (ptr) { MidiKeyName* pMKN = (MidiKeyName*) ptr; pMKN->keyName[0] = '\0'; if (_this->MidiNoteName(pMKN->thisKeyNumber, pMKN->keyName)) { return 1; } } return 0; } case effGetVstVersion: { return VST_VERSION; } case effBeginSetProgram: case effEndSetProgram: case effGetMidiProgramName: case effHasMidiProgramsChanged: case effGetMidiProgramCategory: case effGetCurrentMidiProgram: case effSetBypass: default: { return 0; } } }