DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit ac803f5991e1adac6581658a346250d4246e59b6
parent 513fab3ec64ad0daef4604a1c639f343c459d226
Author: falkTX <falktx@falktx.com>
Date:   Thu, 15 Sep 2022 11:53:47 +0100

Rework VST2 callback and cleanup handling

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdistrho/src/DistrhoPluginVST2.cpp | 234+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 121 insertions(+), 113 deletions(-)

diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp @@ -63,6 +63,8 @@ struct ERect { START_NAMESPACE_DISTRHO +// -------------------------------------------------------------------------------------------------------------------- + typedef std::map<const String, String> StringMap; static constexpr const int kVstMidiEventSize = static_cast<int>(sizeof(VstMidiEvent)); @@ -74,7 +76,7 @@ static const writeMidiFunc writeMidiCallback = nullptr; static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- struct ParameterAndNotesHelper { @@ -122,7 +124,7 @@ struct ParameterAndNotesHelper }; #if DISTRHO_PLUGIN_HAS_UI -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT static const sendNoteFunc sendNoteCallback = nullptr; @@ -363,7 +365,7 @@ private: }; #endif // DISTRHO_PLUGIN_HAS_UI -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- class PluginVst : public ParameterAndNotesHelper { @@ -1272,66 +1274,76 @@ private: #endif }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- -struct VstObject { +struct ExtendedAEffect : AEffect { + char _padding[63]; + char valid; audioMasterCallback audioMaster; - PluginVst* plugin; + PluginVst* pluginPtr; }; -#define validObject effect != nullptr && effect->object != nullptr -#define validPlugin effect != nullptr && effect->object != nullptr && ((VstObject*)effect->object)->plugin != nullptr -#define vstObjectPtr (VstObject*)effect->object -#define pluginPtr (vstObjectPtr)->plugin - static ScopedPointer<PluginExporter> sPlugin; -static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) -{ - // first internal init - const bool doInternalInit = (opcode == -1729 && index == 0xdead && value == 0xf00d); +static struct Cleanup { + std::vector<ExtendedAEffect*> effects; - if (doInternalInit || opcode == effOpen) + ~Cleanup() { - if (sPlugin == nullptr) + for (std::vector<ExtendedAEffect*>::iterator it = effects.begin(), end = effects.end(); it != end; ++it) { - // set valid but dummy values - d_nextBufferSize = 512; - d_nextSampleRate = 44100.0; - d_nextPluginIsDummy = true; - d_nextCanRequestParameterValueChanges = true; - - // Create dummy plugin to get data from - sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr); - - // unset - d_nextBufferSize = 0; - d_nextSampleRate = 0.0; - d_nextPluginIsDummy = false; - d_nextCanRequestParameterValueChanges = false; + ExtendedAEffect* const exteffect = *it; + delete exteffect->pluginPtr; + delete exteffect; } - if (doInternalInit) - { - *(PluginExporter**)ptr = sPlugin.get(); - return 0; - } + sPlugin = nullptr; } +} sCleanup; + +// -------------------------------------------------------------------------------------------------------------------- + +static inline +ExtendedAEffect* getExtendedEffect(AEffect* const effect) +{ + if (effect == nullptr) + return nullptr; + + ExtendedAEffect* const exteffect = static_cast<ExtendedAEffect*>(effect); + DISTRHO_SAFE_ASSERT_RETURN(exteffect->valid == 101, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(exteffect->audioMaster != nullptr, nullptr); + + return exteffect; +} + +static inline +PluginVst* getEffectPlugin(AEffect* const effect) +{ + if (effect == nullptr) + return nullptr; + + ExtendedAEffect* const exteffect = static_cast<ExtendedAEffect*>(effect); + DISTRHO_SAFE_ASSERT_RETURN(exteffect->valid == 101, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(exteffect->audioMaster != nullptr, nullptr); + + return exteffect->pluginPtr; +} + +// -------------------------------------------------------------------------------------------------------------------- +static intptr_t vst_dispatcherCallback(AEffect* const effect, const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) +{ // handle base opcodes switch (opcode) { case effOpen: - if (VstObject* const obj = vstObjectPtr) + if (ExtendedAEffect* const exteffect = getExtendedEffect(effect)) { - // this must always be valid - DISTRHO_SAFE_ASSERT_RETURN(obj->audioMaster != nullptr, 0); - // some hosts call effOpen twice - if (obj->plugin != nullptr) + if (exteffect->pluginPtr != nullptr) return 1; - audioMasterCallback audioMaster = (audioMasterCallback)obj->audioMaster; + const audioMasterCallback audioMaster = exteffect->audioMaster; d_nextBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); d_nextSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); @@ -1343,32 +1355,34 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t if (d_nextSampleRate <= 0.0) d_nextSampleRate = 44100.0; - obj->plugin = new PluginVst(audioMaster, effect); + exteffect->pluginPtr = new PluginVst(audioMaster, effect); return 1; } return 0; case effClose: - if (VstObject* const obj = vstObjectPtr) + if (ExtendedAEffect* const exteffect = getExtendedEffect(effect)) { - if (obj->plugin != nullptr) + // delete plugin object + if (exteffect->pluginPtr != nullptr) { - delete obj->plugin; - obj->plugin = nullptr; + delete exteffect->pluginPtr; + exteffect->pluginPtr = nullptr; } -#if 0 - /* This code invalidates the object created in VSTPluginMain - * Probably not safe against all hosts */ - obj->audioMaster = nullptr; - effect->object = nullptr; - delete obj; -#endif + // delete effect too, if it comes from us + const std::vector<ExtendedAEffect*>::iterator it = std::find(sCleanup.effects.begin(), sCleanup.effects.end(), exteffect); + if (it != sCleanup.effects.end()) + { + delete exteffect; + sCleanup.effects.erase(it); + } - sPlugin = nullptr; + // delete global plugin instance too if this is the last loaded effect + if (sCleanup.effects.empty()) + sPlugin = nullptr; return 1; } - //delete effect; return 0; case effGetParamLabel: @@ -1469,11 +1483,11 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t return 0; case effGetPlugCategory: -#if DISTRHO_PLUGIN_IS_SYNTH + #if DISTRHO_PLUGIN_IS_SYNTH return kPlugCategSynth; -#else + #else return kPlugCategEffect; -#endif + #endif case effGetEffectName: if (char* const cptr = (char*)ptr) @@ -1507,69 +1521,50 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t }; // handle advanced opcodes - if (validPlugin) + if (PluginVst* const pluginPtr = getEffectPlugin(effect)) return pluginPtr->vst_dispatcher(opcode, index, value, ptr, opt); return 0; } -static float vst_getParameterCallback(AEffect* effect, int32_t index) +static float vst_getParameterCallback(AEffect* const effect, const int32_t index) { - if (validPlugin) + if (PluginVst* const pluginPtr = getEffectPlugin(effect)) return pluginPtr->vst_getParameter(index); return 0.0f; } -static void vst_setParameterCallback(AEffect* effect, int32_t index, float value) +static void vst_setParameterCallback(AEffect* const effect, const int32_t index, const float value) { - if (validPlugin) + if (PluginVst* const pluginPtr = getEffectPlugin(effect)) pluginPtr->vst_setParameter(index, value); } -static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) +static void vst_processCallback(AEffect* const effect, float** const inputs, float** const outputs, const int32_t sampleFrames) { - if (validPlugin) + if (PluginVst* const pluginPtr = getEffectPlugin(effect)) pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames); } -static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames) +static void vst_processReplacingCallback(AEffect* const effect, float** const inputs, float** const outputs, const int32_t sampleFrames) { - if (validPlugin) + if (PluginVst* const pluginPtr = getEffectPlugin(effect)) pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames); } -#undef pluginPtr -#undef validObject -#undef validPlugin -#undef vstObjectPtr - -static struct Cleanup { - std::vector<AEffect*> effects; - - ~Cleanup() - { - for (std::vector<AEffect*>::iterator it = effects.begin(), end = effects.end(); it != end; ++it) - { - AEffect* const effect = *it; - delete (VstObject*)effect->object; - delete effect; - } - } -} sCleanup; - // ----------------------------------------------------------------------- END_NAMESPACE_DISTRHO DISTRHO_PLUGIN_EXPORT -#if DISTRHO_OS_MAC || DISTRHO_OS_WASM || DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) const AEffect* VSTPluginMain(audioMasterCallback audioMaster); #else const AEffect* VSTPluginMain(audioMasterCallback audioMaster) asm ("main"); #endif DISTRHO_PLUGIN_EXPORT -const AEffect* VSTPluginMain(audioMasterCallback audioMaster) +const AEffect* VSTPluginMain(const audioMasterCallback audioMaster) { USE_NAMESPACE_DISTRHO @@ -1583,7 +1578,7 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) { String tmpPath(getBinaryFilename()); tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); -#ifdef DISTRHO_OS_MAC + #ifdef DISTRHO_OS_MAC if (tmpPath.endsWith("/MacOS")) { tmpPath.truncate(tmpPath.rfind('/')); @@ -1594,38 +1589,52 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) d_nextBundlePath = bundlePath.buffer(); } } -#else + #else if (tmpPath.endsWith(".vst")) { bundlePath = tmpPath; d_nextBundlePath = bundlePath.buffer(); } -#endif + #endif } // first internal init - PluginExporter* plugin = nullptr; - vst_dispatcherCallback(nullptr, -1729, 0xdead, 0xf00d, &plugin, 0.0f); - DISTRHO_SAFE_ASSERT_RETURN(plugin != nullptr, nullptr); + if (sPlugin == nullptr) + { + // set valid but dummy values + d_nextBufferSize = 512; + d_nextSampleRate = 44100.0; + d_nextPluginIsDummy = true; + d_nextCanRequestParameterValueChanges = true; + + // Create dummy plugin to get data from + sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr); + + // unset + d_nextBufferSize = 0; + d_nextSampleRate = 0.0; + d_nextPluginIsDummy = false; + d_nextCanRequestParameterValueChanges = false; + } - AEffect* const effect(new AEffect); - std::memset(effect, 0, sizeof(AEffect)); + ExtendedAEffect* const effect = new ExtendedAEffect; + std::memset(effect, 0, sizeof(ExtendedAEffect)); // vst fields effect->magic = kEffectMagic; - effect->uniqueID = plugin->getUniqueId(); - effect->version = plugin->getVersion(); + effect->uniqueID = sPlugin->getUniqueId(); + effect->version = sPlugin->getVersion(); // VST doesn't support parameter outputs. we can fake them, but it is a hack. Disabled by default. #ifdef DPF_VST_SHOW_PARAMETER_OUTPUTS - const int numParams = plugin->getParameterCount(); + const int numParams = sPlugin->getParameterCount(); #else int numParams = 0; bool outputsReached = false; - for (uint32_t i=0, count=plugin->getParameterCount(); i < count; ++i) + for (uint32_t i=0, count=sPlugin->getParameterCount(); i < count; ++i) { - if (plugin->isParameterInput(i)) + if (sPlugin->isParameterInput(i)) { // parameter outputs must be all at the end DISTRHO_SAFE_ASSERT_BREAK(! outputsReached); @@ -1644,15 +1653,15 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) // plugin flags effect->flags |= effFlagsCanReplacing; -#if DISTRHO_PLUGIN_IS_SYNTH + #if DISTRHO_PLUGIN_IS_SYNTH effect->flags |= effFlagsIsSynth; -#endif -#if DISTRHO_PLUGIN_HAS_UI + #endif + #if DISTRHO_PLUGIN_HAS_UI effect->flags |= effFlagsHasEditor; -#endif -#if DISTRHO_PLUGIN_WANT_STATE + #endif + #if DISTRHO_PLUGIN_WANT_STATE effect->flags |= effFlagsProgramChunks; -#endif + #endif // static calls effect->dispatcher = vst_dispatcherCallback; @@ -1661,13 +1670,12 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) effect->setParameter = vst_setParameterCallback; effect->processReplacing = vst_processReplacingCallback; - // pointers - VstObject* const obj = new VstObject(); - obj->audioMaster = audioMaster; - obj->plugin = nullptr; + // special values + effect->valid = 101; + effect->audioMaster = audioMaster; + effect->pluginPtr = nullptr; // done - effect->object = obj; sCleanup.effects.push_back(effect); return effect;