DPF

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

commit d7ba57dee15380086fb2c2ea0d40fbc147307fc0
parent 4bba885338e39608ad689a2b93fa368b8b077f65
Author: falkTX <falktx@falktx.com>
Date:   Thu, 13 May 2021 03:39:51 +0100

Initial work for VST3 compatible plugins, lots to do..

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

Diffstat:
MMakefile.plugins.mk | 41+++++++++++++++++++++++++++++++++++------
Mdistrho/DistrhoPluginMain.cpp | 2++
Mdistrho/DistrhoUIMain.cpp | 2++
Mdistrho/src/DistrhoPluginInternal.hpp | 2++
Adistrho/src/DistrhoPluginVST3.cpp | 333+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdistrho/src/travesty/factory.h | 7-------
Mexamples/Parameters/Makefile | 1+
7 files changed, 375 insertions(+), 13 deletions(-)

diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk @@ -53,6 +53,19 @@ endif BASE_FLAGS += -DHAVE_JACK # --------------------------------------------------------------------------------------------------------------------- +# Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html + +ifeq ($(LINUX),true) +VST3_FILENAME = $(TARGET_PROCESSOR)-linux/$(NAME).so +endif +ifeq ($(MACOS),true) +VST3_FILENAME = MacOS/$(NAME) +endif +ifeq ($(WINDOWS),true) +VST3_FILENAME = $(TARGET_PROCESSOR)-win/$(NAME).vst3 +endif + +# --------------------------------------------------------------------------------------------------------------------- # Set files to build OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o) @@ -72,7 +85,10 @@ dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT) lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) -vst = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) +vst2 = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) +ifneq ($(VST3_FILENAME),) +vst3 = $(TARGET_DIR)/$(NAME).vst3/Contents/$(VST3_FILENAME) +endif # --------------------------------------------------------------------------------------------------------------------- # Set plugin symbols to export @@ -83,6 +99,7 @@ SYMBOLS_DSSI = -Wl,-exported_symbol,_ladspa_descriptor -Wl,-exported_symbol,_d SYMBOLS_LV2 = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor SYMBOLS_VST2 = -Wl,-exported_symbol,_VSTPluginMain +SYMBOLS_VST3 = -Wl,-exported_symbol,_GetPluginFactory -Wl,-exported_symbol,_bundleEntry -Wl,-exported_symbol,_bundleExit endif # --------------------------------------------------------------------------------------------------------------------- @@ -288,20 +305,30 @@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ # --------------------------------------------------------------------------------------------------------------------- -# VST +# VST2 -vst: $(vst) +vst2 vst: $(vst2) ifeq ($(HAVE_DGL),true) -$(vst): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST.cpp.o $(DGL_LIB) +$(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST.cpp.o $(DGL_LIB) else -$(vst): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o +$(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o endif -@mkdir -p $(shell dirname $@) - @echo "Creating VST plugin for $(NAME)" + @echo "Creating VST2 plugin for $(NAME)" $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@ # --------------------------------------------------------------------------------------------------------------------- +# VST3 + +vst3: $(vst3) + +$(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o + -@mkdir -p $(shell dirname $@) + @echo "Creating VST3 plugin for $(NAME)" + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ + +# --------------------------------------------------------------------------------------------------------------------- -include $(OBJS_DSP:%.o=%.d) ifneq ($(UI_TYPE),) @@ -313,10 +340,12 @@ endif -include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.d +-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_VST.cpp.d +-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp @@ -27,6 +27,8 @@ # include "src/DistrhoPluginLV2export.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_VST) # include "src/DistrhoPluginVST.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_VST3) +# include "src/DistrhoPluginVST3.cpp" #else # error unsupported format #endif diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp @@ -26,6 +26,8 @@ # include "src/DistrhoUILV2.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_VST) // nothing +#elif defined(DISTRHO_PLUGIN_TARGET_VST3) +// nothing #else # error unsupported format #endif diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp @@ -814,7 +814,9 @@ private: static const PortGroupWithId sFallbackPortGroup; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) +#ifndef DISTRHO_PLUGIN_TARGET_VST3 /* there is no way around this for VST3 */ DISTRHO_PREVENT_HEAP_ALLOCATION +#endif }; // ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp @@ -0,0 +1,333 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPluginInternal.hpp" +#include "../extra/ScopedPointer.hpp" + +#include "travesty/audio_processor.h" +#include "travesty/component.h" +#include "travesty/edit_controller.h" +#include "travesty/factory.h" + +START_NAMESPACE_DISTRHO + +#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +static const writeMidiFunc writeMidiCallback = nullptr; +#endif + +// custom v3_tuid compatible type +typedef uint32_t dpf_tuid[4]; +static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch"); + +// custom uids, fully created during module init +static constexpr const uint32_t dpf_id_entry = d_cconst('D', 'P', 'F', ' '); +static constexpr const uint32_t dpf_id_clas = d_cconst('c', 'l', 'a', 's'); +static constexpr const uint32_t dpf_id_comp = d_cconst('c', 'o', 'm', 'p'); +static constexpr const uint32_t dpf_id_ctrl = d_cconst('c', 't', 'r', 'l'); +static constexpr const uint32_t dpf_id_proc = d_cconst('p', 'r', 'o', 'c'); +static constexpr const uint32_t dpf_id_view = d_cconst('v', 'i', 'e', 'w'); + +static dpf_tuid dpf_tuid_class = { dpf_id_entry, dpf_id_clas, 0, 0 }; +static dpf_tuid dpf_tuid_component = { dpf_id_entry, dpf_id_comp, 0, 0 }; +static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, 0 }; +static dpf_tuid dpf_tuid_processor = { dpf_id_entry, dpf_id_proc, 0, 0 }; +static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; + +// ----------------------------------------------------------------------- + +void strncpy(char* const dst, const char* const src, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + + if (const size_t len = std::min(std::strlen(src), size-1U)) + { + std::memcpy(dst, src, len); + dst[len] = '\0'; + } + else + { + dst[0] = '\0'; + } +} + +// ----------------------------------------------------------------------- + +// v3_funknown, v3_plugin_base, v3_component + +// audio_processor + +class PluginVst3 +{ +public: + PluginVst3() + : fPlugin(this, writeMidiCallback) + { + } + +private: + // Plugin + PluginExporter fPlugin; + + // VST3 stuff + // TODO + +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool writeMidi(const MidiEvent& midiEvent) + { + // TODO + return true; + } + + static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) + { + return ((PluginVst*)ptr)->writeMidi(midiEvent); + } +#endif + +}; + +// ----------------------------------------------------------------------- +// WIP this whole section still TODO + +struct ControllerComponent; +struct ProcessorComponent; + +struct ComponentAdapter : v3_funknown, v3_plugin_base +{ + // needs atomic refcount, starts at 1 + + ComponentAdapter() + { + static const uint8_t* kSupportedFactories[] = { + v3_funknown_iid, + v3_plugin_base_iid, + /* + v3_component_iid, + v3_edit_controller_iid, + v3_audio_processor_iid + */ + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("ComponentAdapter::query_interface %p %p %p", self, iid, iface); + + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* factory_iid : kSupportedFactories) + { + if (v3_tuid_match(factory_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // TODO use atomic counter + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + } +}; + +struct ControllerComponent : ComponentAdapter +{ +}; + +struct ProcessorComponent : ComponentAdapter +{ +}; + +// -------------------------------------------------------------------------------------------------------------------- +// Dummy plugin to get data from + +static ScopedPointer<PluginExporter> gPluginInfo; + +static void gPluginInit() +{ + if (gPluginInfo != nullptr) + return; + + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + gPluginInfo = new PluginExporter(nullptr, nullptr); + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + dpf_tuid_class[3] = dpf_tuid_component[3] = dpf_tuid_controller[3] + = dpf_tuid_processor[3] = dpf_tuid_view[3] = gPluginInfo->getUniqueId(); +} + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_factory + +struct v3_plugin_factory_cpp : v3_funknown, v3_plugin_factory { + v3_plugin_factory_2 v2; + v3_plugin_factory_3 v3; +}; + +struct dpf_factory : v3_plugin_factory_cpp { + dpf_factory() + { + static const uint8_t* kSupportedFactories[] = { + v3_funknown_iid, + v3_plugin_factory_iid, + v3_plugin_factory_2_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* factory_iid : kSupportedFactories) + { + if (v3_tuid_match(factory_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // we only support 1 plugin per binary, so don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_factory + + get_factory_info = []V3_API(void*, struct v3_factory_info* const info) -> v3_result + { + DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); + DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo->getHomePage(), sizeof(info->url)); + DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO + return V3_OK; + }; + + num_classes = []V3_API(void*) -> int32_t + { + return 1; + }; + + get_class_info = []V3_API(void*, int32_t /*idx*/, struct v3_class_info* const info) -> v3_result + { + memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + info->cardinality = 0x7FFFFFFF; + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); + DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo->getName(), sizeof(info->name)); + return V3_OK; + }; + + create_instance = []V3_API(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) -> v3_result + { + d_stdout("%s %i %p %p %p %p", __PRETTY_FUNCTION__, __LINE__, self, class_id, iid, instance); + DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && + v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); + + *instance = nullptr; // new ComponentAdapter(); + return V3_INTERNAL_ERR; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_factory_2 + + v2.get_class_info_2 = []V3_API(void*, int32_t /*idx*/, struct v3_class_info_2 *info) -> v3_result + { + // get_class_info + memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + info->cardinality = 0x7FFFFFFF; + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); + DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo->getName(), sizeof(info->name)); + // get_class_info_2 + info->class_flags = 0; + DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO + DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); + DISTRHO_NAMESPACE::strncpy(info->version, "", sizeof(info->version)); // TODO + DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? + return V3_OK; + }; + } +}; + +static const dpf_factory dpf_factory; + +END_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- +// VST3 entry point + +DISTRHO_PLUGIN_EXPORT +const void* GetPluginFactory(void); + +const void* GetPluginFactory(void) +{ + static const struct v3_plugin_factory_2* const factory = (v3_plugin_factory_2*)&dpf_factory; + return &factory; +} + +// -------------------------------------------------------------------------------------------------------------------- +// OS specific module load + +#ifdef DISTRHO_OS_MAC +DISTRHO_PLUGIN_EXPORT bool bundleEntry(CFBundleRef); +DISTRHO_PLUGIN_EXPORT bool bundleExit(void); +bool bundleEntry(CFBundleRef) +{ + gPluginInit(); + return true; +} +bool bundleExit(void) +{ + gPluginInfo = nullptr; + return true; +} +#else +# ifdef DISTRHO_OS_WINDOWS +# define ENTRYFNNAME InitDll +# define EXITFNNAME ExitDll +# else +# define ENTRYFNNAME ModuleEntry +# define EXITFNNAME ModuleExit +# endif +DISTRHO_PLUGIN_EXPORT bool ENTRYFNNAME(void*); +DISTRHO_PLUGIN_EXPORT bool EXITFNNAME(void); +bool ENTRYFNNAME(void*) +{ + gPluginInit(); + return true; +} +bool EXITFNNAME(void) +{ + gPluginInfo = nullptr; + return true; +} +# undef ENTRYFNNAME +# undef EXITFNNAME +#endif + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h @@ -113,10 +113,3 @@ struct v3_plugin_factory_3 { static const v3_tuid v3_plugin_factory_3_iid = V3_ID(0x4555A2AB, 0xC1234E57, 0x9B122910, 0x36878931); - -#ifdef __cplusplus -struct v3_plugin_factory_cpp : v3_funknown, v3_plugin_factory { - v3_plugin_factory_2 v2; - v3_plugin_factory_3 v3; -}; -#endif diff --git a/examples/Parameters/Makefile b/examples/Parameters/Makefile @@ -46,6 +46,7 @@ TARGETS += lv2_dsp endif TARGETS += vst +TARGETS += vst3 all: $(TARGETS)