diff --git a/dpf/Makefile.base.mk b/dpf/Makefile.base.mk index 87ae06b..4c56955 100644 --- a/dpf/Makefile.base.mk +++ b/dpf/Makefile.base.mk @@ -8,6 +8,23 @@ AR ?= ar CC ?= gcc CXX ?= g++ +# Before including this file, a few variables can be set in order to tweak build behaviour: +# DEBUG=true +# NOOPT=true +# SKIP_STRIPPING=true +# NVG_DISABLE_SKIPPING_WHITESPACE=true +# NVG_FONT_TEXTURE_FLAGS=0 +# FILE_BROWSER_DISABLED=true +# WINDOWS_ICON_ID=0 +# USE_GLES2=true +# USE_GLES3=true +# USE_OPENGL3=true +# USE_NANOVG_FBO=true +# USE_NANOVG_FREETYPE=true +# STATIC_BUILD=true +# FORCE_NATIVE_AUDIO_FALLBACK=true +# SKIP_NATIVE_AUDIO_FALLBACK=true + # --------------------------------------------------------------------------------------------------------------------- # Protect against multiple inclusion @@ -16,7 +33,24 @@ ifneq ($(DPF_MAKEFILE_BASE_INCLUDED),true) DPF_MAKEFILE_BASE_INCLUDED = true # --------------------------------------------------------------------------------------------------------------------- -# Auto-detect OS if not defined +# Auto-detect target compiler if not defined + +TARGET_COMPILER = $(shell echo '\#ifdef __clang__\nclang\n\#else\ngcc\n\#endif' | $(CC) -E -P -x c - 2>/dev/null) + +ifneq ($(CLANG),true) +ifneq ($(GCC),true) + +ifneq (,$(findstring clang,$(TARGET_COMPILER))) +CLANG = true +else +GCC = true +endif + +endif +endif + +# --------------------------------------------------------------------------------------------------------------------- +# Auto-detect target OS if not defined TARGET_MACHINE := $(shell $(CC) -dumpmachine) @@ -57,7 +91,7 @@ endif # HAIKU endif # BSD # --------------------------------------------------------------------------------------------------------------------- -# Auto-detect the processor +# Auto-detect target processor TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE))) @@ -75,15 +109,21 @@ CPU_I386_OR_X86_64 = true endif ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) CPU_ARM = true -CPU_ARM_OR_AARCH64 = true +CPU_ARM_OR_ARM64 = true endif ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) CPU_ARM64 = true -CPU_ARM_OR_AARCH64 = true +CPU_ARM_OR_ARM64 = true endif ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) -CPU_AARCH64 = true -CPU_ARM_OR_AARCH64 = true +CPU_ARM64 = true +CPU_ARM_OR_ARM64 = true +endif + +ifeq ($(CPU_ARM),true) +ifneq ($(CPU_ARM64),true) +CPU_ARM32 = true +endif endif # --------------------------------------------------------------------------------------------------------------------- @@ -168,48 +208,56 @@ endif BASE_FLAGS = -Wall -Wextra -pipe -MD -MP BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections +LINK_OPTS = -fdata-sections -ffunction-sections -ifeq ($(CPU_I386_OR_X86_64),true) -BASE_OPTS += -mtune=generic -ifeq ($(WASM),true) -BASE_OPTS += -msse -msse2 -msse3 -msimd128 -else -BASE_OPTS += -msse -msse2 -mfpmath=sse -endif +ifeq ($(GCC),true) +BASE_FLAGS += -fno-gnu-unique endif -ifeq ($(CPU_ARM),true) -ifneq ($(CPU_ARM64),true) -BASE_OPTS += -mfpu=neon-vfpv4 -mfloat-abi=hard -endif +ifeq ($(SKIP_STRIPPING),true) +BASE_FLAGS += -g endif -ifeq ($(MACOS),true) - -# MacOS linker flags -LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip,-dead_strip_dylibs -ifneq ($(SKIP_STRIPPING),true) -LINK_OPTS += -Wl,-x +ifeq ($(STATIC_BUILD),true) +BASE_FLAGS += -DSTATIC_BUILD endif +ifeq ($(WINDOWS),true) +# Assume we want posix +BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS=1 -D__USE_MINGW_ANSI_STDIO=1 +# Needed for windows, see https://github.com/falkTX/Carla/issues/855 +BASE_FLAGS += -mstackrealign else +# Not needed for Windows +BASE_FLAGS += -fPIC -DPIC +endif -# Common linker flags -LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections ifeq ($(WASM),true) +BASE_OPTS += -msse -msse2 -msse3 -msimd128 +else ifeq ($(CPU_ARM32),true) +BASE_OPTS += -mfpu=neon-vfpv4 -mfloat-abi=hard +else ifeq ($(CPU_I386_OR_X86_64),true) +BASE_OPTS += -mtune=generic -msse -msse2 -mfpmath=sse +endif + +ifeq ($(MACOS),true) +LINK_OPTS += -Wl,-dead_strip,-dead_strip_dylibs +else ifeq ($(WASM),true) LINK_OPTS += -O3 -LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1 +LINK_OPTS += -Wl,--gc-sections else +LINK_OPTS += -Wl,-O1,--gc-sections LINK_OPTS += -Wl,--as-needed -ifneq ($(SKIP_STRIPPING),true) -LINK_OPTS += -Wl,--strip-all -endif endif +ifneq ($(SKIP_STRIPPING),true) +ifeq ($(MACOS),true) +LINK_OPTS += -Wl,-x +else ifeq ($(WASM),true) +LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1 +else +LINK_OPTS += -Wl,--strip-all endif - -ifeq ($(SKIP_STRIPPING),true) -BASE_FLAGS += -g endif ifeq ($(NOOPT),true) @@ -217,22 +265,6 @@ ifeq ($(NOOPT),true) BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections endif -ifneq ($(MACOS_OR_WASM_OR_WINDOWS),true) -ifneq ($(BSD),true) -BASE_FLAGS += -fno-gnu-unique -endif -endif - -ifeq ($(WINDOWS),true) -# Assume we want posix -BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS=1 -D__USE_MINGW_ANSI_STDIO=1 -# Needed for windows, see https://github.com/falkTX/Carla/issues/855 -BASE_FLAGS += -mstackrealign -else -# Not needed for Windows -BASE_FLAGS += -fPIC -DPIC -endif - ifeq ($(DEBUG),true) BASE_FLAGS += -DDEBUG -O0 -g LINK_OPTS = @@ -244,11 +276,6 @@ BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden CXXFLAGS += -fvisibility-inlines-hidden endif -ifeq ($(STATIC_BUILD),true) -BASE_FLAGS += -DSTATIC_BUILD -# LINK_OPTS += -static -endif - ifeq ($(WITH_LTO),true) BASE_FLAGS += -fno-strict-aliasing -flto LINK_OPTS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch @@ -292,7 +319,7 @@ ifeq ($(TESTBUILD),true) BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings BASE_FLAGS += -Wpointer-arith -Wabi=98 -Winit-self -Wuninitialized -Wstrict-overflow=5 # BASE_FLAGS += -Wfloat-equal -ifeq ($(CC),clang) +ifeq ($(CLANG),true) BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command BASE_FLAGS += -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-exit-time-destructors -Wno-float-equal else @@ -300,7 +327,7 @@ BASE_FLAGS += -Wcast-align -Wunsafe-loop-optimizations endif ifneq ($(MACOS),true) BASE_FLAGS += -Wmissing-declarations -Wsign-conversion -ifneq ($(CC),clang) +ifeq ($(GCC),true) BASE_FLAGS += -Wlogical-op endif endif @@ -621,11 +648,14 @@ all: print_available = @echo $(1): $(shell echo $($(1)) | grep -q true && echo Yes || echo No) features: + @echo === Detected Compiler + $(call print_available,CLANG) + $(call print_available,GCC) @echo === Detected CPU - $(call print_available,CPU_AARCH64) $(call print_available,CPU_ARM) + $(call print_available,CPU_ARM32) $(call print_available,CPU_ARM64) - $(call print_available,CPU_ARM_OR_AARCH64) + $(call print_available,CPU_ARM_OR_ARM64) $(call print_available,CPU_I386) $(call print_available,CPU_I386_OR_X86_64) @echo === Detected OS diff --git a/dpf/Makefile.plugins.mk b/dpf/Makefile.plugins.mk index 4794a08..5322be7 100644 --- a/dpf/Makefile.plugins.mk +++ b/dpf/Makefile.plugins.mk @@ -239,6 +239,18 @@ VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3 endif endif +# --------------------------------------------------------------------------------------------------------------------- +# Set CLAP filename, either single binary or inside a bundle + +ifeq ($(MACOS),true) +CLAP_CONTENTS = $(NAME).clap/Contents +CLAP_FILENAME = $(CLAP_CONTENTS)/MacOS/$(NAME) +else ifeq ($(USE_CLAP_BUNDLE),true) +CLAP_FILENAME = $(NAME).clap/$(NAME).clap +else +CLAP_FILENAME = $(NAME).clap +endif + # --------------------------------------------------------------------------------------------------------------------- # Set plugin binary file targets @@ -264,6 +276,7 @@ vst2 = $(TARGET_DIR)/$(VST2_FILENAME) ifneq ($(VST3_FILENAME),) vst3 = $(TARGET_DIR)/$(VST3_FILENAME) endif +clap = $(TARGET_DIR)/$(CLAP_FILENAME) shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) static = $(TARGET_DIR)/$(NAME).a @@ -274,6 +287,9 @@ vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Resources/empty.lproj vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Info.plist vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/PkgInfo vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Resources/empty.lproj +clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Info.plist +clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/PkgInfo +clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Resources/empty.lproj endif ifneq ($(HAVE_DGL),true) @@ -298,6 +314,7 @@ SYMBOLS_LV2UI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2-ui.exp SYMBOLS_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp SYMBOLS_VST2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst2.exp SYMBOLS_VST3 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst3.exp +SYMBOLS_CLAP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/clap.exp SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp else ifeq ($(WASM),true) SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" @@ -307,6 +324,7 @@ SYMBOLS_LV2UI = -sEXPORTED_FUNCTIONS="['lv2ui_descriptor']" SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']" SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']" SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']" +SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']" SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" else ifeq ($(WINDOWS),true) SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def @@ -316,6 +334,7 @@ SYMBOLS_LV2UI = $(DPF_PATH)/utils/symbols/lv2-ui.def SYMBOLS_LV2 = $(DPF_PATH)/utils/symbols/lv2.def SYMBOLS_VST2 = $(DPF_PATH)/utils/symbols/vst2.def SYMBOLS_VST3 = $(DPF_PATH)/utils/symbols/vst3.def +SYMBOLS_CLAP = $(DPF_PATH)/utils/symbols/clap.def SYMBOLS_SHARED = $(DPF_PATH)/utils/symbols/shared.def else ifneq ($(DEBUG),true) SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version @@ -325,6 +344,7 @@ SYMBOLS_LV2UI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2-ui.version SYMBOLS_LV2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2.version SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version SYMBOLS_VST3 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst3.version +SYMBOLS_CLAP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/clap.version SYMBOLS_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version endif @@ -521,6 +541,28 @@ endif @echo "Creating VST3 plugin for $(NAME)" $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ +# --------------------------------------------------------------------------------------------------------------------- +# CLAP + +ifeq ($(HAVE_DGL),true) +ifneq ($(HAIKU),true) +ifneq ($(WASM),true) +CLAP_LIBS = -lpthread +endif +endif +endif + +clap: $(clap) $(clapfiles) + +ifeq ($(HAVE_DGL),true) +$(clap): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.o $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.o $(DGL_LIB) +else +$(clap): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.o +endif + -@mkdir -p $(shell dirname $@) + @echo "Creating CLAP plugin for $(NAME)" + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(CLAP_LIBS) $(SHARED) $(SYMBOLS_CLAP) -o $@ + # --------------------------------------------------------------------------------------------------------------------- # Shared @@ -557,19 +599,23 @@ $(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/I -@mkdir -p $(shell dirname $@) $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ -$(TARGET_DIR)/%.vst/Contents/Info.plist: $(DPF_PATH)/utils/plugin.vst/Contents/Info.plist +$(TARGET_DIR)/%.vst/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist + -@mkdir -p $(shell dirname $@) + $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ + +$(TARGET_DIR)/%.vst3/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist -@mkdir -p $(shell dirname $@) $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ -$(TARGET_DIR)/%.vst3/Contents/Info.plist: $(DPF_PATH)/utils/plugin.vst/Contents/Info.plist +$(TARGET_DIR)/%.clap/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist -@mkdir -p $(shell dirname $@) $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ -$(TARGET_DIR)/%/Contents/PkgInfo: $(DPF_PATH)/utils/plugin.vst/Contents/PkgInfo +$(TARGET_DIR)/%/Contents/PkgInfo: $(DPF_PATH)/utils/plugin.bundle/Contents/PkgInfo -@mkdir -p $(shell dirname $@) $(SILENT)cp $< $@ -$(TARGET_DIR)/%/Resources/empty.lproj: $(DPF_PATH)/utils/plugin.vst/Contents/Resources/empty.lproj +$(TARGET_DIR)/%/Resources/empty.lproj: $(DPF_PATH)/utils/plugin.bundle/Contents/Resources/empty.lproj -@mkdir -p $(shell dirname $@) $(SILENT)cp $< $@ @@ -586,6 +632,7 @@ endif -include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d +-include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d @@ -594,6 +641,7 @@ endif -include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d +-include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d diff --git a/dpf/cmake/DPF-plugin.cmake b/dpf/cmake/DPF-plugin.cmake index 858c49c..e0f8edf 100644 --- a/dpf/cmake/DPF-plugin.cmake +++ b/dpf/cmake/DPF-plugin.cmake @@ -336,9 +336,9 @@ function(dpf__build_vst2 NAME DGL_LIBRARY) OUTPUT_NAME "${NAME}" SUFFIX "") set(INFO_PLIST_PROJECT_NAME "${NAME}") - configure_file("${DPF_ROOT_DIR}/utils/plugin.vst/Contents/Info.plist" + configure_file("${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/Info.plist" "${PROJECT_BINARY_DIR}/bin/${NAME}.vst/Contents/Info.plist" @ONLY) - file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst/Contents/PkgInfo" + file(COPY "${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/PkgInfo" DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst/Contents") endif() endfunction() @@ -425,9 +425,9 @@ function(dpf__build_vst3 NAME DGL_LIBRARY) if(APPLE) # Uses the same macOS bundle template as VST2 set(INFO_PLIST_PROJECT_NAME "${NAME}") - configure_file("${DPF_ROOT_DIR}/utils/plugin.vst/Contents/Info.plist" + configure_file("${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/Info.plist" "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/Info.plist" @ONLY) - file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst/Contents/PkgInfo" + file(COPY "${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/PkgInfo" DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents") endif() endfunction() diff --git a/dpf/dgl/Color.hpp b/dpf/dgl/Color.hpp index fbce9cc..5153473 100644 --- a/dpf/dgl/Color.hpp +++ b/dpf/dgl/Color.hpp @@ -68,7 +68,7 @@ struct Color { /** Create a new color based on this one but with a different alpha value. */ - Color withAlpha(float alpha) noexcept; + Color withAlpha(float alpha) const noexcept; /** Create a color specified by hue, saturation and lightness. diff --git a/dpf/dgl/EventHandlers.hpp b/dpf/dgl/EventHandlers.hpp index a460440..f56b5a6 100644 --- a/dpf/dgl/EventHandlers.hpp +++ b/dpf/dgl/EventHandlers.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -94,7 +94,8 @@ class KnobEventHandler public: enum Orientation { Horizontal, - Vertical + Vertical, + Both }; // NOTE hover not implemented yet @@ -112,6 +113,7 @@ public: virtual void knobDragStarted(SubWidget* widget) = 0; virtual void knobDragFinished(SubWidget* widget) = 0; virtual void knobValueChanged(SubWidget* widget, float value) = 0; + virtual void knobDoubleClicked(SubWidget*) {}; }; explicit KnobEventHandler(SubWidget* self); @@ -119,6 +121,9 @@ public: KnobEventHandler& operator=(const KnobEventHandler& other); virtual ~KnobEventHandler(); + // if setStep(1) has been called before, this returns true + bool isInteger() const noexcept; + // returns raw value, is assumed to be scaled if using log float getValue() const noexcept; @@ -139,12 +144,15 @@ public: void setUsingLogScale(bool yesNo) noexcept; Orientation getOrientation() const noexcept; - void setOrientation(const Orientation orientation) noexcept; + void setOrientation(Orientation orientation) noexcept; void setCallback(Callback* callback) noexcept; - bool mouseEvent(const Widget::MouseEvent& ev); - bool motionEvent(const Widget::MotionEvent& ev); + // default 200, higher means slower + void setMouseDeceleration(float accel) noexcept; + + bool mouseEvent(const Widget::MouseEvent& ev, double scaleFactor = 1.0); + bool motionEvent(const Widget::MotionEvent& ev, double scaleFactor = 1.0); bool scrollEvent(const Widget::ScrollEvent& ev); protected: @@ -168,6 +176,21 @@ private: // -------------------------------------------------------------------------------------------------------------------- +class SliderEventHandler +{ +public: + explicit SliderEventHandler(SubWidget* self); + virtual ~SliderEventHandler(); + +private: + struct PrivateData; + PrivateData* const pData; + + DISTRHO_LEAK_DETECTOR(SliderEventHandler) +}; + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL #endif // DGL_EVENT_HANDLERS_HPP_INCLUDED diff --git a/dpf/dgl/Layout.hpp b/dpf/dgl/Layout.hpp new file mode 100644 index 0000000..e4cfc3d --- /dev/null +++ b/dpf/dgl/Layout.hpp @@ -0,0 +1,71 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * 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. + */ + +#ifndef DGL_LAYOUT_HPP_INCLUDED +#define DGL_LAYOUT_HPP_INCLUDED + +#include "Geometry.hpp" + +#include + +START_NAMESPACE_DGL + +class SubWidget; + +// -------------------------------------------------------------------------------------------------------------------- + +// NOTE: under development, API to be finalized and documented soon + +enum SizeHint { + Expanding, + Fixed +}; + +struct SubWidgetWithSizeHint { + SubWidget* widget; + SizeHint sizeHint; +}; + +template +struct Layout +{ + std::list widgets; + uint setAbsolutePos(int x, int y, uint padding); + void setSize(uint size, uint padding); +}; + +typedef Layout HorizontalLayout; +typedef Layout VerticalLayout; + +struct HorizontallyStackedVerticalLayout +{ + std::list items; + Size adjustSize(uint padding); // TODO + void setAbsolutePos(int x, int y, uint padding); +}; + +struct VerticallyStackedHorizontalLayout +{ + std::list items; + Size adjustSize(uint padding); + void setAbsolutePos(int x, int y, uint padding); +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +#endif // DGL_LAYOUT_HPP_INCLUDED diff --git a/dpf/dgl/Makefile b/dpf/dgl/Makefile index be92632..c756bf0 100644 --- a/dpf/dgl/Makefile +++ b/dpf/dgl/Makefile @@ -34,6 +34,7 @@ OBJS_common = \ ../build/dgl/Geometry.cpp.o \ ../build/dgl/ImageBase.cpp.o \ ../build/dgl/ImageBaseWidgets.cpp.o \ + ../build/dgl/Layout.cpp.o \ ../build/dgl/Resources.cpp.o \ ../build/dgl/SubWidget.cpp.o \ ../build/dgl/SubWidgetPrivateData.cpp.o \ diff --git a/dpf/dgl/NanoVG.hpp b/dpf/dgl/NanoVG.hpp index bae8f27..ebafdb9 100644 --- a/dpf/dgl/NanoVG.hpp +++ b/dpf/dgl/NanoVG.hpp @@ -591,14 +591,14 @@ public: /** Creates image by loading it from the specified chunk of memory. */ - NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, ImageFlags imageFlags); + NanoImage::Handle createImageFromMemory(const uchar* data, uint dataSize, ImageFlags imageFlags); /** Creates image by loading it from the specified chunk of memory. Overloaded function for convenience. @see ImageFlags */ - NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, int imageFlags); + NanoImage::Handle createImageFromMemory(const uchar* data, uint dataSize, int imageFlags); /** Creates image from specified raw format image data. diff --git a/dpf/dgl/SubWidget.hpp b/dpf/dgl/SubWidget.hpp index eefb4c5..2811984 100644 --- a/dpf/dgl/SubWidget.hpp +++ b/dpf/dgl/SubWidget.hpp @@ -137,6 +137,12 @@ public: */ void repaint() noexcept override; + /** + Pushes this widget to the "bottom" of the parent widget. + Makes the widget behave as if it was the first to be registered on the parent widget, thus being "on bottom". + */ + virtual void toBottom(); + /** Bring this widget to the "front" of the parent widget. Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front". diff --git a/dpf/dgl/Widget.hpp b/dpf/dgl/Widget.hpp index 38a49b1..ab20d77 100644 --- a/dpf/dgl/Widget.hpp +++ b/dpf/dgl/Widget.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -19,6 +19,8 @@ #include "Geometry.hpp" +#include + START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- @@ -62,7 +64,7 @@ public: uint mod; /** Event flags. @see EventFlag */ uint flags; - /** Event timestamp (if any). */ + /** Event timestamp in milliseconds (if any). */ uint time; /** Constructor for default/null values */ @@ -331,16 +333,33 @@ public: /** Get the Id associated with this widget. + Returns 0 by default. @see setId */ uint getId() const noexcept; + /** + Get the name associated with this widget. + This is complately optional, mostly useful for debugging purposes. + Returns an empty string by default. + @see setName + */ + const char* getName() const noexcept; + /** Set an Id to be associated with this widget. @see getId */ void setId(uint id) noexcept; + /** + Set a name to be associated with this widget. + This is complately optional, only useful for debugging purposes. + @note name must not be null + @see getName + */ + void setName(const char* name) noexcept; + /** Get the application associated with this widget's window. This is the same as calling `getTopLevelWidget()->getApp()`. @@ -367,6 +386,11 @@ public: */ TopLevelWidget* getTopLevelWidget() const noexcept; + /** + Get list of children (a subwidgets) that belong to this widget. + */ + std::list getChildren() const noexcept; + /** Request repaint of this widget's area to the window this widget belongs to. On the raw Widget class this function does nothing. diff --git a/dpf/dgl/src/Color.cpp b/dpf/dgl/src/Color.cpp index 10382fb..454ada3 100644 --- a/dpf/dgl/src/Color.cpp +++ b/dpf/dgl/src/Color.cpp @@ -114,7 +114,7 @@ Color::Color(const Color& color1, const Color& color2, const float u) noexcept interpolate(color2, u); } -Color Color::withAlpha(const float alpha2) noexcept +Color Color::withAlpha(const float alpha2) const noexcept { Color color(*this); color.alpha = alpha2; diff --git a/dpf/dgl/src/EventHandlers.cpp b/dpf/dgl/src/EventHandlers.cpp index 64f81ca..2b75b5f 100644 --- a/dpf/dgl/src/EventHandlers.cpp +++ b/dpf/dgl/src/EventHandlers.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -274,6 +274,7 @@ struct KnobEventHandler::PrivateData { SubWidget* const widget; KnobEventHandler::Callback* callback; + float accel; float minimum; float maximum; float step; @@ -287,13 +288,15 @@ struct KnobEventHandler::PrivateData { double lastX; double lastY; + uint lastClickTime; PrivateData(KnobEventHandler* const s, SubWidget* const w) : self(s), widget(w), callback(nullptr), - minimum(0.0f), - maximum(1.0f), + accel(200.f), + minimum(0.f), + maximum(1.f), step(0.0f), value(0.5f), valueDef(value), @@ -303,12 +306,14 @@ struct KnobEventHandler::PrivateData { orientation(Vertical), state(kKnobStateDefault), lastX(0.0), - lastY(0.0) {} + lastY(0.0), + lastClickTime(0) {} PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other) : self(s), widget(w), callback(other->callback), + accel(other->accel), minimum(other->minimum), maximum(other->maximum), step(other->step), @@ -320,11 +325,13 @@ struct KnobEventHandler::PrivateData { orientation(other->orientation), state(kKnobStateDefault), lastX(0.0), - lastY(0.0) {} + lastY(0.0), + lastClickTime(0) {} void assignFrom(PrivateData* const other) { callback = other->callback; + accel = other->accel; minimum = other->minimum; maximum = other->maximum; step = other->step; @@ -337,6 +344,7 @@ struct KnobEventHandler::PrivateData { state = kKnobStateDefault; lastX = 0.0; lastY = 0.0; + lastClickTime = 0; } inline float logscale(const float v) const @@ -353,7 +361,7 @@ struct KnobEventHandler::PrivateData { return std::log(v/a)/b; } - bool mouseEvent(const Widget::MouseEvent& ev) + bool mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor) { if (ev.button != 1) return false; @@ -370,9 +378,21 @@ struct KnobEventHandler::PrivateData { return true; } + lastX = ev.pos.getX() / scaleFactor; + lastY = ev.pos.getY() / scaleFactor; + + if (lastClickTime > 0 && ev.time > lastClickTime && ev.time - lastClickTime <= 300) + { + lastClickTime = 0; + + if (callback != nullptr) + callback->knobDoubleClicked(widget); + + return true; + } + + lastClickTime = ev.time; state |= kKnobStateDragging; - lastX = ev.pos.getX(); - lastY = ev.pos.getY(); widget->repaint(); if (callback != nullptr) @@ -394,62 +414,87 @@ struct KnobEventHandler::PrivateData { return false; } - bool motionEvent(const Widget::MotionEvent& ev) + bool motionEvent(const Widget::MotionEvent& ev, const double scaleFactor) { if ((state & kKnobStateDragging) == 0x0) return false; - bool doVal = false; - float d, value2 = 0.0f; + float movDiff; - if (orientation == Horizontal) + switch (orientation) { - if (const double movX = ev.pos.getX() - lastX) + case Horizontal: + movDiff = ev.pos.getX() / scaleFactor - lastX; + break; + case Vertical: + movDiff = lastY - ev.pos.getY() / scaleFactor; + break; + case Both: { - d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movX)); - doVal = true; - } - } - else if (orientation == Vertical) - { - if (const double movY = lastY - ev.pos.getY()) - { - d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; - value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movY)); - doVal = true; + const float movDiffX = ev.pos.getX() / scaleFactor - lastX; + const float movDiffY = lastY - ev.pos.getY() / scaleFactor; + movDiff = std::abs(movDiffX) > std::abs(movDiffY) ? movDiffX : movDiffY; } + break; + default: + return false; } - if (! doVal) + if (d_isZero(movDiff)) return false; + const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel; + valueTmp += (maximum - minimum) / divisor * movDiff; + if (usingLog) - value2 = logscale(value2); + valueTmp = logscale(valueTmp); - if (value2 < minimum) + float value2; + bool valueChanged = false; + + if (valueTmp < minimum) { valueTmp = value2 = minimum; + valueChanged = true; } - else if (value2 > maximum) + else if (valueTmp > maximum) { valueTmp = value2 = maximum; + valueChanged = true; } else { - valueTmp = value2; - if (d_isNotZero(step)) { - const float rest = std::fmod(value2, step); - value2 -= rest + (rest > step/2.0f ? step : 0.0f); + if (std::abs(valueTmp - value) >= step) + { + const float rest = std::fmod(valueTmp, step); + valueChanged = true; + value2 = valueTmp - rest; + + if (rest < 0 && rest < step * -0.5f) + value2 -= step; + else if (rest > 0 && rest > step * 0.5f) + value2 += step; + + if (value2 < minimum) + value2 = minimum; + else if (value2 > maximum) + value2 = maximum; + } + } + else + { + value2 = valueTmp; + valueChanged = true; } } - setValue(value2, true); + if (valueChanged) + setValue(value2, true); - lastX = ev.pos.getX(); - lastY = ev.pos.getY(); + lastX = ev.pos.getX() / scaleFactor; + lastY = ev.pos.getY() / scaleFactor; return true; } @@ -460,7 +505,7 @@ struct KnobEventHandler::PrivateData { return false; const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f; - const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; + const float d = (ev.mod & kModifierControl) ? accel * 10.f : accel; float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + ((maximum - minimum) / d * 10.f * dir); @@ -553,6 +598,11 @@ KnobEventHandler::~KnobEventHandler() delete pData; } +bool KnobEventHandler::isInteger() const noexcept +{ + return d_isEqual(pData->step, 1.f); +} + float KnobEventHandler::getValue() const noexcept { return pData->value; @@ -596,9 +646,6 @@ KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept void KnobEventHandler::setOrientation(const Orientation orientation) noexcept { - if (pData->orientation == orientation) - return; - pData->orientation = orientation; } @@ -607,14 +654,19 @@ void KnobEventHandler::setCallback(Callback* const callback) noexcept pData->callback = callback; } -bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev) +void KnobEventHandler::setMouseDeceleration(float accel) noexcept { - return pData->mouseEvent(ev); + pData->accel = accel; } -bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev) +bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor) { - return pData->motionEvent(ev); + return pData->mouseEvent(ev, scaleFactor); +} + +bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev, const double scaleFactor) +{ + return pData->motionEvent(ev, scaleFactor); } bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev) diff --git a/dpf/dgl/src/ImageBaseWidgets.cpp b/dpf/dgl/src/ImageBaseWidgets.cpp index bb16f0c..eadc57f 100644 --- a/dpf/dgl/src/ImageBaseWidgets.cpp +++ b/dpf/dgl/src/ImageBaseWidgets.cpp @@ -395,7 +395,7 @@ bool ImageBaseKnob::onMouse(const MouseEvent& ev) { if (SubWidget::onMouse(ev)) return true; - return KnobEventHandler::mouseEvent(ev); + return KnobEventHandler::mouseEvent(ev, getTopLevelWidget()->getScaleFactor()); } template @@ -403,7 +403,7 @@ bool ImageBaseKnob::onMotion(const MotionEvent& ev) { if (SubWidget::onMotion(ev)) return true; - return KnobEventHandler::motionEvent(ev); + return KnobEventHandler::motionEvent(ev, getTopLevelWidget()->getScaleFactor()); } template diff --git a/dpf/dgl/src/Layout.cpp b/dpf/dgl/src/Layout.cpp new file mode 100644 index 0000000..ea1dc81 --- /dev/null +++ b/dpf/dgl/src/Layout.cpp @@ -0,0 +1,201 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * 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 "../Layout.hpp" +#include "../SubWidget.hpp" + +START_NAMESPACE_DGL + +typedef std::list::iterator SubWidgetWithSizeHintIterator; +typedef std::list::iterator HorizontalLayoutIterator; +typedef std::list::iterator VerticalLayoutIterator; + +// -------------------------------------------------------------------------------------------------------------------- + +template<> // horizontal +uint Layout::setAbsolutePos(int x, const int y, const uint padding) +{ + uint maxHeight = 0; + + for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) + { + SubWidgetWithSizeHint& s(*it); + maxHeight = std::max(maxHeight, s.widget->getHeight()); + s.widget->setAbsolutePos(x, y); + x += s.widget->getWidth(); + x += padding; + } + + return maxHeight; +} + +template<> // vertical +uint Layout::setAbsolutePos(const int x, int y, const uint padding) +{ + uint maxWidth = 0; + + for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) + { + SubWidgetWithSizeHint& s(*it); + maxWidth = std::max(maxWidth, s.widget->getWidth()); + s.widget->setAbsolutePos(x, y); + y += s.widget->getHeight(); + y += padding; + } + + return maxWidth; +} + +template<> // horizontal +void Layout::setSize(const uint width, const uint padding) +{ + uint maxHeight = 0; + uint nonFixedWidth = width; + uint numDynamiclySizedWidgets = 0; + + for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) + { + SubWidgetWithSizeHint& s(*it); + maxHeight = std::max(maxHeight, s.widget->getHeight()); + + if (s.sizeHint == Fixed) + nonFixedWidth -= s.widget->getWidth(); + else + ++numDynamiclySizedWidgets; + } + + if (const size_t numWidgets = widgets.size()) + nonFixedWidth -= padding * (numWidgets - 1); + + const uint widthPerWidget = numDynamiclySizedWidgets != 0 ? nonFixedWidth / numDynamiclySizedWidgets : 0; + + for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) + { + SubWidgetWithSizeHint& s(*it); + if (s.sizeHint != Fixed) + s.widget->setSize(widthPerWidget, maxHeight); + else + s.widget->setHeight(maxHeight); + } +} + +template<> // vertical +void Layout::setSize(const uint height, const uint padding) +{ + uint biggestWidth = 0; + uint nonFixedHeight = height; + uint numDynamiclySizedWidgets = 0; + + for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) + { + SubWidgetWithSizeHint& s(*it); + biggestWidth = std::max(biggestWidth, s.widget->getWidth()); + + if (s.sizeHint == Fixed) + nonFixedHeight -= s.widget->getHeight(); + else + ++numDynamiclySizedWidgets; + } + + if (const size_t numWidgets = widgets.size()) + nonFixedHeight -= padding * (numWidgets - 1); + + const uint heightPerWidget = numDynamiclySizedWidgets != 0 ? nonFixedHeight / numDynamiclySizedWidgets : 0; + + for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it) + { + SubWidgetWithSizeHint& s(*it); + if (s.sizeHint != Fixed) + s.widget->setSize(biggestWidth, heightPerWidget); + else + s.widget->setWidth(biggestWidth); + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +/* TODO +void HorizontallyStackedVerticalLayout::adjustSize(const uint padding) +{ +} +*/ + +void HorizontallyStackedVerticalLayout::setAbsolutePos(int x, const int y, const uint padding) +{ + for (VerticalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) + { + VerticalLayout* l(*it); + x += l->setAbsolutePos(x, y, padding); + x += padding; + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +Size VerticallyStackedHorizontalLayout::adjustSize(const uint padding) +{ + uint biggestWidth = 0; + uint totalHeight = 0; + + // iterate all widgets to find which one is the biggest (horizontally) + for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) + { + HorizontalLayout* const l(*it); + uint width = 0; + uint height = 0; + + for (SubWidgetWithSizeHintIterator it=l->widgets.begin(), end=l->widgets.end(); it != end; ++it) + { + SubWidgetWithSizeHint& s(*it); + + if (width != 0) + width += padding; + + width += s.widget->getWidth(); + height = std::max(height, s.widget->getHeight()); + } + + biggestWidth = std::max(biggestWidth, width); + + if (totalHeight != 0) + totalHeight += padding; + + totalHeight += height; + } + + // now make all horizontal lines the same width + for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) + { + HorizontalLayout* const l(*it); + l->setSize(biggestWidth, padding); + } + + return Size(biggestWidth, totalHeight); +} + +void VerticallyStackedHorizontalLayout::setAbsolutePos(const int x, int y, const uint padding) +{ + for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it) + { + HorizontalLayout* l(*it); + y += l->setAbsolutePos(x, y, padding); + y += padding; + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL diff --git a/dpf/dgl/src/NanoVG.cpp b/dpf/dgl/src/NanoVG.cpp index 42c0fcd..f5f0b9f 100644 --- a/dpf/dgl/src/NanoVG.cpp +++ b/dpf/dgl/src/NanoVG.cpp @@ -664,18 +664,18 @@ NanoImage::Handle NanoVG::createImageFromFile(const char* filename, int imageFla return NanoImage::Handle(fContext, nvgCreateImage(fContext, filename, imageFlags)); } -NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, ImageFlags imageFlags) +NanoImage::Handle NanoVG::createImageFromMemory(const uchar* data, uint dataSize, ImageFlags imageFlags) { return createImageFromMemory(data, dataSize, static_cast(imageFlags)); } -NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, int imageFlags) +NanoImage::Handle NanoVG::createImageFromMemory(const uchar* data, uint dataSize, int imageFlags) { if (fContext == nullptr) return NanoImage::Handle(); DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); DISTRHO_SAFE_ASSERT_RETURN(dataSize > 0, NanoImage::Handle()); - return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast(dataSize))); + return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data, static_cast(dataSize))); } NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, diff --git a/dpf/dgl/src/SubWidget.cpp b/dpf/dgl/src/SubWidget.cpp index d0f3891..af1274a 100644 --- a/dpf/dgl/src/SubWidget.cpp +++ b/dpf/dgl/src/SubWidget.cpp @@ -139,12 +139,21 @@ void SubWidget::repaint() noexcept if (TopLevelWidget* const topw = getTopLevelWidget()) { if (pData->needsFullViewportForDrawing) - topw->repaint(); + // repaint is virtual and we want precisely the top-level specific implementation, not any higher level + topw->TopLevelWidget::repaint(); else topw->repaint(getConstrainedAbsoluteArea()); } } +void SubWidget::toBottom() +{ + std::list& subwidgets(pData->parentWidget->pData->subWidgets); + + subwidgets.remove(this); + subwidgets.insert(subwidgets.begin(), this); +} + void SubWidget::toFront() { std::list& subwidgets(pData->parentWidget->pData->subWidgets); diff --git a/dpf/dgl/src/Widget.cpp b/dpf/dgl/src/Widget.cpp index 8aaa90d..ced7c4a 100644 --- a/dpf/dgl/src/Widget.cpp +++ b/dpf/dgl/src/Widget.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -148,6 +148,11 @@ TopLevelWidget* Widget::getTopLevelWidget() const noexcept return pData->topLevelWidget; } +std::list Widget::getChildren() const noexcept +{ + return pData->subWidgets; +} + void Widget::repaint() noexcept { } @@ -157,11 +162,22 @@ uint Widget::getId() const noexcept return pData->id; } +const char* Widget::getName() const noexcept +{ + return pData->name != nullptr ? pData->name : ""; +} + void Widget::setId(uint id) noexcept { pData->id = id; } +void Widget::setName(const char* const name) noexcept +{ + std::free(pData->name); + pData->name = strdup(name); +} + bool Widget::onKeyboard(const KeyboardEvent& ev) { return pData->giveKeyboardEventForSubWidgets(ev); diff --git a/dpf/dgl/src/WidgetPrivateData.cpp b/dpf/dgl/src/WidgetPrivateData.cpp index 59b8790..e05073c 100644 --- a/dpf/dgl/src/WidgetPrivateData.cpp +++ b/dpf/dgl/src/WidgetPrivateData.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -33,6 +33,7 @@ Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) topLevelWidget(tlw), parentWidget(nullptr), id(0), + name(nullptr), needsScaling(false), visible(true), size(0, 0), @@ -43,6 +44,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw) topLevelWidget(findTopLevelWidget(pw)), parentWidget(pw), id(0), + name(nullptr), needsScaling(false), visible(true), size(0, 0), @@ -51,6 +53,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw) Widget::PrivateData::~PrivateData() { subWidgets.clear(); + std::free(name); } void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor) diff --git a/dpf/dgl/src/WidgetPrivateData.hpp b/dpf/dgl/src/WidgetPrivateData.hpp index 15b8409..9cc157f 100644 --- a/dpf/dgl/src/WidgetPrivateData.hpp +++ b/dpf/dgl/src/WidgetPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -30,6 +30,7 @@ struct Widget::PrivateData { TopLevelWidget* const topLevelWidget; Widget* const parentWidget; uint id; + char* name; bool needsScaling; bool visible; Size size; diff --git a/dpf/dgl/src/nanovg/nanovg.c b/dpf/dgl/src/nanovg/nanovg.c index c92af25..988e7ca 100644 --- a/dpf/dgl/src/nanovg/nanovg.c +++ b/dpf/dgl/src/nanovg/nanovg.c @@ -23,6 +23,11 @@ #include "nanovg.h" #define FONTSTASH_IMPLEMENTATION +#define stbtt_fontinfo dpf_nvg_stbtt_fontinfo +#define stbrp_context dpf_nvg_stbrp_context +#define stbrp_rect dpf_nvg_stbrp_rect +#define stbrp_node dpf_nvg_stbrp_node +#define stbrp_coord dpf_nvg_stbrp_coord #include "fontstash.h" #ifndef NVG_NO_STB @@ -869,7 +874,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) return image; } -int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata) +int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata) { int w, h, n, image; unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); diff --git a/dpf/dgl/src/nanovg/nanovg.h b/dpf/dgl/src/nanovg/nanovg.h index 89b4dd8..027e828 100644 --- a/dpf/dgl/src/nanovg/nanovg.h +++ b/dpf/dgl/src/nanovg/nanovg.h @@ -385,7 +385,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); // Creates image by loading it from the specified chunk of memory. // Returns handle to the image. -int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); +int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata); // Creates image from specified image data and texture format. // Returns handle to the image. diff --git a/dpf/dgl/src/pugl-upstream/.clang-tidy b/dpf/dgl/src/pugl-upstream/.clang-tidy deleted file mode 100644 index 1e40901..0000000 --- a/dpf/dgl/src/pugl-upstream/.clang-tidy +++ /dev/null @@ -1,4 +0,0 @@ -Checks: > - *, -FormatStyle: file -WarningsAsErrors: '*' diff --git a/dpf/dgl/src/pugl-upstream/src/.clang-tidy b/dpf/dgl/src/pugl-upstream/src/.clang-tidy index 981e098..48a7467 100644 --- a/dpf/dgl/src/pugl-upstream/src/.clang-tidy +++ b/dpf/dgl/src/pugl-upstream/src/.clang-tidy @@ -8,7 +8,6 @@ Checks: > -hicpp-signed-bitwise, -llvm-header-guard, -llvmlibc-*, - -performance-no-int-to-ptr, -readability-function-cognitive-complexity, FormatStyle: file HeaderFilterRegex: 'pugl/.*' diff --git a/dpf/dgl/src/pugl-upstream/src/mac.m b/dpf/dgl/src/pugl-upstream/src/mac.m index 39eff67..556102a 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac.m +++ b/dpf/dgl/src/pugl-upstream/src/mac.m @@ -1378,7 +1378,7 @@ puglProcessEvents(PuglView* view) double puglGetTime(const PuglWorld* world) { - return (mach_absolute_time() / 1e9) - world->startTime; + return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9) - world->startTime; } PuglStatus diff --git a/dpf/distrho/DistrhoInfo.hpp b/dpf/distrho/DistrhoInfo.hpp index 5eaf687..d1b8bda 100644 --- a/dpf/distrho/DistrhoInfo.hpp +++ b/dpf/distrho/DistrhoInfo.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -54,6 +54,9 @@ START_NAMESPACE_DISTRHO Let's begin with some examples.@n Here is one of a stereo audio plugin that simply mutes the host output: @code + /* DPF plugin include */ + #include "DistrhoPlugin.hpp" + /* Make DPF related classes available for us to use without any extra namespace references */ USE_NAMESPACE_DISTRHO; @@ -115,7 +118,7 @@ START_NAMESPACE_DISTRHO /** Get the plugin unique Id. - This value is used by LADSPA, DSSI and VST plugin formats. + This value is used by LADSPA, DSSI, VST2 and VST3 plugin formats. */ int64_t getUniqueId() const override { @@ -157,7 +160,7 @@ START_NAMESPACE_DISTRHO A plugin is nothing without parameters.@n In DPF parameters can be inputs or outputs.@n They have hints to describe how they behave plus a name and a symbol identifying them.@n - Parameters also have 'ranges' – a minimum, maximum and default value. + Parameters also have 'ranges' - a minimum, maximum and default value. Input parameters are by default "read-only": the plugin can read them but not change them. (there are exceptions and possibly a request to the host to change values, more on that below)@n @@ -349,10 +352,10 @@ START_NAMESPACE_DISTRHO } } - /** - Set the name of the program @a index. - This function will be called once, shortly after the plugin is created. - */ + /** + Set the name of the program @a index. + This function will be called once, shortly after the plugin is created. + */ void initProgramName(uint32_t index, String& programName) { // we only have one program so we can skip checking the index @@ -374,6 +377,8 @@ START_NAMESPACE_DISTRHO return fGainL; case 1; return fGainR; + default: + return 0.f; } } @@ -618,6 +623,24 @@ START_NAMESPACE_DISTRHO */ #define DISTRHO_UI_CUSTOM_WIDGET_TYPE +/** + Default UI width to use when creating initial and temporary windows.@n + Setting this macro allows to skip a temporary UI from being created in certain VST2 and VST3 hosts. + (which would normally be done for knowing the UI size before host creates a window for it) + + When this macro is defined, the companion DISTRHO_UI_DEFAULT_HEIGHT macro must be defined as well. + */ +#define DISTRHO_UI_DEFAULT_WIDTH 300 + +/** + Default UI height to use when creating initial and temporary windows.@n + Setting this macro allows to skip a temporary UI from being created in certain VST2 and VST3 hosts. + (which would normally be done for knowing the UI size before host creates a window for it) + + When this macro is defined, the companion DISTRHO_UI_DEFAULT_WIDTH macro must be defined as well. + */ +#define DISTRHO_UI_DEFAULT_HEIGHT 300 + /** Whether the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n When enabled your %UI instance will subclass @ref NanoWidget instead of @ref Widget. @@ -640,9 +663,8 @@ START_NAMESPACE_DISTRHO /** Custom LV2 category for the plugin.@n - This can be one of the following values: + This is a single string, and can be one of the following values: - - lv2:Plugin - lv2:AllpassPlugin - lv2:AmplifierPlugin - lv2:AnalyserPlugin @@ -688,7 +710,7 @@ START_NAMESPACE_DISTRHO /** Custom VST3 categories for the plugin.@n - This is a list of categories, separated by a @c |. + This is a single concatenated string of categories, separated by a @c |. Each effect category can be one of the following values: @@ -723,9 +745,70 @@ START_NAMESPACE_DISTRHO - Instrument|Synth - Instrument|Synth|Sampler - @note DPF will automatically set Mono and Stereo categories when appropriate. + And extra categories possible for any plugin type: + + - Mono + - Stereo */ -#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx" +#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx|Stereo" + +/** + Custom CLAP features for the plugin.@n + This is a list of features defined as a string array body, without the terminating @c , or nullptr. + + A top-level category can be set as feature and be one of the following values: + + - instrument + - audio-effect + - note-effect + - analyzer + + The following sub-categories can also be set: + + - synthesizer + - sampler + - drum + - drum-machine + + - filter + - phaser + - equalizer + - de-esser + - phase-vocoder + - granular + - frequency-shifter + - pitch-shifter + + - distortion + - transient-shaper + - compressor + - limiter + + - flanger + - chorus + - delay + - reverb + + - tremolo + - glitch + + - utility + - pitch-correction + - restoration + + - multi-effects + + - mixing + - mastering + + And finally the following audio capabilities can be set: + + - mono + - stereo + - surround + - ambisonic +*/ +#define DISTRHO_PLUGIN_CLAP_FEATURES "audio-effect", "stereo" /** @} */ diff --git a/dpf/distrho/DistrhoPlugin.hpp b/dpf/distrho/DistrhoPlugin.hpp index aed6051..41493e5 100644 --- a/dpf/distrho/DistrhoPlugin.hpp +++ b/dpf/distrho/DistrhoPlugin.hpp @@ -362,7 +362,7 @@ struct ParameterRanges { */ float getNormalizedValue(const float& value) const noexcept { - const float normValue((value - min) / (max - min)); + const float normValue = (value - min) / (max - min); if (normValue <= 0.0f) return 0.0f; @@ -371,6 +371,21 @@ struct ParameterRanges { return normValue; } + /** + Get a value normalized to 0.0<->1.0. + Overloaded function using double precision values. + */ + double getNormalizedValue(const double& value) const noexcept + { + const double normValue = (value - min) / (max - min); + + if (normValue <= 0.0) + return 0.0; + if (normValue >= 1.0) + return 1.0; + return normValue; + } + /** Get a value normalized to 0.0<->1.0, fixed within range. */ @@ -381,7 +396,7 @@ struct ParameterRanges { if (value >= max) return 1.0f; - const float normValue((value - min) / (max - min)); + const float normValue = (value - min) / (max - min); if (normValue <= 0.0f) return 0.0f; @@ -391,6 +406,27 @@ struct ParameterRanges { return normValue; } + /** + Get a value normalized to 0.0<->1.0, fixed within range. + Overloaded function using double precision values. + */ + double getFixedAndNormalizedValue(const double& value) const noexcept + { + if (value <= min) + return 0.0; + if (value >= max) + return 1.0; + + const double normValue = (value - min) / (max - min); + + if (normValue <= 0.0) + return 0.0; + if (normValue >= 1.0) + return 1.0; + + return normValue; + } + /** Get a proper value previously normalized to 0.0<->1.0. */ @@ -403,6 +439,20 @@ struct ParameterRanges { return value * (max - min) + min; } + + /** + Get a proper value previously normalized to 0.0<->1.0. + Overloaded function using double precision values. + */ + double getUnnormalizedValue(const double& value) const noexcept + { + if (value <= 0.0) + return min; + if (value >= 1.0) + return max; + + return value * (max - min) + min; + } }; /** @@ -706,6 +756,16 @@ struct State { @note This value is optional and only used for LV2. */ String description; + + /** + Default constructor for a null state. + */ + State() noexcept + : hints(0x0), + key(), + defaultValue(), + label(), + description() {} }; /** @@ -951,6 +1011,16 @@ public: */ bool isDummyInstance() const noexcept; + /** + Check if this plugin instance is a "selftest" one used for automated plugin tests.@n + To enable this mode build with `DPF_RUNTIME_TESTING` macro defined (i.e. set as compiler build flag), + and run the JACK/Standalone executable with "selftest" as its only and single argument. + + A few basic DSP and UI tests will run in self-test mode, with once instance having this function returning true.@n + You can use this chance to do a few tests of your own as well. + */ + bool isSelfTestInstance() const noexcept; + #if DISTRHO_PLUGIN_WANT_TIMEPOS /** Get the current host transport time position.@n diff --git a/dpf/distrho/DistrhoPluginMain.cpp b/dpf/distrho/DistrhoPluginMain.cpp index 2676cca..561da7b 100644 --- a/dpf/distrho/DistrhoPluginMain.cpp +++ b/dpf/distrho/DistrhoPluginMain.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -18,6 +18,8 @@ #if defined(DISTRHO_PLUGIN_TARGET_CARLA) # include "src/DistrhoPluginCarla.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_CLAP) +# include "src/DistrhoPluginCLAP.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_JACK) # include "src/DistrhoPluginJACK.cpp" #elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI)) diff --git a/dpf/distrho/DistrhoUIMain.cpp b/dpf/distrho/DistrhoUIMain.cpp index 656eb30..7970f01 100644 --- a/dpf/distrho/DistrhoUIMain.cpp +++ b/dpf/distrho/DistrhoUIMain.cpp @@ -17,24 +17,29 @@ #include "src/DistrhoUI.cpp" #if defined(DISTRHO_PLUGIN_TARGET_CARLA) -// nothing +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 +#elif defined(DISTRHO_PLUGIN_TARGET_CLAP) +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 #elif defined(DISTRHO_PLUGIN_TARGET_JACK) -// nothing +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 #elif defined(DISTRHO_PLUGIN_TARGET_DSSI) +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 0 # include "src/DistrhoUIDSSI.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_LV2) +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS # include "src/DistrhoUILV2.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_VST2) -// nothing +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 #elif defined(DISTRHO_PLUGIN_TARGET_VST3) +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 # include "src/DistrhoUIVST3.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC) -// nothing +# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 #else # error unsupported format #endif -#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && !defined(DISTRHO_PLUGIN_TARGET_CARLA) && !defined(DISTRHO_PLUGIN_TARGET_JACK) && !defined(DISTRHO_PLUGIN_TARGET_VST2) && !defined(DISTRHO_PLUGIN_TARGET_VST3) +#if !DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT # ifdef DISTRHO_PLUGIN_TARGET_DSSI # define DISTRHO_IS_STANDALONE 1 # else diff --git a/dpf/distrho/src/DistrhoDefines.h b/dpf/distrho/src/DistrhoDefines.h index e42eec2..452e802 100644 --- a/dpf/distrho/src/DistrhoDefines.h +++ b/dpf/distrho/src/DistrhoDefines.h @@ -211,7 +211,7 @@ private: \ #endif /* Useful macros */ -#define ARRAY_SIZE(ARRAY) sizeof(ARRAY)/sizeof(ARRAY[0]) +#define ARRAY_SIZE(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0])) /* Useful typedefs */ typedef unsigned char uchar; diff --git a/dpf/distrho/src/DistrhoPlugin.cpp b/dpf/distrho/src/DistrhoPlugin.cpp index 9c4c9b6..03902fe 100644 --- a/dpf/distrho/src/DistrhoPlugin.cpp +++ b/dpf/distrho/src/DistrhoPlugin.cpp @@ -25,6 +25,7 @@ uint32_t d_nextBufferSize = 0; double d_nextSampleRate = 0.0; const char* d_nextBundlePath = nullptr; bool d_nextPluginIsDummy = false; +bool d_nextPluginIsSelfTest = false; bool d_nextCanRequestParameterValueChanges = false; /* ------------------------------------------------------------------------------------------------------------ @@ -42,45 +43,45 @@ const PortGroupWithId PluginExporter::sFallbackPortGroup; Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) : pData(new PrivateData()) { -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 pData->audioPorts = new AudioPortWithBusId[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; -#endif + #endif -#ifdef DPF_ABORT_ON_ERROR -# define DPF_ABORT abort(); -#else -# define DPF_ABORT -#endif + #if defined(DPF_ABORT_ON_ERROR) || defined(DPF_RUNTIME_TESTING) + #define DPF_ABORT abort(); + #else + #define DPF_ABORT + #endif if (parameterCount > 0) { pData->parameterCount = parameterCount; - pData->parameters = new Parameter[parameterCount]; + pData->parameters = new Parameter[parameterCount]; } if (programCount > 0) { -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #if DISTRHO_PLUGIN_WANT_PROGRAMS pData->programCount = programCount; pData->programNames = new String[programCount]; -#else + #else d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); DPF_ABORT -#endif + #endif } if (stateCount > 0) { -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE pData->stateCount = stateCount; - pData->states = new State[stateCount]; -#else + pData->states = new State[stateCount]; + #else d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); DPF_ABORT -#endif + #endif } -#undef DPF_ABORT + #undef DPF_ABORT } Plugin::~Plugin() @@ -111,6 +112,11 @@ bool Plugin::isDummyInstance() const noexcept return pData->isDummy; } +bool Plugin::isSelfTestInstance() const noexcept +{ + return pData->isSelfTest; +} + #if DISTRHO_PLUGIN_WANT_TIMEPOS const TimePosition& Plugin::getTimePosition() const noexcept { diff --git a/dpf/distrho/src/DistrhoPluginCLAP.cpp b/dpf/distrho/src/DistrhoPluginCLAP.cpp new file mode 100644 index 0000000..64847b7 --- /dev/null +++ b/dpf/distrho/src/DistrhoPluginCLAP.cpp @@ -0,0 +1,1471 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * 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" + +#if DISTRHO_PLUGIN_HAS_UI +# include "DistrhoUIInternal.hpp" +# include "extra/Mutex.hpp" +#endif + +#include "clap/entry.h" +#include "clap/plugin-factory.h" +#include "clap/ext/audio-ports.h" +#include "clap/ext/gui.h" +#include "clap/ext/params.h" + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +struct ClapEventQueue +{ + #if DISTRHO_PLUGIN_HAS_UI + enum EventType { + kEventGestureBegin, + kEventGestureEnd, + kEventParamSet + }; + + struct Event { + EventType type; + uint32_t index; + float value; + }; + + struct Queue { + Mutex lock; + uint allocated; + uint used; + Event* events; + + Queue() + : allocated(0), + used(0), + events(nullptr) {} + + ~Queue() + { + delete[] events; + } + + void addEventFromUI(const Event& event) + { + const MutexLocker cml(lock); + + if (events == nullptr) + { + events = static_cast(std::malloc(sizeof(Event) * 8)); + allocated = 8; + } + else if (used + 1 > allocated) + { + allocated = used * 2; + events = static_cast(std::realloc(events, sizeof(Event) * allocated)); + } + + std::memcpy(&events[used++], &event, sizeof(Event)); + } + } fEventQueue; + #endif + + #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_STATE + virtual void setStateFromUI(const char* key, const char* value) = 0; + #endif + + struct CachedParameters { + uint numParams; + bool* changed; + float* values; + + CachedParameters() + : changed(nullptr), + values(nullptr) {} + + ~CachedParameters() + { + delete[] changed; + delete[] values; + } + + void setup(const uint numParameters) + { + if (numParameters == 0) + return; + + numParams = numParameters; + changed = new bool[numParameters]; + values = new float[numParameters]; + + std::memset(changed, 0, sizeof(bool)*numParameters); + std::memset(values, 0, sizeof(float)*numParameters); + } + } fCachedParameters; + + virtual ~ClapEventQueue() {} +}; + +// -------------------------------------------------------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_HAS_UI + +#if ! DISTRHO_PLUGIN_WANT_STATE +static constexpr const setStateFunc setStateCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static constexpr const sendNoteFunc sendNoteCallback = nullptr; +#endif + +/** + * CLAP UI class. + */ +class ClapUI : public DGL_NAMESPACE::IdleCallback +{ +public: + ClapUI(PluginExporter& plugin, ClapEventQueue* const eventQueue, const bool isFloating) + : fPlugin(plugin), + fPluinEventQueue(eventQueue), + fEventQueue(eventQueue->fEventQueue), + fCachedParameters(eventQueue->fCachedParameters), + fUI(), + fIsFloating(isFloating), + fCallbackRegistered(false), + fScaleFactor(0.0), + fParentWindow(0), + fTransientWindow(0) + { + } + + ~ClapUI() override + { + if (fCallbackRegistered) + { + #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + if (UIExporter* const ui = fUI.get()) + ui->removeIdleCallbackForVST3(this); + #endif + } + } + + bool setScaleFactor(const double scaleFactor) + { + if (d_isEqual(fScaleFactor, scaleFactor)) + return true; + + fScaleFactor = scaleFactor; + + if (UIExporter* const ui = fUI.get()) + ui->notifyScaleFactorChanged(scaleFactor); + + return true; + } + + bool getSize(uint32_t* const width, uint32_t* const height) const + { + if (UIExporter* const ui = fUI.get()) + { + *width = ui->getWidth(); + *height = ui->getHeight(); + return true; + } + + #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) + *width = DISTRHO_UI_DEFAULT_WIDTH; + *height = DISTRHO_UI_DEFAULT_HEIGHT; + #else + UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, + fPlugin.getInstancePointer(), fScaleFactor); + *width = tmpUI.getWidth(); + *height = tmpUI.getHeight(); + tmpUI.quit(); + #endif + + return true; + } + + bool canResize() const noexcept + { + #if DISTRHO_UI_USER_RESIZABLE + if (UIExporter* const ui = fUI.get()) + return ui->isResizable(); + #endif + return false; + } + + bool getResizeHints(clap_gui_resize_hints_t* const hints) const + { + if (canResize()) + { + uint minimumWidth, minimumHeight; + bool keepAspectRatio; + fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); + + hints->can_resize_horizontally = true; + hints->can_resize_vertically = true; + hints->preserve_aspect_ratio = keepAspectRatio; + hints->aspect_ratio_width = minimumWidth; + hints->aspect_ratio_height = minimumHeight; + + return true; + } + + hints->can_resize_horizontally = false; + hints->can_resize_vertically = false; + hints->preserve_aspect_ratio = false; + hints->aspect_ratio_width = 0; + hints->aspect_ratio_height = 0; + + return false; + } + + bool adjustSize(uint32_t* const width, uint32_t* const height) const + { + if (canResize()) + { + uint minimumWidth, minimumHeight; + bool keepAspectRatio; + fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); + + if (keepAspectRatio) + { + if (*width < 1) + *width = 1; + if (*height < 1) + *height = 1; + + const double ratio = static_cast(minimumWidth) / static_cast(minimumHeight); + const double reqRatio = static_cast(*width) / static_cast(*height); + + if (d_isNotEqual(ratio, reqRatio)) + { + // fix width + if (reqRatio > ratio) + *width = static_cast(*height * ratio + 0.5); + // fix height + else + *height = static_cast(static_cast(*width) / ratio + 0.5); + } + } + + if (minimumWidth > *width) + *width = minimumWidth; + if (minimumHeight > *height) + *height = minimumHeight; + + return true; + } + + return false; + } + + bool setSizeFromHost(const uint32_t width, const uint32_t height) + { + if (UIExporter* const ui = fUI.get()) + { + ui->setWindowSizeFromHost(width, height); + return true; + } + + return false; + } + + bool setParent(const clap_window_t* const window) + { + if (fIsFloating) + return false; + + fParentWindow = window->uptr; + + /* + if (fUI != nullptr) + createUI(); + */ + + return true; + } + + bool setTransient(const clap_window_t* const window) + { + if (! fIsFloating) + return false; + + fTransientWindow = window->uptr; + + if (UIExporter* const ui = fUI.get()) + ui->setWindowTransientWinId(window->uptr); + + return true; + } + + void suggestTitle(const char* const title) + { + if (! fIsFloating) + return; + + fWindowTitle = title; + + if (UIExporter* const ui = fUI.get()) + ui->setWindowTitle(title); + } + + bool show() + { + if (fUI == nullptr) + createUI(); + + if (fIsFloating) + fUI->setWindowVisible(true); + + #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + fUI->addIdleCallbackForVST3(this, 16); + #endif + fCallbackRegistered = true; + return true; + } + + bool hide() + { + if (UIExporter* const ui = fUI.get()) + { + ui->setWindowVisible(false); + #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + ui->removeIdleCallbackForVST3(this); + #endif + fCallbackRegistered = false; + } + + return true; + } + + // ---------------------------------------------------------------------------------------------------------------- + +private: + // Plugin and UI + PluginExporter& fPlugin; + ClapEventQueue* const fPluinEventQueue; + ClapEventQueue::Queue& fEventQueue; + ClapEventQueue::CachedParameters& fCachedParameters; + ScopedPointer fUI; + + const bool fIsFloating; + + // Temporary data + bool fCallbackRegistered; + double fScaleFactor; + uintptr_t fParentWindow; + uintptr_t fTransientWindow; + String fWindowTitle; + + // ---------------------------------------------------------------------------------------------------------------- + + void createUI() + { + DISTRHO_SAFE_ASSERT_RETURN(fUI == nullptr,); + + fUI = new UIExporter(this, + fParentWindow, + fPlugin.getSampleRate(), + editParameterCallback, + setParameterCallback, + setStateCallback, + sendNoteCallback, + setSizeCallback, + fileRequestCallback, + d_nextBundlePath, + fPlugin.getInstancePointer(), + fScaleFactor); + + if (fIsFloating) + { + if (fWindowTitle.isNotEmpty()) + fUI->setWindowTitle(fWindowTitle); + + if (fTransientWindow != 0) + fUI->setWindowTransientWinId(fTransientWindow); + } + } + + void idleCallback() override + { + #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + if (UIExporter* const ui = fUI.get()) + { + ui->idleForVST3(); + + for (uint i=0; iparameterChanged(i, fCachedParameters.values[i]); + } + } + } + #endif + } + + // ---------------------------------------------------------------------------------------------------------------- + // DPF callbacks + + void editParameter(const uint32_t rindex, const bool started) const + { + const ClapEventQueue::Event ev = { + started ? ClapEventQueue::kEventGestureBegin : ClapEventQueue::kEventGestureBegin, + rindex, 0.f + }; + fEventQueue.addEventFromUI(ev); + } + + static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) + { + static_cast(ptr)->editParameter(rindex, started); + } + + void setParameterValue(const uint32_t rindex, const float value) + { + const ClapEventQueue::Event ev = { + ClapEventQueue::kEventParamSet, + rindex, value + }; + fEventQueue.addEventFromUI(ev); + } + + static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) + { + static_cast(ptr)->setParameterValue(rindex, value); + } + + void setSizeFromPlugin(uint, uint) + { + } + + static void setSizeCallback(void* const ptr, const uint width, const uint height) + { + static_cast(ptr)->setSizeFromPlugin(width, height); + } + + #if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* const key, const char* const value) + { + fPluinEventQueue->setStateFromUI(key, value); + } + + static void setStateCallback(void* const ptr, const char* key, const char* value) + { + static_cast(ptr)->setState(key, value); + } + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + } + + static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + static_cast(ptr)->sendNote(channel, note, velocity); + } + #endif + + bool fileRequest(const char*) + { + return true; + } + + static bool fileRequestCallback(void* const ptr, const char* const key) + { + return static_cast(ptr)->fileRequest(key); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +#endif // DISTRHO_PLUGIN_HAS_UI + +#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +static constexpr const writeMidiFunc writeMidiCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST +static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_STATE +static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +/** + * CLAP plugin class. + */ +class PluginCLAP : ClapEventQueue +{ +public: + PluginCLAP(const clap_host_t* const host) + : fPlugin(this, + writeMidiCallback, + requestParameterValueChangeCallback, + updateStateValueCallback), + fHost(host), + fOutputEvents(nullptr) + { + fCachedParameters.setup(fPlugin.getParameterCount()); + } + + // ---------------------------------------------------------------------------------------------------------------- + // core + + bool init() + { + if (!clap_version_is_compatible(fHost->clap_version)) + return false; + + // TODO check host features + return true; + } + + void activate(const double sampleRate, const uint32_t maxFramesCount) + { + fPlugin.setSampleRate(sampleRate, true); + fPlugin.setBufferSize(maxFramesCount, true); + fPlugin.activate(); + } + + void deactivate() + { + fPlugin.deactivate(); + } + + bool process(const clap_process_t* const process) + { + #if DISTRHO_PLUGIN_HAS_UI + if (const clap_output_events_t* const outputEvents = process->out_events) + { + const MutexTryLocker cmtl(fEventQueue.lock); + + if (cmtl.wasLocked()) + { + // reuse the same struct for gesture and parameters, they are compatible up to where it matters + clap_event_param_value_t clapEvent = { + { 0, 0, 0, 0, CLAP_EVENT_IS_LIVE }, + 0, nullptr, 0, 0, 0, 0, 0.0 + }; + + for (uint32_t i=0; itry_push(outputEvents, &clapEvent.header); + } + + fEventQueue.used = 0; + } + } + #endif + + #if DISTRHO_PLUGIN_WANT_TIMEPOS + if (const clap_event_transport_t* const transport = process->transport) + { + fTimePosition.playing = transport->flags & CLAP_TRANSPORT_IS_PLAYING; + + fTimePosition.frame = process->steady_time >= 0 ? process->steady_time : 0; + + if (transport->flags & CLAP_TRANSPORT_HAS_TEMPO) + fTimePosition.bbt.beatsPerMinute = transport->tempo; + else + fTimePosition.bbt.beatsPerMinute = 120.0; + + // ticksPerBeat is not possible with CLAP + fTimePosition.bbt.ticksPerBeat = 1920.0; + + // TODO verify if this works or makes any sense + if ((transport->flags & (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) == (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) + { + const double ppqPos = std::abs(transport->song_pos_beats); + const int ppqPerBar = transport->tsig_num * 4 / transport->tsig_denom; + const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * transport->tsig_num; + const double rest = std::fmod(barBeats, 1.0); + + fTimePosition.bbt.valid = true; + fTimePosition.bbt.bar = static_cast(ppqPos) / ppqPerBar + 1; + fTimePosition.bbt.beat = static_cast(barBeats - rest + 0.5) + 1; + fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; + fTimePosition.bbt.beatsPerBar = transport->tsig_num; + fTimePosition.bbt.beatType = transport->tsig_denom; + + if (transport->song_pos_beats < 0.0) + { + --fTimePosition.bbt.bar; + fTimePosition.bbt.beat = transport->tsig_num - fTimePosition.bbt.beat + 1; + fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1; + } + } + else + { + fTimePosition.bbt.valid = false; + fTimePosition.bbt.bar = 1; + fTimePosition.bbt.beat = 1; + fTimePosition.bbt.tick = 0.0; + fTimePosition.bbt.beatsPerBar = 4.0f; + fTimePosition.bbt.beatType = 4.0f; + } + + fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* + fTimePosition.bbt.beatsPerBar* + (fTimePosition.bbt.bar-1); + } + else + { + fTimePosition.playing = false; + fTimePosition.frame = 0; + fTimePosition.bbt.valid = false; + fTimePosition.bbt.beatsPerMinute = 120.0; + fTimePosition.bbt.bar = 1; + fTimePosition.bbt.beat = 1; + fTimePosition.bbt.tick = 0.0; + fTimePosition.bbt.beatsPerBar = 4.f; + fTimePosition.bbt.beatType = 4.f; + fTimePosition.bbt.barStartTick = 0; + } + + fPlugin.setTimePosition(fTimePosition); + #endif + + if (const clap_input_events_t* const inputEvents = process->in_events) + { + if (const uint32_t len = inputEvents->size(inputEvents)) + { + for (uint32_t i=0; iget(inputEvents, i); + + // event->time + switch (event->type) + { + case CLAP_EVENT_NOTE_ON: + case CLAP_EVENT_NOTE_OFF: + case CLAP_EVENT_NOTE_CHOKE: + case CLAP_EVENT_NOTE_END: + case CLAP_EVENT_NOTE_EXPRESSION: + break; + case CLAP_EVENT_PARAM_VALUE: + DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value), + event->size, sizeof(clap_event_param_value)); + setParameterValueFromEvent(static_cast(static_cast(event))); + break; + case CLAP_EVENT_PARAM_MOD: + case CLAP_EVENT_PARAM_GESTURE_BEGIN: + case CLAP_EVENT_PARAM_GESTURE_END: + case CLAP_EVENT_TRANSPORT: + case CLAP_EVENT_MIDI: + case CLAP_EVENT_MIDI_SYSEX: + case CLAP_EVENT_MIDI2: + break; + } + } + } + } + + if (const uint32_t frames = process->frames_count) + { + // TODO multi-port bus stuff + DISTRHO_SAFE_ASSERT_UINT_RETURN(process->audio_inputs_count == 0 || process->audio_inputs_count == 1, + process->audio_inputs_count, false); + DISTRHO_SAFE_ASSERT_UINT_RETURN(process->audio_outputs_count == 0 || process->audio_outputs_count == 1, + process->audio_outputs_count, false); + + const float** inputs = process->audio_inputs != nullptr + ? const_cast(process->audio_inputs[0].data32) + : nullptr; + /**/ float** outputs = process->audio_outputs != nullptr + ? process->audio_outputs[0].data32 + : nullptr; + + fOutputEvents = process->out_events; + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + fPlugin.run(inputs, outputs, frames, nullptr, 0); + #else + fPlugin.run(inputs, outputs, frames); + #endif + + // TODO set last frame + flushParameters(nullptr, process->out_events); + + fOutputEvents = nullptr; + } + + return true; + } + + // ---------------------------------------------------------------------------------------------------------------- + // parameters + + uint32_t getParameterCount() const + { + return fPlugin.getParameterCount(); + } + + bool getParameterInfo(const uint32_t index, clap_param_info_t* const info) const + { + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + + if (fPlugin.getParameterDesignation(index) == kParameterDesignationBypass) + { + info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_BYPASS|CLAP_PARAM_IS_AUTOMATABLE; + std::strcpy(info->name, "Bypass"); + std::strcpy(info->module, "dpf_bypass"); + } + else + { + const uint32_t hints = fPlugin.getParameterHints(index); + const uint32_t groupId = fPlugin.getParameterGroupId(index); + + info->flags = 0; + if (hints & kParameterIsOutput) + info->flags |= CLAP_PARAM_IS_READONLY; + else if (hints & kParameterIsAutomatable) + info->flags |= CLAP_PARAM_IS_AUTOMATABLE; + + if (hints & (kParameterIsBoolean|kParameterIsInteger)) + info->flags |= CLAP_PARAM_IS_STEPPED; + + DISTRHO_NAMESPACE::strncpy(info->name, fPlugin.getParameterName(index), CLAP_NAME_SIZE); + + uint wrtn; + if (groupId != kPortGroupNone) + { + const PortGroupWithId& portGroup(fPlugin.getPortGroupById(groupId)); + strncpy(info->module, portGroup.symbol, CLAP_PATH_SIZE / 2); + info->module[CLAP_PATH_SIZE / 2] = '\0'; + wrtn = std::strlen(info->module); + info->module[wrtn++] = '/'; + } + else + { + wrtn = 0; + } + + DISTRHO_NAMESPACE::strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn); + } + + info->id = index; + info->cookie = nullptr; + info->min_value = ranges.min; + info->max_value = ranges.max; + info->default_value = ranges.def; + return true; + } + + bool getParameterValue(const clap_id param_id, double* const value) const + { + *value = fPlugin.getParameterValue(param_id); + return true; + } + + bool getParameterStringForValue(const clap_id param_id, double value, char* const display, const uint32_t size) const + { + const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(param_id)); + const uint32_t hints = fPlugin.getParameterHints(param_id); + + if (hints & kParameterIsBoolean) + { + const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f; + value = value > midRange ? ranges.max : ranges.min; + } + else if (hints & kParameterIsInteger) + { + value = std::round(value); + } + + for (uint32_t i=0; i < enumValues.count; ++i) + { + if (d_isEqual(static_cast(enumValues.values[i].value), value)) + { + DISTRHO_NAMESPACE::strncpy(display, enumValues.values[i].label, size); + return true; + } + } + + if (hints & kParameterIsInteger) + snprintf_i32(display, value, size); + else + snprintf_f32(display, value, size); + + return true; + } + + bool getParameterValueForString(const clap_id param_id, const char* const display, double* const value) const + { + const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id)); + const bool isInteger = fPlugin.isParameterInteger(param_id); + + for (uint32_t i=0; i < enumValues.count; ++i) + { + if (std::strcmp(display, enumValues.values[i].label) == 0) + { + *value = enumValues.values[i].value; + return true; + } + } + + if (isInteger) + *value = std::atoi(display); + else + *value = std::atof(display); + + return true; + } + + void setParameterValueFromEvent(const clap_event_param_value* const param) + { + fCachedParameters.values[param->param_id] = param->value; + fCachedParameters.changed[param->param_id] = true; + fPlugin.setParameterValue(param->param_id, param->value); + } + + void flushParameters(const clap_input_events_t* const in, const clap_output_events_t* const out) + { + if (const uint32_t len = in != nullptr ? in->size(in) : 0) + { + for (uint32_t i=0; iget(in, i); + + if (event->type != CLAP_EVENT_PARAM_VALUE) + continue; + + DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value), + event->size, sizeof(clap_event_param_value)); + + setParameterValueFromEvent(static_cast(static_cast(event))); + } + } + + if (out != nullptr) + { + clap_event_param_value_t clapEvent = { + { sizeof(clap_event_param_value_t), 0, 0, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_IS_LIVE }, + 0, nullptr, 0, 0, 0, 0, 0.0 + }; + + float value; + for (uint i=0; itry_push(out, &clapEvent.header); + } + } + } + } + + // ---------------------------------------------------------------------------------------------------------------- + // gui + + #if DISTRHO_PLUGIN_HAS_UI + bool createUI(const bool isFloating) + { + fUI = new ClapUI(fPlugin, this, isFloating); + return true; + } + + void destroyUI() + { + fUI = nullptr; + } + + ClapUI* getUI() const noexcept + { + return fUI.get(); + } + #endif + + #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_STATE + void setStateFromUI(const char* const key, const char* const value) override + { + fPlugin.setState(key, value); + + // TODO check if we want to save this key, and save it + } + #endif + + // ---------------------------------------------------------------------------------------------------------------- + +private: + // Plugin and UI + PluginExporter fPlugin; + #if DISTRHO_PLUGIN_HAS_UI + ScopedPointer fUI; + #endif + + // CLAP stuff + const clap_host_t* const fHost; + const clap_output_events_t* fOutputEvents; + #if DISTRHO_PLUGIN_WANT_TIMEPOS + TimePosition fTimePosition; + #endif + + // ---------------------------------------------------------------------------------------------------------------- + // DPF callbacks + + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool writeMidi(const MidiEvent&) + { + return true; + } + + static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent) + { + return static_cast(ptr)->writeMidi(midiEvent); + } + #endif + + #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST + bool requestParameterValueChange(uint32_t, float) + { + return true; + } + + static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) + { + return static_cast(ptr)->requestParameterValueChange(index, value); + } + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + bool updateState(const char*, const char*) + { + return true; + } + + static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) + { + return static_cast(ptr)->updateState(key, value); + } + #endif +}; + +// -------------------------------------------------------------------------------------------------------------------- + +static ScopedPointer sPlugin; + +// -------------------------------------------------------------------------------------------------------------------- +// plugin gui + +#if DISTRHO_PLUGIN_HAS_UI + +static const char* const kSupportedAPIs[] = { +#if defined(DISTRHO_OS_WINDOWS) + CLAP_WINDOW_API_WIN32, +#elif defined(DISTRHO_OS_MAC) + CLAP_WINDOW_API_COCOA, +#else + CLAP_WINDOW_API_X11, +#endif +}; + +// TODO DPF external UI +static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool) +{ + for (size_t i=0; i(plugin->plugin_data); + return instance->createUI(is_floating); + } + } + + return false; +} + +static void clap_gui_destroy(const clap_plugin_t* const plugin) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + instance->destroyUI(); +} + +static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setScaleFactor(scale); +} + +static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->getSize(width, height); +} + +static bool clap_gui_can_resize(const clap_plugin_t* const plugin) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->canResize(); +} + +static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->getResizeHints(hints); +} + +static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->adjustSize(width, height); +} + +static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setSizeFromHost(width, height); +} + +static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setParent(window); +} + +static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setTransient(window); +} + +static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,); + return gui->suggestTitle(title); +} + +static bool clap_gui_show(const clap_plugin_t* const plugin) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->show(); +} + +static bool clap_gui_hide(const clap_plugin_t* const plugin) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->hide(); +} + +static const clap_plugin_gui_t clap_plugin_gui = { + clap_gui_is_api_supported, + clap_gui_get_preferred_api, + clap_gui_create, + clap_gui_destroy, + clap_gui_set_scale, + clap_gui_get_size, + clap_gui_can_resize, + clap_gui_get_resize_hints, + clap_gui_adjust_size, + clap_gui_set_size, + clap_gui_set_parent, + clap_gui_set_transient, + clap_gui_suggest_title, + clap_gui_show, + clap_gui_hide +}; +#endif // DISTRHO_PLUGIN_HAS_UI + +// -------------------------------------------------------------------------------------------------------------------- +// plugin audio ports + +static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t*, const bool is_input) +{ + return (is_input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS) != 0 ? 1 : 0; +} + +static bool clap_plugin_audio_ports_get(const clap_plugin_t* /* const plugin */, + const uint32_t index, + const bool is_input, + clap_audio_port_info_t* const info) +{ + const uint32_t maxPortCount = is_input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < maxPortCount, index, maxPortCount, false); + + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + // PluginCLAP* const instance = static_cast(plugin->plugin_data); + + // TODO use groups + AudioPortWithBusId& audioPort(sPlugin->getAudioPort(is_input, index)); + + info->id = index; + DISTRHO_NAMESPACE::strncpy(info->name, audioPort.name, CLAP_NAME_SIZE); + + // TODO bus stuff + info->flags = CLAP_AUDIO_PORT_IS_MAIN; + info->channel_count = maxPortCount; + + // TODO CV + // info->port_type = audioPort.hints & kAudioPortIsCV ? CLAP_PORT_CV : nullptr; + info->port_type = nullptr; + + info->in_place_pair = DISTRHO_PLUGIN_NUM_INPUTS == DISTRHO_PLUGIN_NUM_OUTPUTS ? index : CLAP_INVALID_ID; + + return true; + #else + return false; + #endif +} + +static const clap_plugin_audio_ports_t clap_plugin_audio_ports = { + clap_plugin_audio_ports_count, + clap_plugin_audio_ports_get +}; + +// -------------------------------------------------------------------------------------------------------------------- +// plugin parameters + +static uint32_t clap_plugin_params_count(const clap_plugin_t* const plugin) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->getParameterCount(); +} + +static bool clap_plugin_params_get_info(const clap_plugin_t* const plugin, const uint32_t index, clap_param_info_t* const info) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->getParameterInfo(index, info); +} + +static bool clap_plugin_params_get_value(const clap_plugin_t* const plugin, const clap_id param_id, double* const value) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->getParameterValue(param_id, value); +} + +static bool clap_plugin_params_value_to_text(const clap_plugin_t* plugin, const clap_id param_id, const double value, char* const display, const uint32_t size) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->getParameterStringForValue(param_id, value, display, size); +} + +static bool clap_plugin_params_text_to_value(const clap_plugin_t* plugin, const clap_id param_id, const char* const display, double* const value) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->getParameterValueForString(param_id, display, value); +} + +static void clap_plugin_params_flush(const clap_plugin_t* plugin, const clap_input_events_t* in, const clap_output_events_t* out) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->flushParameters(in, out); +} + +static const clap_plugin_params_t clap_plugin_params = { + clap_plugin_params_count, + clap_plugin_params_get_info, + clap_plugin_params_get_value, + clap_plugin_params_value_to_text, + clap_plugin_params_text_to_value, + clap_plugin_params_flush +}; + +// -------------------------------------------------------------------------------------------------------------------- +// plugin + +static bool clap_plugin_init(const clap_plugin_t* const plugin) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->init(); +} + +static void clap_plugin_destroy(const clap_plugin_t* const plugin) +{ + delete static_cast(plugin->plugin_data); + std::free(const_cast(plugin)); +} + +static bool clap_plugin_activate(const clap_plugin_t* const plugin, + const double sample_rate, + uint32_t, + const uint32_t max_frames_count) +{ + d_nextBufferSize = max_frames_count; + d_nextSampleRate = sample_rate; + + PluginCLAP* const instance = static_cast(plugin->plugin_data); + instance->activate(sample_rate, max_frames_count); + return true; +} + +static void clap_plugin_deactivate(const clap_plugin_t* const plugin) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + instance->deactivate(); +} + +static bool clap_plugin_start_processing(const clap_plugin_t*) +{ + // nothing to do + return true; +} + +static void clap_plugin_stop_processing(const clap_plugin_t*) +{ + // nothing to do +} + +static void clap_plugin_reset(const clap_plugin_t*) +{ + // nothing to do +} + +static clap_process_status clap_plugin_process(const clap_plugin_t* const plugin, const clap_process_t* const process) +{ + PluginCLAP* const instance = static_cast(plugin->plugin_data); + return instance->process(process) ? CLAP_PROCESS_CONTINUE : CLAP_PROCESS_ERROR; +} + +static const void* clap_plugin_get_extension(const clap_plugin_t*, const char* const id) +{ + if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) + return &clap_plugin_audio_ports; + if (std::strcmp(id, CLAP_EXT_PARAMS) == 0) + return &clap_plugin_params; + #if DISTRHO_PLUGIN_HAS_UI + if (std::strcmp(id, CLAP_EXT_GUI) == 0) + return &clap_plugin_gui; + #endif + return nullptr; +} + +static void clap_plugin_on_main_thread(const clap_plugin_t*) +{ + // nothing to do +} + +// -------------------------------------------------------------------------------------------------------------------- +// plugin factory + +static uint32_t clap_get_plugin_count(const clap_plugin_factory_t*) +{ + return 1; +} + +static const clap_plugin_descriptor_t* clap_get_plugin_descriptor(const clap_plugin_factory_t*, const uint32_t index) +{ + DISTRHO_SAFE_ASSERT_UINT_RETURN(index == 0, index, nullptr); + + static const char* features[] = { + #ifdef DISTRHO_PLUGIN_CLAP_FEATURES + DISTRHO_PLUGIN_CLAP_FEATURES, + #elif DISTRHO_PLUGIN_IS_SYNTH + "instrument", + #endif + nullptr + }; + + static const clap_plugin_descriptor_t descriptor = { + CLAP_VERSION, + sPlugin->getLabel(), + sPlugin->getName(), + sPlugin->getMaker(), + // TODO url + "", + // TODO manual url + "", + // TODO support url + "", + // TODO version string + "", + sPlugin->getDescription(), + features + }; + + return &descriptor; +} + +static const clap_plugin_t* clap_create_plugin(const clap_plugin_factory_t* const factory, + const clap_host_t* const host, + const char*) +{ + clap_plugin_t* const pluginptr = static_cast(std::malloc(sizeof(clap_plugin_t))); + DISTRHO_SAFE_ASSERT_RETURN(pluginptr != nullptr, nullptr); + + // default early values + if (d_nextBufferSize == 0) + d_nextBufferSize = 1024; + if (d_nextSampleRate <= 0.0) + d_nextSampleRate = 44100.0; + + d_nextCanRequestParameterValueChanges = true; + + const clap_plugin_t plugin = { + clap_get_plugin_descriptor(factory, 0), + new PluginCLAP(host), + clap_plugin_init, + clap_plugin_destroy, + clap_plugin_activate, + clap_plugin_deactivate, + clap_plugin_start_processing, + clap_plugin_stop_processing, + clap_plugin_reset, + clap_plugin_process, + clap_plugin_get_extension, + clap_plugin_on_main_thread + }; + + std::memcpy(pluginptr, &plugin, sizeof(clap_plugin_t)); + return pluginptr; +} + +static const clap_plugin_factory_t clap_plugin_factory = { + clap_get_plugin_count, + clap_get_plugin_descriptor, + clap_create_plugin +}; + +// -------------------------------------------------------------------------------------------------------------------- +// plugin entry + +static bool clap_plugin_entry_init(const char* const plugin_path) +{ + static String bundlePath; + bundlePath = plugin_path; + d_nextBundlePath = bundlePath.buffer(); + + // init dummy plugin + 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; + } + + return true; +} + +static void clap_plugin_entry_deinit(void) +{ + sPlugin = nullptr; +} + +static const void* clap_plugin_entry_get_factory(const char* const factory_id) +{ + if (std::strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID) == 0) + return &clap_plugin_factory; + return nullptr; +} + +static const clap_plugin_entry_t clap_plugin_entry = { + CLAP_VERSION, + clap_plugin_entry_init, + clap_plugin_entry_deinit, + clap_plugin_entry_get_factory +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +DISTRHO_PLUGIN_EXPORT +const clap_plugin_entry_t clap_entry = DISTRHO_NAMESPACE::clap_plugin_entry; + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoPluginChecks.h b/dpf/distrho/src/DistrhoPluginChecks.h index dd838b9..21c98ff 100644 --- a/dpf/distrho/src/DistrhoPluginChecks.h +++ b/dpf/distrho/src/DistrhoPluginChecks.h @@ -183,6 +183,17 @@ # define DISTRHO_PLUGIN_HAS_UI 0 #endif +// ----------------------------------------------------------------------- +// Make sure both default width and height are provided + +#if defined(DISTRHO_UI_DEFAULT_WIDTH) && !defined(DISTRHO_UI_DEFAULT_HEIGHT) +# error DISTRHO_UI_DEFAULT_WIDTH is defined but DISTRHO_UI_DEFAULT_HEIGHT is not +#endif + +#if defined(DISTRHO_UI_DEFAULT_HEIGHT) && !defined(DISTRHO_UI_DEFAULT_WIDTH) +# error DISTRHO_UI_DEFAULT_HEIGHT is defined but DISTRHO_UI_DEFAULT_WIDTH is not +#endif + // ----------------------------------------------------------------------- // Prevent users from messing about with DPF internals diff --git a/dpf/distrho/src/DistrhoPluginInternal.hpp b/dpf/distrho/src/DistrhoPluginInternal.hpp index 94868ae..30b3135 100644 --- a/dpf/distrho/src/DistrhoPluginInternal.hpp +++ b/dpf/distrho/src/DistrhoPluginInternal.hpp @@ -39,6 +39,7 @@ extern uint32_t d_nextBufferSize; extern double d_nextSampleRate; extern const char* d_nextBundlePath; extern bool d_nextPluginIsDummy; +extern bool d_nextPluginIsSelfTest; extern bool d_nextCanRequestParameterValueChanges; // ----------------------------------------------------------------------- @@ -67,7 +68,8 @@ struct PortGroupWithId : PortGroup { groupId(kPortGroupNone) {} }; -static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup) +static inline +void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup) { switch (groupId) { @@ -86,12 +88,62 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por } } +static inline +void strncpy(char* const dst, const char* const src, const size_t length) +{ + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); + + if (const size_t len = std::min(std::strlen(src), length-1U)) + { + std::memcpy(dst, src, len); + dst[len] = '\0'; + } + else + { + dst[0] = '\0'; + } +} + +template +static inline +void snprintf_t(char* const dst, const T value, const char* const format, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + std::snprintf(dst, size-1, format, value); + dst[size-1] = '\0'; +} + +static inline +void snprintf_f32(char* const dst, const float value, const size_t size) +{ + return snprintf_t(dst, value, "%f", size); +} + +static inline +void snprintf_f32(char* const dst, const double value, const size_t size) +{ + return snprintf_t(dst, value, "%f", size); +} + +static inline +void snprintf_i32(char* const dst, const int32_t value, const size_t size) +{ + return snprintf_t(dst, value, "%d", size); +} + +static inline +void snprintf_u32(char* const dst, const uint32_t value, const size_t size) +{ + return snprintf_t(dst, value, "%u", size); +} + // ----------------------------------------------------------------------- // Plugin private data struct Plugin::PrivateData { const bool canRequestParameterValueChanges; const bool isDummy; + const bool isSelfTest; bool isProcessing; #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 @@ -136,6 +188,7 @@ struct Plugin::PrivateData { PrivateData() noexcept : canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges), isDummy(d_nextPluginIsDummy), + isSelfTest(d_nextPluginIsSelfTest), isProcessing(false), #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 audioPorts(nullptr), @@ -324,7 +377,8 @@ public: # if DISTRHO_PLUGIN_WANT_STATE if (fData->stateCount != 0) { - if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState) + if ((void*)(fPlugin->*(static_cast(&Plugin::initState))) == + (void*)static_cast(&Plugin::initState)) { d_stderr2("DPF warning: Plugins with state must implement `initState`"); abort(); @@ -593,6 +647,11 @@ public: return (getParameterHints(index) & kParameterIsOutput) != 0x0; } + bool isParameterInteger(const uint32_t index) const noexcept + { + return (getParameterHints(index) & kParameterIsInteger) != 0x0; + } + bool isParameterTrigger(const uint32_t index) const noexcept { return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger; diff --git a/dpf/distrho/src/DistrhoPluginJACK.cpp b/dpf/distrho/src/DistrhoPluginJACK.cpp index 3570182..2373fa7 100644 --- a/dpf/distrho/src/DistrhoPluginJACK.cpp +++ b/dpf/distrho/src/DistrhoPluginJACK.cpp @@ -38,6 +38,12 @@ #include "jackbridge/JackBridge.cpp" #include "lv2/lv2.h" +#ifdef DISTRHO_OS_MAC +# define Point CocoaPoint +# include +# undef Point +#endif + #ifndef DISTRHO_OS_WINDOWS # include # include @@ -789,11 +795,11 @@ protected: while (! shouldThreadExit()) { -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT plugin.run(inputs, outputs, 128, nullptr, 0); -#else + #else plugin.run(inputs, outputs, 128); -#endif + #endif d_msleep(100); } @@ -807,7 +813,7 @@ bool runSelfTests() { d_nextBufferSize = 512; d_nextSampleRate = 44100.0; - PluginExporter plugin(nullptr, nullptr, nullptr); + PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; } @@ -818,7 +824,17 @@ bool runSelfTests() // simple processing { - PluginExporter plugin(nullptr, nullptr, nullptr); + d_nextPluginIsSelfTest = true; + PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); + d_nextPluginIsSelfTest = false; + + #if DISTRHO_PLUGIN_HAS_UI + UIExporter ui(nullptr, 0, plugin.getSampleRate(), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + plugin.getInstancePointer(), 0.0); + ui.showAndFocus(); + #endif + plugin.activate(); plugin.deactivate(); plugin.setBufferSize(128); @@ -833,20 +849,26 @@ bool runSelfTests() for (int i=0; i(std::atoll(argv[2])); -#endif + #endif const PluginJack p(client, winId); -#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI + #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI /* the code below is based on * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/ */ @@ -1075,14 +1112,9 @@ int main(int argc, char* argv[]) ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release SendInput(1, &ip, sizeof(INPUT)); } -#endif + #endif return 0; - -#ifndef DPF_RUNTIME_TESTING - // unused - (void)argc; (void)argv; -#endif } // ----------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoPluginLV2.cpp b/dpf/distrho/src/DistrhoPluginLV2.cpp index b3e7956..0e730e3 100644 --- a/dpf/distrho/src/DistrhoPluginLV2.cpp +++ b/dpf/distrho/src/DistrhoPluginLV2.cpp @@ -620,22 +620,22 @@ public: // Run plugin if (sampleCount != 0) { -#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD + #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD fRunCount = mod_license_run_begin(fRunCount, sampleCount); -#endif + #endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount); -#else + #else fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); -#endif + #endif -#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD + #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD for (uint32_t i=0; i= fPlugin.getProgramCount()) @@ -895,30 +895,30 @@ public: setPortControlValue(i, fLastControlValues[i]); } -# if DISTRHO_PLUGIN_WANT_FULL_STATE + #if DISTRHO_PLUGIN_WANT_FULL_STATE // Update state for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; fStateMap[key] = fPlugin.getStateValue(key); } -# endif + #endif } -#endif + #endif // ------------------------------------------------------------------- -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle) { -# if DISTRHO_PLUGIN_WANT_FULL_STATE + #if DISTRHO_PLUGIN_WANT_FULL_STATE // Update current state for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; fStateMap[key] = fPlugin.getStateValue(key); } -# endif + #endif String lv2key; LV2_URID urid; @@ -1021,11 +1021,11 @@ public: setState(key, value); -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE // signal msg needed for UI if ((hints & kStateIsOnlyForDSP) == 0x0) fNeededUiSends[i] = true; -#endif + #endif } return LV2_STATE_SUCCESS; @@ -1090,16 +1090,16 @@ public: { return LV2_WORKER_SUCCESS; } -#endif + #endif // ------------------------------------------------------------------- -#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS void* lv2_get_instance_pointer() { return fPlugin.getInstancePointer(); } -#endif + #endif // ------------------------------------------------------------------- @@ -1107,36 +1107,36 @@ private: PluginExporter fPlugin; const bool fUsingNominal; // if false use maxBlockLength -#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD + #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD uint32_t fRunCount; -#endif + #endif // LV2 ports -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 const float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; -#else + #else const float** fPortAudioIns; -#endif -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; -#else + #else float** fPortAudioOuts; -#endif + #endif float** fPortControls; -#if DISTRHO_LV2_USE_EVENTS_IN + #if DISTRHO_LV2_USE_EVENTS_IN LV2_Atom_Sequence* fPortEventsIn; -#endif -#if DISTRHO_PLUGIN_WANT_LATENCY + #endif + #if DISTRHO_PLUGIN_WANT_LATENCY float* fPortLatency; -#endif + #endif // Temporary data float* fLastControlValues; double fSampleRate; -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT MidiEvent fMidiEvents[kMaxMidiEvents]; -#endif -#if DISTRHO_PLUGIN_WANT_TIMEPOS + #endif + #if DISTRHO_PLUGIN_WANT_TIMEPOS TimePosition fTimePosition; struct Lv2PositionData { @@ -1160,9 +1160,9 @@ private: ticksPerBeat(-1.0) {} } fLastPositionData; -#endif + #endif -#if DISTRHO_LV2_USE_EVENTS_OUT + #if DISTRHO_LV2_USE_EVENTS_OUT struct Lv2EventsOutData { uint32_t capacity, offset; LV2_Atom_Sequence* port; @@ -1198,7 +1198,7 @@ private: } } fEventsOutData; -#endif + #endif // LV2 URIDs struct URIDs { @@ -1262,13 +1262,13 @@ private: } fURIDs; // LV2 features -#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST + #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST const LV2_ControlInputPort_Change_Request* const fCtrlInPortChangeReq; -#endif + #endif const LV2_URID_Map* const fUridMap; const LV2_Worker_Schedule* const fWorker; -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE LV2_Atom_Forge fAtomForge; StringToStringMap fStateMap; UridToStringMap fUridStateMap; @@ -1321,7 +1321,7 @@ private: d_stderr("Failed to find plugin state with key \"%s\"", key); return false; } -#endif + #endif void updateParameterOutputsAndTriggers() { @@ -1341,13 +1341,13 @@ private: } } -#if DISTRHO_PLUGIN_WANT_LATENCY + #if DISTRHO_PLUGIN_WANT_LATENCY if (fPortLatency != nullptr) *fPortLatency = fPlugin.getLatency(); -#endif + #endif } -#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST + #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST bool requestParameterValueChange(const uint32_t index, const float value) { if (fCtrlInPortChangeReq == nullptr) @@ -1359,16 +1359,16 @@ private: { return (((PluginLv2*)ptr)->requestParameterValueChange(index, value) == 0); } -#endif + #endif -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) { return ((PluginLv2*)ptr)->updateState(key, value); } -#endif + #endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT bool writeMidi(const MidiEvent& midiEvent) { DISTRHO_SAFE_ASSERT_RETURN(fEventsOutData.port != nullptr, false); @@ -1398,7 +1398,7 @@ private: { return ((PluginLv2*)ptr)->writeMidi(midiEvent); } -#endif + #endif }; // ----------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoPluginStub.cpp b/dpf/distrho/src/DistrhoPluginStub.cpp new file mode 100644 index 0000000..3a6e7d2 --- /dev/null +++ b/dpf/distrho/src/DistrhoPluginStub.cpp @@ -0,0 +1,97 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * 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" + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +static constexpr const writeMidiFunc writeMidiCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST +static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_STATE +static const updateStateValueFunc updateStateValueCallback = nullptr; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +/** + * Stub plugin class, does nothing but serving as example code for other implementations. + */ +class PluginStub +{ +public: + PluginStub() + : fPlugin(this, + writeMidiCallback, + requestParameterValueChangeCallback, + updateStateValueCallback) + { + } + + // ---------------------------------------------------------------------------------------------------------------- + +private: + // Plugin + PluginExporter fPlugin; + + // ---------------------------------------------------------------------------------------------------------------- + // DPF callbacks + + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool writeMidi(const MidiEvent&) + { + return true; + } + + static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent) + { + return ((PluginStub*)ptr)->writeMidi(midiEvent); + } + #endif + + #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST + bool requestParameterValueChange(uint32_t, float) + { + return true; + } + + static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) + { + return ((PluginStub*)ptr)->requestParameterValueChange(index, value); + } + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + bool updateState(const char*, const char*) + { + return true; + } + + static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) + { + return ((PluginStub*)ptr)->updateState(key, value); + } + #endif +}; + +END_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoPluginVST.hpp b/dpf/distrho/src/DistrhoPluginVST.hpp index 7d08adc..dd83d5e 100644 --- a/dpf/distrho/src/DistrhoPluginVST.hpp +++ b/dpf/distrho/src/DistrhoPluginVST.hpp @@ -67,24 +67,24 @@ START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- enum Vst3InternalParameters { -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER kVst3InternalParameterBufferSize, kVst3InternalParameterSampleRate, -#endif -#if DISTRHO_PLUGIN_WANT_LATENCY + #endif + #if DISTRHO_PLUGIN_WANT_LATENCY kVst3InternalParameterLatency, -#endif -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS kVst3InternalParameterProgram, -#endif + #endif kVst3InternalParameterBaseCount, -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end -#else + #else kVst3InternalParameterCount = kVst3InternalParameterBaseCount -#endif + #endif }; #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -136,22 +136,6 @@ size_t strlen_utf16(const int16_t* const str) // -------------------------------------------------------------------------------------------------------------------- -static inline -void strncpy(char* const dst, const char* const src, const size_t length) -{ - DISTRHO_SAFE_ASSERT_RETURN(length > 0,); - - if (const size_t len = std::min(std::strlen(src), length-1U)) - { - std::memcpy(dst, src, len); - dst[len] = '\0'; - } - else - { - dst[0] = '\0'; - } -} - static inline void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length) { @@ -201,15 +185,8 @@ void strncpy_utf16(int16_t* const dst, const char* const src, const size_t lengt // -------------------------------------------------------------------------------------------------------------------- template -static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - std::snprintf(dst, size-1, format, value); - dst[size-1] = '\0'; -} - -template -static void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size) +static inline +void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size) { DISTRHO_SAFE_ASSERT_RETURN(size > 0,); @@ -224,27 +201,15 @@ static void snprintf_utf16_t(int16_t* const dst, const T value, const char* cons } static inline -void snprintf_f32(char* const dst, const float value, const size_t size) -{ - return snprintf_t(dst, value, "%f", size); -} - -static inline -void snprintf_i32(char* const dst, const int32_t value, const size_t size) -{ - return snprintf_t(dst, value, "%d", size); -} - -static inline -void snprintf_u32(char* const dst, const uint32_t value, const size_t size) +void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size) { - return snprintf_t(dst, value, "%u", size); + return snprintf_utf16_t(dst, value, "%f", size); } static inline -void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size) +void snprintf_f32_utf16(int16_t* const dst, const double value, const size_t size) { - return snprintf_utf16_t(dst, value, "%f", size); + return snprintf_utf16_t(dst, value, "%f", size); } static inline diff --git a/dpf/distrho/src/DistrhoPluginVST2.cpp b/dpf/distrho/src/DistrhoPluginVST2.cpp index 8f47a98..1c257ce 100644 --- a/dpf/distrho/src/DistrhoPluginVST2.cpp +++ b/dpf/distrho/src/DistrhoPluginVST2.cpp @@ -65,7 +65,7 @@ START_NAMESPACE_DISTRHO typedef std::map StringMap; -static const int kVstMidiEventSize = static_cast(sizeof(VstMidiEvent)); +static constexpr const int kVstMidiEventSize = static_cast(sizeof(VstMidiEvent)); #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT static const writeMidiFunc writeMidiCallback = nullptr; @@ -117,7 +117,7 @@ struct ParameterAndNotesHelper } #if DISTRHO_PLUGIN_WANT_STATE - virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0; + virtual void setStateFromUI(const char* key, const char* value) = 0; #endif }; @@ -575,17 +575,25 @@ public: } else { + double scaleFactor = fLastScaleFactor; + #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) + fVstRect.right = DISTRHO_UI_DEFAULT_WIDTH; + fVstRect.bottom = DISTRHO_UI_DEFAULT_HEIGHT; + if (d_isZero(scaleFactor)) + scaleFactor = 1.0; + #else UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, - fPlugin.getInstancePointer(), fLastScaleFactor); - fVstRect.right = tmpUI.getWidth(); + fPlugin.getInstancePointer(), scaleFactor); + fVstRect.right = tmpUI.getWidth(); fVstRect.bottom = tmpUI.getHeight(); -# ifdef DISTRHO_OS_MAC - const double scaleFactor = tmpUI.getScaleFactor(); + scaleFactor = tmpUI.getScaleFactor(); + tmpUI.quit(); + #endif + #ifdef DISTRHO_OS_MAC fVstRect.right /= scaleFactor; fVstRect.bottom /= scaleFactor; -# endif - tmpUI.quit(); + #endif } *(ERect**)ptr = &fVstRect; return 1; @@ -995,7 +1003,7 @@ public: else fTimePosition.bbt.beatsPerMinute = 120.0; - if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) + if ((vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) == (kVstPpqPosValid|kVstTimeSigValid)) { const double ppqPos = std::abs(vstTimeInfo->ppqPos); const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator; @@ -1131,7 +1139,7 @@ private: { if (fPlugin.isParameterOutput(i)) { - // NOTE: no output parameter support in VST, simulate it here + // NOTE: no output parameter support in VST2, simulate it here curValue = fPlugin.getParameterValue(i); if (d_isEqual(curValue, parameterValues[i])) @@ -1230,12 +1238,12 @@ private: // functions called from the UI side, may block # if DISTRHO_PLUGIN_HAS_UI - void setStateFromUI(const char* const key, const char* const newValue) override + void setStateFromUI(const char* const key, const char* const value) override # else - void setStateFromUI(const char* const key, const char* const newValue) + void setStateFromUI(const char* const key, const char* const value) # endif { - fPlugin.setState(key, newValue); + fPlugin.setState(key, value); // check if we want to save this key if (! fPlugin.wantStateKey(key)) @@ -1248,7 +1256,7 @@ private: if (dkey == key) { - it->second = newValue; + it->second = value; return; } } diff --git a/dpf/distrho/src/DistrhoPluginVST3.cpp b/dpf/distrho/src/DistrhoPluginVST3.cpp index 4369463..7ce39b8 100644 --- a/dpf/distrho/src/DistrhoPluginVST3.cpp +++ b/dpf/distrho/src/DistrhoPluginVST3.cpp @@ -453,7 +453,7 @@ class PluginVst3 return placeSorted(event.sample_offset); } - bool appendCC(const int32_t sampleOffset, v3_param_id paramId, const double value) noexcept + bool appendCC(const int32_t sampleOffset, v3_param_id paramId, const double normalized) noexcept { InputEventStorage& eventStorage(eventListStorage[numUsed]); @@ -465,18 +465,18 @@ class PluginVst3 { case 128: eventStorage.type = CC_ChannelPressure; - eventStorage.midi[1] = std::max(0, std::min(127, (int)(value * 127))); + eventStorage.midi[1] = std::max(0, std::min(127, (int)(normalized * 127))); eventStorage.midi[2] = 0; break; case 129: eventStorage.type = CC_Pitchbend; - eventStorage.midi[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f; - eventStorage.midi[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7; + eventStorage.midi[1] = std::max(0, std::min(16384, (int)(normalized * 16384))) & 0x7f; + eventStorage.midi[2] = std::max(0, std::min(16384, (int)(normalized * 16384))) >> 7; break; default: eventStorage.type = CC_Normal; eventStorage.midi[1] = cc; - eventStorage.midi[2] = std::max(0, std::min(127, (int)(value * 127))); + eventStorage.midi[2] = std::max(0, std::min(127, (int)(normalized * 127))); break; } @@ -578,7 +578,7 @@ class PluginVst3 #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT public: - PluginVst3(v3_host_application** const host) + PluginVst3(v3_host_application** const host, const bool isComponent) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), fComponentHandler(nullptr), #if DISTRHO_PLUGIN_HAS_UI @@ -593,6 +593,9 @@ public: fCachedParameterValues(nullptr), fDummyAudioBuffer(nullptr), fParameterValuesChangedDuringProcessing(nullptr) + #if DPF_VST3_USES_SEPARATE_CONTROLLER + , fIsComponent(isComponent) + #endif #if DISTRHO_PLUGIN_HAS_UI , fParameterValueChangesForUI(nullptr) , fConnectedToUI(false) @@ -608,6 +611,10 @@ public: , fProgramCountMinusOne(fPlugin.getProgramCount()-1) #endif { + #if !DPF_VST3_USES_SEPARATE_CONTROLLER + DISTRHO_SAFE_ASSERT(isComponent); + #endif + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 std::memset(fEnabledInputs, 0, sizeof(fEnabledInputs)); fillInBusInfoDetails(); @@ -690,27 +697,58 @@ public: // ---------------------------------------------------------------------------------------------------------------- // utilities and common code - void setNormalizedPluginParameterValue(const uint32_t index, const float normalized) + double _getNormalizedParameterValue(const uint32_t index, const double plain) + { + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + return ranges.getFixedAndNormalizedValue(plain); + } + + void _setNormalizedPluginParameterValue(const uint32_t index, const double normalized) { const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); const uint32_t hints = fPlugin.getParameterHints(index); float value = ranges.getUnnormalizedValue(normalized); + // convert as needed as check for changes if (hints & kParameterIsBoolean) { - const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; - value = value > midRange ? ranges.max : ranges.min; + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.f; + const bool isHigh = value > midRange; + + if (isHigh == (fCachedParameterValues[kVst3InternalParameterBaseCount + index] > midRange)) + return; + + value = isHigh ? ranges.max : ranges.min; } else if (hints & kParameterIsInteger) { - value = std::round(value); + const int ivalue = static_cast(std::round(value)); + + if (static_cast(fCachedParameterValues[kVst3InternalParameterBaseCount + index]) == ivalue) + return; + + value = ivalue; + } + else + { + // deal with low resolution of some hosts, which convert double to float internally and lose precision + if (std::abs(ranges.getNormalizedValue(static_cast(fCachedParameterValues[kVst3InternalParameterBaseCount + index])) - normalized) < 0.0000001) + return; } fCachedParameterValues[kVst3InternalParameterBaseCount + index] = value; - #if DISTRHO_PLUGIN_HAS_UI - fParameterValueChangesForUI[kVst3InternalParameterBaseCount + index] = true; + + #if DISTRHO_PLUGIN_HAS_UI + #if DPF_VST3_USES_SEPARATE_CONTROLLER + if (!fIsComponent) #endif - fPlugin.setParameterValue(index, value); + { + fParameterValueChangesForUI[kVst3InternalParameterBaseCount + index] = true; + } + #endif + + if (!fPlugin.isParameterOutputOrTrigger(index)) + fPlugin.setParameterValue(index, value); } // ---------------------------------------------------------------------------------------------------------------- @@ -775,7 +813,7 @@ public: #if DISTRHO_PLUGIN_NUM_INPUTS > 0 return getAudioBusInfo(busId, info); #else - d_stdout("invalid input bus %d", busId); + d_stderr("invalid input bus %d", busId); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_INPUTS } @@ -784,12 +822,12 @@ public: #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 return getAudioBusInfo(busId, info); #else - d_stdout("invalid output bus %d", busId); + d_stderr("invalid output bus %d", busId); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_OUTPUTS } #else - d_stdout("invalid bus, line %d", __LINE__); + d_stderr("invalid bus, line %d", __LINE__); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS } @@ -800,7 +838,7 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG); #else - d_stdout("invalid bus, line %d", __LINE__); + d_stderr("invalid bus, line %d", __LINE__); return V3_INVALID_ARG; #endif } @@ -809,7 +847,7 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG); #else - d_stdout("invalid bus, line %d", __LINE__); + d_stderr("invalid bus, line %d", __LINE__); return V3_INVALID_ARG; #endif } @@ -1011,7 +1049,7 @@ public: DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i', queryingType, V3_INTERNAL_ERR); queryingType = 'n'; - d_stdout("found program '%s'", value.buffer()); + d_debug("found program '%s'", value.buffer()); #if DISTRHO_PLUGIN_WANT_PROGRAMS const int program = std::atoi(value.buffer()); @@ -1031,7 +1069,7 @@ public: } else if (queryingType == 's') { - d_stdout("found state '%s' '%s'", key.buffer(), value.buffer()); + d_debug("found state '%s' '%s'", key.buffer(), value.buffer()); #if DISTRHO_PLUGIN_WANT_STATE if (fPlugin.wantStateKey(key)) @@ -1048,7 +1086,7 @@ public: } else if (queryingType == 'p') { - d_stdout("found parameter '%s' '%s'", key.buffer(), value.buffer()); + d_debug("found parameter '%s' '%s'", key.buffer(), value.buffer()); float fvalue; // find parameter with this symbol, and set its value @@ -1095,7 +1133,7 @@ public: if (fPlugin.isParameterOutputOrTrigger(i)) continue; fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = false; - sendParameterSetToUI(kVst3InternalParameterBaseCount + i, + sendParameterSetToUI(kVst3InternalParameterCount + i, fCachedParameterValues[kVst3InternalParameterBaseCount + i]); } } @@ -1257,7 +1295,7 @@ public: if (getAudioBusArrangement(busId, speaker)) return V3_OK; #endif - d_stdout("invalid input bus arrangement %d", busIndex); + d_stderr("invalid input bus arrangement %d, line %d", busIndex, __LINE__); return V3_INVALID_ARG; } else @@ -1266,7 +1304,7 @@ public: if (getAudioBusArrangement(busId, speaker)) return V3_OK; #endif - d_stdout("invalid output bus arrangement %d", busIndex); + d_stderr("invalid output bus arrangement %d, line %d", busIndex, __LINE__); return V3_INVALID_ARG; } } @@ -1330,7 +1368,7 @@ public: v3_result process(v3_process_data* const data) { DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); - // d_stdout("process %i", data->symbolic_sample_size); + // d_debug("process %i", data->symbolic_sample_size); // activate plugin if not done yet if (! fPlugin.isActive()) @@ -1344,17 +1382,17 @@ public: // ticksPerBeat is not possible with VST3 fTimePosition.bbt.ticksPerBeat = 1920.0; - if (ctx->state & V3_PROCESS_CTX_CONT_TIME_VALID) - fTimePosition.frame = ctx->continuous_time_in_samples; - else + if (ctx->state & V3_PROCESS_CTX_PROJECT_TIME_VALID) fTimePosition.frame = ctx->project_time_in_samples; + else if (ctx->state & V3_PROCESS_CTX_CONT_TIME_VALID) + fTimePosition.frame = ctx->continuous_time_in_samples; if (ctx->state & V3_PROCESS_CTX_TEMPO_VALID) fTimePosition.bbt.beatsPerMinute = ctx->bpm; else fTimePosition.bbt.beatsPerMinute = 120.0; - if (ctx->state & (V3_PROCESS_CTX_PROJECT_TIME_VALID|V3_PROCESS_CTX_TIME_SIG_VALID)) + if ((ctx->state & (V3_PROCESS_CTX_PROJECT_TIME_VALID|V3_PROCESS_CTX_TIME_SIG_VALID)) == (V3_PROCESS_CTX_PROJECT_TIME_VALID|V3_PROCESS_CTX_TIME_SIG_VALID)) { const double ppqPos = std::abs(ctx->project_time_quarters); const int ppqPerBar = ctx->time_sig_numerator * 4 / ctx->time_sig_denom; @@ -1488,7 +1526,7 @@ public: if (v3_param_changes** const inparamsptr = data->input_params) { int32_t offset; - double value; + double normalized; for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i) { @@ -1507,10 +1545,10 @@ public: { for (int32_t j = 0, pcount = v3_cpp_obj(queue)->get_point_count(queue); j < pcount; ++j) { - if (v3_cpp_obj(queue)->get_point(queue, j, &offset, &value) != V3_OK) + if (v3_cpp_obj(queue)->get_point(queue, j, &offset, &normalized) != V3_OK) break; - if (inputEventList.appendCC(offset, rindex, value)) + if (inputEventList.appendCC(offset, rindex, normalized)) { canAppendMoreEvents = false; break; @@ -1526,14 +1564,14 @@ public: continue; // if there are any parameter changes at frame 0, handle them here - if (v3_cpp_obj(queue)->get_point(queue, 0, &offset, &value) != V3_OK) + if (v3_cpp_obj(queue)->get_point(queue, 0, &offset, &normalized) != V3_OK) break; if (offset != 0) continue; const uint32_t index = rindex - kVst3InternalParameterCount; - setNormalizedPluginParameterValue(index, value); + _setNormalizedPluginParameterValue(index, normalized); } } @@ -1552,7 +1590,7 @@ public: if (v3_param_changes** const inparamsptr = data->input_params) { int32_t offset; - double value; + double normalized; for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i) { @@ -1572,14 +1610,14 @@ public: if (pcount <= 0) continue; - if (v3_cpp_obj(queue)->get_point(queue, pcount - 1, &offset, &value) != V3_OK) + if (v3_cpp_obj(queue)->get_point(queue, pcount - 1, &offset, &normalized) != V3_OK) break; if (offset == 0) continue; const uint32_t index = rindex - kVst3InternalParameterCount; - setNormalizedPluginParameterValue(index, value); + _setNormalizedPluginParameterValue(index, normalized); } } @@ -1917,8 +1955,7 @@ public: const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); - return ranges.getNormalizedValue(plain); + return _getNormalizedParameterValue(index, plain); } double getParameterNormalized(const v3_param_id rindex) @@ -1933,7 +1970,7 @@ public: return 0.0; #endif - #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS + #if DPF_VST3_HAS_INTERNAL_PARAMETERS && !DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS switch (rindex) { #if DPF_VST3_USES_SEPARATE_CONTROLLER @@ -1948,13 +1985,12 @@ public: #endif return plainParameterToNormalized(rindex, fCachedParameterValues[rindex]); } - #endif + #endif const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); - return ranges.getNormalizedValue(fCachedParameterValues[kVst3InternalParameterBaseCount + index]); + return _getNormalizedParameterValue(index, fCachedParameterValues[kVst3InternalParameterBaseCount + index]); } v3_result setParameterNormalized(const v3_param_id rindex, const double normalized) @@ -2002,7 +2038,7 @@ public: { if (fPlugin.isParameterOutputOrTrigger(i)) continue; - fCachedParameterValues[kVst3InternalParameterCount + i] = fPlugin.getParameterValue(i); + fCachedParameterValues[kVst3InternalParameterBaseCount + i] = fPlugin.getParameterValue(i); } #if DISTRHO_PLUGIN_HAS_UI @@ -2019,18 +2055,20 @@ public: } #endif + DISTRHO_SAFE_ASSERT_UINT2_RETURN(rindex >= kVst3InternalParameterCount, rindex, kVst3InternalParameterCount, V3_INVALID_ARG); + #if DPF_VST3_USES_SEPARATE_CONTROLLER const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, V3_INVALID_ARG); - setNormalizedPluginParameterValue(index, normalized); - #endif - return V3_OK; + if (fIsComponent) { + DISTRHO_SAFE_ASSERT_RETURN(!fPlugin.isParameterOutputOrTrigger(index), V3_INVALID_ARG); + } - #if !DPF_VST3_HAS_INTERNAL_PARAMETERS - // unused - (void)rindex; + _setNormalizedPluginParameterValue(index, normalized); #endif + + return V3_OK; } v3_result setComponentHandler(v3_component_handler** const handler) noexcept @@ -2072,7 +2110,7 @@ public: return notify_state(attrs); #endif - d_stdout("comp2ctrl_notify received unknown msg '%s'", msgid); + d_stderr("comp2ctrl_notify received unknown msg '%s'", msgid); return V3_NOT_IMPLEMENTED; } @@ -2139,7 +2177,7 @@ public: for (uint32_t i=0; iperform_edit(fComponentHandler, rindex, normalized); } @@ -2268,7 +2311,7 @@ public: } #endif - d_stdout("ctrl2view_notify received unknown msg '%s'", msgid); + d_stderr("ctrl2view_notify received unknown msg '%s'", msgid); return V3_NOT_IMPLEMENTED; } @@ -2388,6 +2431,9 @@ private: #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 bool fEnabledOutputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; #endif + #if DPF_VST3_USES_SEPARATE_CONTROLLER + const bool fIsComponent; + #endif #if DISTRHO_PLUGIN_HAS_UI bool* fParameterValueChangesForUI; // basic offset + real bool fConnectedToUI; @@ -2503,7 +2549,7 @@ private: } template - v3_result getAudioBusInfo(uint32_t busId, v3_bus_info* const info) const + v3_result getAudioBusInfo(const uint32_t busId, v3_bus_info* const info) const { constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; const BusInfo& busInfo(isInput ? inputBuses : outputBuses); @@ -2529,8 +2575,12 @@ private: { case kPortGroupStereo: case kPortGroupMono: - strncpy_utf16(busName, isInput ? "Audio Input" : "Audio Output", 128); - break; + if (busId == 0) + { + strncpy_utf16(busName, isInput ? "Audio Input" : "Audio Output", 128); + break; + } + // fall-through default: if (group.name.isNotEmpty()) strncpy_utf16(busName, group.name, 128); @@ -2541,16 +2591,21 @@ private: numChannels = fPlugin.getAudioPortCountWithGroupId(isInput, port.groupId); - if (busInfo.audio == 0 && (port.hints & kAudioPortIsSidechain) == 0x0) + if (port.hints & kAudioPortIsCV) { busType = V3_MAIN; - flags = V3_DEFAULT_ACTIVE; + flags = V3_IS_CONTROL_VOLTAGE; } - else + else if (port.hints & kAudioPortIsSidechain) { busType = V3_AUX; flags = 0; } + else + { + busType = V3_MAIN; + flags = busInfo.audio == 0 ? V3_DEFAULT_ACTIVE : 0; + } break; } } @@ -2559,9 +2614,7 @@ private: } else { - busId -= busInfo.groups; - - switch (busId) + switch (busId - busInfo.groups) { case 0: if (busInfo.audio) @@ -2583,12 +2636,12 @@ private: // fall-through default: numChannels = 1; - busType = V3_AUX; + busType = V3_MAIN; flags = V3_IS_CONTROL_VOLTAGE; break; } - if (busType == V3_MAIN) + if (busType == V3_MAIN && flags != V3_IS_CONTROL_VOLTAGE) { strncpy_utf16(busName, isInput ? "Audio Input" : "Audio Output", 128); } @@ -2600,7 +2653,11 @@ private: if (port.busId == busId) { - const String& groupName(busInfo.groups ? fPlugin.getPortGroupById(port.groupId).name : port.name); + String groupName; + if (busInfo.groups) + groupName = fPlugin.getPortGroupById(port.groupId).name; + if (groupName.isEmpty()) + groupName = port.name; strncpy_utf16(busName, groupName, 128); break; } @@ -2608,7 +2665,7 @@ private: } } - // d_stdout("getAudioBusInfo %d %d %d", (int)isInput, busId, numChannels); + // d_debug("getAudioBusInfo %d %d %d", (int)isInput, busId, numChannels); std::memset(info, 0, sizeof(v3_bus_info)); info->media_type = V3_AUDIO; info->direction = isInput ? V3_INPUT : V3_OUTPUT; @@ -2619,8 +2676,53 @@ private: return V3_OK; } + // someone please tell me what is up with these.. + static inline v3_speaker_arrangement portCountToSpeaker(const uint32_t portCount) + { + DISTRHO_SAFE_ASSERT_RETURN(portCount != 0, 0); + + switch (portCount) + { + // regular mono + case 1: return V3_SPEAKER_M; + // regular stereo + case 2: return V3_SPEAKER_L | V3_SPEAKER_R; + // stereo with center channel + case 3: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_C; + // stereo with surround (quadro) + case 4: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS; + // regular 5.0 + case 5: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_C; + // regular 6.0 + case 6: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR; + // regular 7.0 + case 7: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR | V3_SPEAKER_C; + // regular 8.0 + case 8: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR | V3_SPEAKER_C | V3_SPEAKER_S; + // regular 8.1 + case 9: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR | V3_SPEAKER_C | V3_SPEAKER_S | V3_SPEAKER_LFE; + // cinema 10.0 + case 10: return ( + V3_SPEAKER_L | V3_SPEAKER_R | + V3_SPEAKER_LS | V3_SPEAKER_RS | + V3_SPEAKER_SL | V3_SPEAKER_SR | + V3_SPEAKER_LC | V3_SPEAKER_RC | + V3_SPEAKER_C | V3_SPEAKER_S); + // cinema 10.1 + case 11: return ( + V3_SPEAKER_L | V3_SPEAKER_R | + V3_SPEAKER_LS | V3_SPEAKER_RS | + V3_SPEAKER_SL | V3_SPEAKER_SR | + V3_SPEAKER_LC | V3_SPEAKER_RC | + V3_SPEAKER_C | V3_SPEAKER_S | V3_SPEAKER_LFE); + default: + d_stderr("portCountToSpeaker error: got weirdly big number ports %u in a single bus", portCount); + return 0; + } + } + template - v3_speaker_arrangement getSpeakerArrangementForAudioPort(const BusInfo& busInfo, const uint32_t portGroupId, uint32_t busId) const noexcept + v3_speaker_arrangement getSpeakerArrangementForAudioPort(const BusInfo& busInfo, const uint32_t portGroupId, const uint32_t busId) const noexcept { switch (portGroupId) { @@ -2630,37 +2732,16 @@ private: return V3_SPEAKER_L | V3_SPEAKER_R; } - v3_speaker_arrangement arr = 0x0; - if (busId < busInfo.groups) - { - const uint32_t numPortsInBus = fPlugin.getAudioPortCountWithGroupId(isInput, busId); - for (uint32_t j=0; j @@ -2668,17 +2749,6 @@ private: { constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; const BusInfo& busInfo(isInput ? inputBuses : outputBuses); - const bool* const enabledPorts = isInput - #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - ? fEnabledInputs - #else - ? nullptr - #endif - #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - : fEnabledOutputs; - #else - : nullptr; - #endif for (uint32_t i=0; i(busInfo, port.groupId, busId); - // d_stdout("getAudioBusArrangement %d enabled by value %lx", busId, *speaker); + // d_debug("getAudioBusArrangement %d enabled by value %lx", busId, *speaker); return true; } @@ -2728,7 +2791,7 @@ private: { const v3_speaker_arrangement arr = speakers[busId]; - // d_stdout("setAudioBusArrangement %d %d | %d %lx", (int)isInput, numBuses, busId, arr); + // d_debug("setAudioBusArrangement %d %d | %d %lx", (int)isInput, numBuses, busId, arr); for (uint32_t i=0; i(busInfo, port.groupId, busId); // fail if host tries to map it to anything else - if (earr != arr && arr != 0) + // FIXME should we allow to map speaker to zero as a way to disable it? + if (earr != arr /* && arr != 0 */) { ok = false; continue; @@ -2782,8 +2846,8 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(outparamsptr != nullptr,); - v3_param_id paramId; float curValue; + double normalized; #if DPF_VST3_USES_SEPARATE_CONTROLLER for (v3_param_id i=kVst3InternalParameterBufferSize; i<=kVst3InternalParameterSampleRate; ++i) @@ -2791,9 +2855,9 @@ private: if (! fParameterValuesChangedDuringProcessing[i]) continue; - curValue = plainParameterToNormalized(i, fCachedParameterValues[i]); + normalized = plainParameterToNormalized(i, fCachedParameterValues[i]); fParameterValuesChangedDuringProcessing[i] = false; - addParameterDataToHostOutputEvents(outparamsptr, i, curValue); + addParameterDataToHostOutputEvents(outparamsptr, i, normalized); } #endif @@ -2832,10 +2896,9 @@ private: fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = true; #endif - paramId = kVst3InternalParameterCount + i; - curValue = fPlugin.getParameterRanges(i).getNormalizedValue(curValue); + normalized = _getNormalizedParameterValue(i, curValue); - if (! addParameterDataToHostOutputEvents(outparamsptr, paramId, curValue, offset)) + if (! addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterCount + i, normalized, offset)) break; } @@ -2846,28 +2909,33 @@ private: { fLastKnownLatency = latency; - curValue = plainParameterToNormalized(kVst3InternalParameterLatency, - fCachedParameterValues[kVst3InternalParameterLatency]); - addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterLatency, curValue); + normalized = plainParameterToNormalized(kVst3InternalParameterLatency, + fCachedParameterValues[kVst3InternalParameterLatency]); + addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterLatency, normalized); } #endif } bool addParameterDataToHostOutputEvents(v3_param_changes** const outparamsptr, v3_param_id paramId, - const float curValue, + const double normalized, const int32_t offset = 0) { int32_t index = 0; v3_param_value_queue** const queue = v3_cpp_obj(outparamsptr)->add_param_data(outparamsptr, ¶mId, &index); DISTRHO_SAFE_ASSERT_RETURN(queue != nullptr, false); - DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj(queue)->add_point(queue, 0, curValue, &index) == V3_OK, false); + DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj(queue)->add_point(queue, 0, normalized, &index) == V3_OK, false); + /* FLStudio gets confused with this one, skip it for now if (offset != 0) - v3_cpp_obj(queue)->add_point(queue, offset, curValue, &index); + v3_cpp_obj(queue)->add_point(queue, offset, normalized, &index); + */ return true; + + // unused at the moment, buggy VST3 hosts :/ + (void)offset; } #if DISTRHO_PLUGIN_HAS_UI @@ -3023,7 +3091,7 @@ private: return v3_cpp_obj(fHostEventOutputHandle)->add_event(fHostEventOutputHandle, &event) == V3_OK; } - static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) + static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent) { return ((PluginVst3*)ptr)->writeMidi(midiEvent); } @@ -3118,13 +3186,13 @@ struct dpf_comp2ctrl_connection_point : v3_connection_point_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_connection_point_iid)) { - d_stdout("dpf_comp2ctrl_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("dpf_comp2ctrl_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); ++point->refcounter; *iface = self; return V3_OK; } - d_stdout("dpf_comp2ctrl_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("dpf_comp2ctrl_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; @@ -3135,7 +3203,7 @@ struct dpf_comp2ctrl_connection_point : v3_connection_point_cpp { static v3_result V3_API connect(void* const self, v3_connection_point** const other) { - d_stdout("dpf_comp2ctrl_connection_point::connect => %p %p", self, other); + d_debug("dpf_comp2ctrl_connection_point::connect => %p %p", self, other); dpf_comp2ctrl_connection_point* const point = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG); @@ -3150,7 +3218,7 @@ struct dpf_comp2ctrl_connection_point : v3_connection_point_cpp { static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) { - d_stdout("dpf_comp2ctrl_connection_point => %p %p", self, other); + d_debug("dpf_comp2ctrl_connection_point => %p %p", self, other); dpf_comp2ctrl_connection_point* const point = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG); @@ -3215,7 +3283,7 @@ struct dpf_ctrl2view_connection_point : v3_connection_point_cpp { static v3_result V3_API connect(void* const self, v3_connection_point** const other) { - d_stdout("dpf_ctrl2view_connection_point::connect => %p %p", self, other); + d_debug("dpf_ctrl2view_connection_point::connect => %p %p", self, other); dpf_ctrl2view_connection_point* const point = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG); @@ -3230,7 +3298,7 @@ struct dpf_ctrl2view_connection_point : v3_connection_point_cpp { static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) { - d_stdout("dpf_ctrl2view_connection_point::disconnect => %p %p", self, other); + d_debug("dpf_ctrl2view_connection_point::disconnect => %p %p", self, other); dpf_ctrl2view_connection_point* const point = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG); @@ -3300,12 +3368,12 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_midi_mapping_iid)) { - d_stdout("query_interface_midi_mapping => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_midi_mapping => %p %s %p | OK", self, tuid2str(iid), iface); *iface = self; return V3_OK; } - d_stdout("query_interface_midi_mapping => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_midi_mapping => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; @@ -3370,7 +3438,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { #endif hostApplicationFromInitialize(nullptr) { - d_stdout("dpf_edit_controller() with hostApplication %p", hostApplicationFromFactory); + d_debug("dpf_edit_controller() with hostApplication %p", hostApplicationFromFactory); // make sure host application is valid through out this controller lifetime if (hostApplicationFromFactory != nullptr) @@ -3407,7 +3475,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ~dpf_edit_controller() { - d_stdout("~dpf_edit_controller()"); + d_debug("~dpf_edit_controller()"); #if DISTRHO_PLUGIN_HAS_UI connectionCtrl2View = nullptr; #endif @@ -3435,7 +3503,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { v3_tuid_match(iid, v3_plugin_base_iid) || v3_tuid_match(iid, v3_edit_controller_iid)) { - d_stdout("query_interface_edit_controller => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_edit_controller => %p %s %p | OK", self, tuid2str(iid), iface); ++controller->refcounter; *iface = self; return V3_OK; @@ -3444,13 +3512,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (v3_tuid_match(iid, v3_midi_mapping_iid)) { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - d_stdout("query_interface_edit_controller => %p %s %p | OK convert static", self, tuid2str(iid), iface); + d_debug("query_interface_edit_controller => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_midi_mapping midi_mapping; static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; *iface = &midi_mapping_ptr; return V3_OK; #else - d_stdout("query_interface_edit_controller => %p %s %p | reject unused", self, tuid2str(iid), iface); + d_debug("query_interface_edit_controller => %p %s %p | reject unused", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; #endif @@ -3459,8 +3527,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (v3_tuid_match(iid, v3_connection_point_iid)) { #if DPF_VST3_USES_SEPARATE_CONTROLLER - d_stdout("query_interface_edit_controller => %p %s %p | OK convert %p", - self, tuid2str(iid), iface, controller->connectionComp2Ctrl.get()); + d_debug("query_interface_edit_controller => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, controller->connectionComp2Ctrl.get()); if (controller->connectionComp2Ctrl == nullptr) controller->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(controller->vst3); @@ -3469,13 +3537,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp { *iface = &controller->connectionComp2Ctrl; return V3_OK; #else - d_stdout("query_interface_edit_controller => %p %s %p | reject unwanted", self, tuid2str(iid), iface); + d_debug("query_interface_edit_controller => %p %s %p | reject unwanted", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; #endif } - d_stdout("query_interface_edit_controller => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_edit_controller => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; } @@ -3484,7 +3552,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { dpf_edit_controller* const controller = *static_cast(self); const int refcount = ++controller->refcounter; - d_stdout("dpf_edit_controller::ref => %p | refcount %i", self, refcount); + d_debug("dpf_edit_controller::ref => %p | refcount %i", self, refcount); return refcount; } @@ -3495,7 +3563,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (const int refcount = --controller->refcounter) { - d_stdout("dpf_edit_controller::unref => %p | refcount %i", self, refcount); + d_debug("dpf_edit_controller::unref => %p | refcount %i", self, refcount); return refcount; } @@ -3520,12 +3588,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (unclean) return handleUncleanController(controllerptr); - d_stdout("dpf_edit_controller::unref => %p | refcount is zero, deleting everything now!", self); + d_debug("dpf_edit_controller::unref => %p | refcount is zero, deleting everything now!", self); delete controller; delete controllerptr; #else - d_stdout("dpf_edit_controller::unref => %p | refcount is zero, deletion will be done by component later", self); + d_debug("dpf_edit_controller::unref => %p | refcount is zero, deletion will be done by component later", self); #endif return 0; } @@ -3549,7 +3617,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (context != nullptr) v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication); - d_stdout("dpf_edit_controller::initialize => %p %p | host %p", self, context, hostApplication); + d_debug("dpf_edit_controller::initialize => %p %p | host %p", self, context, hostApplication); // save it for later so we can unref it controller->hostApplicationFromInitialize = hostApplication; @@ -3568,7 +3636,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { d_nextCanRequestParameterValueChanges = true; // create the actual plugin - controller->vst3 = new PluginVst3(hostApplication); + controller->vst3 = new PluginVst3(hostApplication, false); // set connection point if needed if (dpf_comp2ctrl_connection_point* const point = controller->connectionComp2Ctrl) @@ -3586,7 +3654,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API terminate(void* self) { - d_stdout("dpf_edit_controller::terminate => %p", self); + d_debug("dpf_edit_controller::terminate => %p", self); dpf_edit_controller* const controller = *static_cast(self); #if DPF_VST3_USES_SEPARATE_CONTROLLER @@ -3618,7 +3686,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API set_component_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); + d_debug("dpf_edit_controller::set_component_state => %p %p", self, stream); #if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); @@ -3638,7 +3706,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API set_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); + d_debug("dpf_edit_controller::set_state => %p %p", self, stream); #if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); @@ -3654,7 +3722,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API get_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); + d_debug("dpf_edit_controller::get_state => %p %p", self, stream); #if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); @@ -3670,7 +3738,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static int32_t V3_API get_parameter_count(void* self) { - // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); + // d_debug("dpf_edit_controller::get_parameter_count => %p", self); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; @@ -3681,7 +3749,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API get_parameter_info(void* self, int32_t param_idx, v3_param_info* param_info) { - // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); + // d_debug("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; @@ -3690,16 +3758,16 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterInfo(param_idx, param_info); } - static v3_result V3_API get_parameter_string_for_value(void* self, v3_param_id index, double normalised, v3_str_128 output) + static v3_result V3_API get_parameter_string_for_value(void* self, v3_param_id index, double normalized, v3_str_128 output) { // NOTE very noisy, called many times - // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); + // d_debug("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalized, output); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getParameterStringForValue(index, normalised, output); + return vst3->getParameterStringForValue(index, normalized, output); } static v3_result V3_API get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output) @@ -3713,15 +3781,15 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterValueForString(index, input, output); } - static double V3_API normalised_parameter_to_plain(void* self, v3_param_id index, double normalised) + static double V3_API normalised_parameter_to_plain(void* self, v3_param_id index, double normalized) { - d_debug("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); + d_debug("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalized); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->normalizedParameterToPlain(index, normalised); + return vst3->normalizedParameterToPlain(index, normalized); } static double V3_API plain_parameter_to_normalised(void* self, v3_param_id index, double plain) @@ -3745,20 +3813,20 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterNormalized(index); } - static v3_result V3_API set_parameter_normalised(void* const self, const v3_param_id index, const double normalised) + static v3_result V3_API set_parameter_normalised(void* const self, const v3_param_id index, const double normalized) { - // d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised); + // d_debug("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalized); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->setParameterNormalized(index, normalised); + return vst3->setParameterNormalized(index, normalized); } static v3_result V3_API set_component_handler(void* self, v3_component_handler** handler) { - d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); + d_debug("dpf_edit_controller::set_component_handler => %p %p", self, handler); dpf_edit_controller* const controller = *static_cast(self); controller->handler = handler; @@ -3771,21 +3839,22 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_plugin_view** V3_API create_view(void* self, const char* name) { - d_stdout("dpf_edit_controller::create_view => %p %s", self, name); + d_debug("dpf_edit_controller::create_view => %p %s", self, name); + + #if DISTRHO_PLUGIN_HAS_UI dpf_edit_controller* const controller = *static_cast(self); - d_stdout("create_view has contexts %p %p", - controller->hostApplicationFromFactory, controller->hostApplicationFromInitialize); + d_debug("create_view has contexts %p %p", + controller->hostApplicationFromFactory, controller->hostApplicationFromInitialize); - #if DISTRHO_PLUGIN_HAS_UI // plugin must be initialized PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); - d_stdout("dpf_edit_controller::create_view => %p %s | edit-ctrl %p, factory %p", - self, name, - controller->hostApplicationFromInitialize, - controller->hostApplicationFromFactory); + d_debug("dpf_edit_controller::create_view => %p %s | edit-ctrl %p, factory %p", + self, name, + controller->hostApplicationFromInitialize, + controller->hostApplicationFromFactory); // we require a host application for message creation v3_host_application** const host = controller->hostApplicationFromInitialize != nullptr @@ -3807,7 +3876,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { v3_connection_point** uiconn = nullptr; if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &uiconn) == V3_OK) { - d_stdout("view connection query ok %p", uiconn); + d_debug("view connection query ok %p", uiconn); controller->connectionCtrl2View = new dpf_ctrl2view_connection_point(controller->vst3); v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionCtrl2View; @@ -3824,6 +3893,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { #else return nullptr; #endif + + // maybe unused + (void)self; + (void)name; } }; @@ -3850,12 +3923,12 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_process_context_requirements_iid)) { - d_stdout("query_interface_process_context_requirements => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_process_context_requirements => %p %s %p | OK", self, tuid2str(iid), iface); *iface = self; return V3_OK; } - d_stdout("query_interface_process_context_requirements => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_process_context_requirements => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; @@ -3918,7 +3991,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_audio_processor_iid)) { - d_stdout("query_interface_audio_processor => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_audio_processor => %p %s %p | OK", self, tuid2str(iid), iface); ++processor->refcounter; *iface = self; return V3_OK; @@ -3926,14 +3999,14 @@ struct dpf_audio_processor : v3_audio_processor_cpp { if (v3_tuid_match(iid, v3_process_context_requirements_iid)) { - d_stdout("query_interface_audio_processor => %p %s %p | OK convert static", self, tuid2str(iid), iface); + d_debug("query_interface_audio_processor => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_process_context_requirements context_req; static dpf_process_context_requirements* context_req_ptr = &context_req; *iface = &context_req_ptr; return V3_OK; } - d_stdout("query_interface_audio_processor => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_audio_processor => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; @@ -3947,8 +4020,8 @@ struct dpf_audio_processor : v3_audio_processor_cpp { v3_speaker_arrangement* const outputs, const int32_t num_outputs) { // NOTE this is called a bunch of times in JUCE hosts - d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", - self, inputs, num_inputs, outputs, num_outputs); + d_debug("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", + self, inputs, num_inputs, outputs, num_outputs); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; @@ -3960,8 +4033,8 @@ struct dpf_audio_processor : v3_audio_processor_cpp { static v3_result V3_API get_bus_arrangement(void* const self, const int32_t bus_direction, const int32_t idx, v3_speaker_arrangement* const arr) { - d_stdout("dpf_audio_processor::get_bus_arrangement => %p %s %i %p", - self, v3_bus_direction_str(bus_direction), idx, arr); + d_debug("dpf_audio_processor::get_bus_arrangement => %p %s %i %p", + self, v3_bus_direction_str(bus_direction), idx, arr); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; @@ -3973,13 +4046,13 @@ struct dpf_audio_processor : v3_audio_processor_cpp { static v3_result V3_API can_process_sample_size(void*, const int32_t symbolic_sample_size) { // NOTE runs during RT - // d_stdout("dpf_audio_processor::can_process_sample_size => %i", symbolic_sample_size); + // d_debug("dpf_audio_processor::can_process_sample_size => %i", symbolic_sample_size); return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED; } static uint32_t V3_API get_latency_samples(void* const self) { - d_stdout("dpf_audio_processor::get_latency_samples => %p", self); + d_debug("dpf_audio_processor::get_latency_samples => %p", self); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; @@ -3990,13 +4063,13 @@ struct dpf_audio_processor : v3_audio_processor_cpp { static v3_result V3_API setup_processing(void* const self, v3_process_setup* const setup) { - d_stdout("dpf_audio_processor::setup_processing => %p %p", self, setup); + d_debug("dpf_audio_processor::setup_processing => %p %p", self, setup); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - d_stdout("dpf_audio_processor::setup_processing => %p %p | %d %f", self, setup, setup->max_block_size, setup->sample_rate); + d_debug("dpf_audio_processor::setup_processing => %p %p | %d %f", self, setup, setup->max_block_size, setup->sample_rate); d_nextBufferSize = setup->max_block_size; d_nextSampleRate = setup->sample_rate; @@ -4005,7 +4078,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { static v3_result V3_API set_processing(void* const self, const v3_bool state) { - d_stdout("dpf_audio_processor::set_processing => %p %u", self, state); + d_debug("dpf_audio_processor::set_processing => %p %u", self, state); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; @@ -4017,7 +4090,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { static v3_result V3_API process(void* const self, v3_process_data* const data) { // NOTE runs during RT - // d_stdout("dpf_audio_processor::process => %p", self); + // d_debug("dpf_audio_processor::process => %p", self); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; @@ -4028,7 +4101,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { static uint32_t V3_API get_tail_samples(void* const self) { - d_stdout("dpf_audio_processor::get_tail_samples => %p", self); + d_debug("dpf_audio_processor::get_tail_samples => %p", self); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; @@ -4058,7 +4131,7 @@ struct dpf_component : v3_component_cpp { hostApplicationFromFactory(host), hostApplicationFromInitialize(nullptr) { - d_stdout("dpf_component() with hostApplication %p", hostApplicationFromFactory); + d_debug("dpf_component() with hostApplication %p", hostApplicationFromFactory); // make sure host application is valid through out this component lifetime if (hostApplicationFromFactory != nullptr) @@ -4087,7 +4160,7 @@ struct dpf_component : v3_component_cpp { ~dpf_component() { - d_stdout("~dpf_component()"); + d_debug("~dpf_component()"); processor = nullptr; #if DPF_VST3_USES_SEPARATE_CONTROLLER connectionComp2Ctrl = nullptr; @@ -4111,7 +4184,7 @@ struct dpf_component : v3_component_cpp { v3_tuid_match(iid, v3_plugin_base_iid) || v3_tuid_match(iid, v3_component_iid)) { - d_stdout("query_interface_component => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_component => %p %s %p | OK", self, tuid2str(iid), iface); ++component->refcounter; *iface = self; return V3_OK; @@ -4120,13 +4193,13 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_midi_mapping_iid)) { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - d_stdout("query_interface_component => %p %s %p | OK convert static", self, tuid2str(iid), iface); + d_debug("query_interface_component => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_midi_mapping midi_mapping; static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; *iface = &midi_mapping_ptr; return V3_OK; #else - d_stdout("query_interface_component => %p %s %p | reject unused", self, tuid2str(iid), iface); + d_debug("query_interface_component => %p %s %p | reject unused", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; #endif @@ -4134,8 +4207,8 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_audio_processor_iid)) { - d_stdout("query_interface_component => %p %s %p | OK convert %p", - self, tuid2str(iid), iface, component->processor.get()); + d_debug("query_interface_component => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, component->processor.get()); if (component->processor == nullptr) component->processor = new dpf_audio_processor(component->vst3); @@ -4148,8 +4221,8 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_connection_point_iid)) { #if DPF_VST3_USES_SEPARATE_CONTROLLER - d_stdout("query_interface_component => %p %s %p | OK convert %p", - self, tuid2str(iid), iface, component->connectionComp2Ctrl.get()); + d_debug("query_interface_component => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, component->connectionComp2Ctrl.get()); if (component->connectionComp2Ctrl == nullptr) component->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(component->vst3); @@ -4158,7 +4231,7 @@ struct dpf_component : v3_component_cpp { *iface = &component->connectionComp2Ctrl; return V3_OK; #else - d_stdout("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); + d_debug("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; #endif @@ -4167,8 +4240,8 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_edit_controller_iid)) { #if !DPF_VST3_USES_SEPARATE_CONTROLLER - d_stdout("query_interface_component => %p %s %p | OK convert %p", - self, tuid2str(iid), iface, component->controller.get()); + d_debug("query_interface_component => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, component->controller.get()); if (component->controller == nullptr) component->controller = new dpf_edit_controller(component->vst3, @@ -4179,13 +4252,13 @@ struct dpf_component : v3_component_cpp { *iface = &component->controller; return V3_OK; #else - d_stdout("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); + d_debug("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; #endif } - d_stdout("query_interface_component => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_component => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; } @@ -4194,7 +4267,7 @@ struct dpf_component : v3_component_cpp { { dpf_component* const component = *static_cast(self); const int refcount = ++component->refcounter; - d_stdout("dpf_component::ref => %p | refcount %i", self, refcount); + d_debug("dpf_component::ref => %p | refcount %i", self, refcount); return refcount; } @@ -4205,7 +4278,7 @@ struct dpf_component : v3_component_cpp { if (const int refcount = --component->refcounter) { - d_stdout("dpf_component::unref => %p | refcount %i", self, refcount); + d_debug("dpf_component::unref => %p | refcount %i", self, refcount); return refcount; } @@ -4249,7 +4322,7 @@ struct dpf_component : v3_component_cpp { if (unclean) return handleUncleanComponent(componentptr); - d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); + d_debug("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); delete component; delete componentptr; @@ -4271,7 +4344,7 @@ struct dpf_component : v3_component_cpp { if (context != nullptr) v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication); - d_stdout("dpf_component::initialize => %p %p | hostApplication %p", self, context, hostApplication); + d_debug("dpf_component::initialize => %p %p | hostApplication %p", self, context, hostApplication); // save it for later so we can unref it component->hostApplicationFromInitialize = hostApplication; @@ -4295,7 +4368,7 @@ struct dpf_component : v3_component_cpp { d_nextCanRequestParameterValueChanges = true; // create the actual plugin - component->vst3 = new PluginVst3(hostApplication); + component->vst3 = new PluginVst3(hostApplication, true); #if DPF_VST3_USES_SEPARATE_CONTROLLER // set connection point if needed @@ -4311,7 +4384,7 @@ struct dpf_component : v3_component_cpp { static v3_result V3_API terminate(void* const self) { - d_stdout("dpf_component::terminate => %p", self); + d_debug("dpf_component::terminate => %p", self); dpf_component* const component = *static_cast(self); // check if already terminated @@ -4341,7 +4414,7 @@ struct dpf_component : v3_component_cpp { static v3_result V3_API get_controller_class_id(void*, v3_tuid class_id) { - d_stdout("dpf_component::get_controller_class_id => %p", class_id); + d_debug("dpf_component::get_controller_class_id => %p", class_id); std::memcpy(class_id, dpf_tuid_controller, sizeof(v3_tuid)); return V3_OK; @@ -4349,7 +4422,7 @@ struct dpf_component : v3_component_cpp { static v3_result V3_API set_io_mode(void* const self, const int32_t io_mode) { - d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode); + d_debug("dpf_component::set_io_mode => %p %i", self, io_mode); dpf_component* const component = *static_cast(self); @@ -4358,12 +4431,15 @@ struct dpf_component : v3_component_cpp { // TODO return V3_NOT_IMPLEMENTED; + + // unused + (void)io_mode; } static int32_t V3_API get_bus_count(void* const self, const int32_t media_type, const int32_t bus_direction) { // NOTE runs during RT - // d_stdout("dpf_component::get_bus_count => %p %s %s", + // d_debug("dpf_component::get_bus_count => %p %s %s", // self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction)); dpf_component* const component = *static_cast(self); @@ -4371,15 +4447,15 @@ struct dpf_component : v3_component_cpp { DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); const int32_t ret = vst3->getBusCount(media_type, bus_direction); - // d_stdout("dpf_component::get_bus_count returns %i", ret); + // d_debug("dpf_component::get_bus_count returns %i", ret); return ret; } static v3_result V3_API get_bus_info(void* const self, const int32_t media_type, const int32_t bus_direction, const int32_t bus_idx, v3_bus_info* const info) { - d_stdout("dpf_component::get_bus_info => %p %s %s %i %p", - self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, info); + d_debug("dpf_component::get_bus_info => %p %s %s %i %p", + self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, info); dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; @@ -4390,7 +4466,7 @@ struct dpf_component : v3_component_cpp { static v3_result V3_API get_routing_info(void* const self, v3_routing_info* const input, v3_routing_info* const output) { - d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output); + d_debug("dpf_component::get_routing_info => %p %p %p", self, input, output); dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; @@ -4403,8 +4479,8 @@ struct dpf_component : v3_component_cpp { const int32_t bus_idx, const v3_bool state) { // NOTE this is called a bunch of times - // d_stdout("dpf_component::activate_bus => %p %s %s %i %u", - // self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, state); + // d_debug("dpf_component::activate_bus => %p %s %s %i %u", + // self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, state); dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; @@ -4415,7 +4491,7 @@ struct dpf_component : v3_component_cpp { static v3_result V3_API set_active(void* const self, const v3_bool state) { - d_stdout("dpf_component::set_active => %p %u", self, state); + d_debug("dpf_component::set_active => %p %u", self, state); dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; @@ -4426,7 +4502,7 @@ struct dpf_component : v3_component_cpp { static v3_result V3_API set_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_component::set_state => %p", self); + d_debug("dpf_component::set_state => %p", self); dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; @@ -4437,7 +4513,7 @@ struct dpf_component : v3_component_cpp { static v3_result V3_API get_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_component::get_state => %p %p", self, stream); + d_debug("dpf_component::get_state => %p %p", self, stream); dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; @@ -4450,26 +4526,7 @@ struct dpf_component : v3_component_cpp { // -------------------------------------------------------------------------------------------------------------------- // Dummy plugin to get data from -static const PluginExporter& _getPluginInfo() -{ - d_nextBufferSize = 1024; - d_nextSampleRate = 44100.0; - d_nextPluginIsDummy = true; - d_nextCanRequestParameterValueChanges = true; - static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr, nullptr); - d_nextBufferSize = 0; - d_nextSampleRate = 0.0; - d_nextPluginIsDummy = false; - d_nextCanRequestParameterValueChanges = false; - - return gPluginInfo; -} - -static const PluginExporter& getPluginInfo() -{ - static const PluginExporter& info(_getPluginInfo()); - return info; -} +static ScopedPointer sPlugin; static const char* getPluginCategories() { @@ -4478,20 +4535,11 @@ static const char* getPluginCategories() if (firstInit) { -#ifdef DISTRHO_PLUGIN_VST3_CATEGORIES + #ifdef DISTRHO_PLUGIN_VST3_CATEGORIES categories = DISTRHO_PLUGIN_VST3_CATEGORIES; -#elif DISTRHO_PLUGIN_IS_SYNTH + #elif DISTRHO_PLUGIN_IS_SYNTH categories = "Instrument"; -#endif -#if (DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_INPUTS == 1) && DISTRHO_PLUGIN_NUM_OUTPUTS == 1 - if (categories.isNotEmpty()) - categories += "|"; - categories += "Mono"; -#elif (DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_INPUTS == 2) && DISTRHO_PLUGIN_NUM_OUTPUTS == 2 - if (categories.isNotEmpty()) - categories += "|"; - categories += "Stereo"; -#endif + #endif firstInit = false; } @@ -4504,7 +4552,7 @@ static const char* getPluginVersion() if (version.isEmpty()) { - const uint32_t versionNum = getPluginInfo().getVersion(); + const uint32_t versionNum = sPlugin->getVersion(); char versionBuf[64]; std::snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d", @@ -4559,7 +4607,7 @@ struct dpf_factory : v3_plugin_factory_cpp { #if DPF_VST3_USES_SEPARATE_CONTROLLER if (gControllerGarbage.size() != 0) { - d_stdout("DPF notice: cleaning up previously undeleted controllers now"); + d_debug("DPF notice: cleaning up previously undeleted controllers now"); for (std::vector::iterator it = gControllerGarbage.begin(); it != gControllerGarbage.end(); ++it) @@ -4576,7 +4624,7 @@ struct dpf_factory : v3_plugin_factory_cpp { if (gComponentGarbage.size() != 0) { - d_stdout("DPF notice: cleaning up previously undeleted components now"); + d_debug("DPF notice: cleaning up previously undeleted components now"); for (std::vector::iterator it = gComponentGarbage.begin(); it != gComponentGarbage.end(); ++it) @@ -4603,13 +4651,13 @@ struct dpf_factory : v3_plugin_factory_cpp { v3_tuid_match(iid, v3_plugin_factory_2_iid) || v3_tuid_match(iid, v3_plugin_factory_3_iid)) { - d_stdout("query_interface_factory => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_factory => %p %s %p | OK", self, tuid2str(iid), iface); ++factory->refcounter; *iface = self; return V3_OK; } - d_stdout("query_interface_factory => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_factory => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; @@ -4619,7 +4667,7 @@ struct dpf_factory : v3_plugin_factory_cpp { { dpf_factory* const factory = *static_cast(self); const int refcount = ++factory->refcounter; - d_stdout("ref_factory::ref => %p | refcount %i", self, refcount); + d_debug("ref_factory::ref => %p | refcount %i", self, refcount); return refcount; } @@ -4630,11 +4678,11 @@ struct dpf_factory : v3_plugin_factory_cpp { if (const int refcount = --factory->refcounter) { - d_stdout("unref_factory::unref => %p | refcount %i", self, refcount); + d_debug("unref_factory::unref => %p | refcount %i", self, refcount); return refcount; } - d_stdout("unref_factory::unref => %p | refcount is zero, deleting factory", self); + d_debug("unref_factory::unref => %p | refcount is zero, deleting factory", self); delete factory; delete factoryptr; @@ -4646,19 +4694,19 @@ struct dpf_factory : v3_plugin_factory_cpp { static v3_result V3_API get_factory_info(void*, v3_factory_info* const info) { - d_stdout("dpf_factory::get_factory_info => %p", info); + d_debug("dpf_factory::get_factory_info => %p", info); std::memset(info, 0, sizeof(*info)); info->flags = 0x10; // unicode - DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); - DISTRHO_NAMESPACE::strncpy(info->url, getPluginInfo().getHomePage(), ARRAY_SIZE(info->url)); + DISTRHO_NAMESPACE::strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor)); + DISTRHO_NAMESPACE::strncpy(info->url, sPlugin->getHomePage(), ARRAY_SIZE(info->url)); // DISTRHO_NAMESPACE::strncpy(info->email, "", ARRAY_SIZE(info->email)); // TODO return V3_OK; } static int32_t V3_API num_classes(void*) { - d_stdout("dpf_factory::num_classes"); + d_debug("dpf_factory::num_classes"); #if DPF_VST3_USES_SEPARATE_CONTROLLER return 2; // factory can create component and edit-controller #else @@ -4668,12 +4716,12 @@ struct dpf_factory : v3_plugin_factory_cpp { static v3_result V3_API get_class_info(void*, const int32_t idx, v3_class_info* const info) { - d_stdout("dpf_factory::get_class_info => %i %p", idx, info); + d_debug("dpf_factory::get_class_info => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG); info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); + DISTRHO_NAMESPACE::strncpy(info->name, sPlugin->getName(), ARRAY_SIZE(info->name)); if (idx == 0) { @@ -4691,7 +4739,7 @@ struct dpf_factory : v3_plugin_factory_cpp { static v3_result V3_API create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** const instance) { - d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); + d_debug("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); dpf_factory* const factory = *static_cast(self); // query for host application @@ -4733,7 +4781,7 @@ struct dpf_factory : v3_plugin_factory_cpp { static v3_result V3_API get_class_info_2(void*, const int32_t idx, v3_class_info_2* const info) { - d_stdout("dpf_factory::get_class_info_2 => %i %p", idx, info); + d_debug("dpf_factory::get_class_info_2 => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG); @@ -4742,8 +4790,8 @@ struct dpf_factory : v3_plugin_factory_cpp { info->class_flags = V3_DISTRIBUTABLE; #endif DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); - DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); - DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); + DISTRHO_NAMESPACE::strncpy(info->name, sPlugin->getName(), ARRAY_SIZE(info->name)); + DISTRHO_NAMESPACE::strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version)); @@ -4766,7 +4814,7 @@ struct dpf_factory : v3_plugin_factory_cpp { static v3_result V3_API get_class_info_utf16(void*, const int32_t idx, v3_class_info_3* const info) { - d_stdout("dpf_factory::get_class_info_utf16 => %i %p", idx, info); + d_debug("dpf_factory::get_class_info_utf16 => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG); @@ -4775,8 +4823,8 @@ struct dpf_factory : v3_plugin_factory_cpp { info->class_flags = V3_DISTRIBUTABLE; #endif DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); - DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); - DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); + DISTRHO_NAMESPACE::strncpy_utf16(info->name, sPlugin->getName(), ARRAY_SIZE(info->name)); + DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy_utf16(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version)); @@ -4796,7 +4844,7 @@ struct dpf_factory : v3_plugin_factory_cpp { static v3_result V3_API set_host_context(void* const self, v3_funknown** const context) { - d_stdout("dpf_factory::set_host_context => %p %p", self, context); + d_debug("dpf_factory::set_host_context => %p %p", self, context); dpf_factory* const factory = *static_cast(self); // unref old context if there is one @@ -4875,8 +4923,26 @@ bool ENTRYFNNAME(ENTRYFNNAMEARGS) } // init dummy plugin and set uniqueId - dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] - = dpf_tuid_processor[2] = dpf_tuid_view[2] = getPluginInfo().getUniqueId(); + 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; + + dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] + = dpf_tuid_processor[2] = dpf_tuid_view[2] = sPlugin->getUniqueId(); + } return true; } @@ -4886,6 +4952,7 @@ bool EXITFNNAME(void); bool EXITFNNAME(void) { + DISTRHO_NAMESPACE::sPlugin = nullptr; return true; } diff --git a/dpf/distrho/src/DistrhoUI.cpp b/dpf/distrho/src/DistrhoUI.cpp index 3af6926..1605f63 100644 --- a/dpf/distrho/src/DistrhoUI.cpp +++ b/dpf/distrho/src/DistrhoUI.cpp @@ -202,13 +202,23 @@ UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint hei * UI */ UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize) - : UIWidget(UI::PrivateData::createNextWindow(this, width, height)), + : UIWidget(UI::PrivateData::createNextWindow(this, + #ifdef DISTRHO_UI_DEFAULT_WIDTH + width == 0 ? DISTRHO_UI_DEFAULT_WIDTH : + #endif + width, + #ifdef DISTRHO_UI_DEFAULT_WIDTH + height == 0 ? DISTRHO_UI_DEFAULT_WIDTH : + #endif + height)), uiData(UI::PrivateData::s_nextPrivateData) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI if (width != 0 && height != 0) { + #ifndef DISTRHO_UI_DEFAULT_WIDTH Widget::setSize(width, height); + #endif if (automaticallyScaleAndSetAsMinimumSize) setGeometryConstraints(width, height, true, true, true); @@ -405,19 +415,19 @@ void UI::onResize(const ResizeEvent& ev) #endif } -// NOTE: only used for VST3 +// NOTE: only used for VST3 and CLAP void UI::requestSizeChange(const uint width, const uint height) { -# ifdef DISTRHO_PLUGIN_TARGET_VST3 + #if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP) if (uiData->initializing) - uiData->window->setSizeForVST3(width, height); + uiData->window->setSizeFromHost(width, height); else uiData->setSizeCallback(width, height); -# else + #else // unused (void)width; (void)height; -# endif + #endif } #endif diff --git a/dpf/distrho/src/DistrhoUIInternal.hpp b/dpf/distrho/src/DistrhoUIInternal.hpp index b470224..ebcb125 100644 --- a/dpf/distrho/src/DistrhoUIInternal.hpp +++ b/dpf/distrho/src/DistrhoUIInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * 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 @@ -262,7 +262,7 @@ public: // ------------------------------------------------------------------- -#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) + #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) void idleForVST3() { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); @@ -271,7 +271,7 @@ public: ui->uiIdle(); } -# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs) { uiData->window->addIdleCallback(cb, timerFrequencyInMs); @@ -281,31 +281,31 @@ public: { uiData->window->removeIdleCallback(cb); } -# endif -#endif + #endif + #endif // ------------------------------------------------------------------- void setWindowOffset(const int x, const int y) { -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI // TODO (void)x; (void)y; -#else + #else uiData->window->setOffset(x, y); -#endif + #endif } -#ifdef DISTRHO_PLUGIN_TARGET_VST3 - void setWindowSizeForVST3(const uint width, const uint height) + #if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP) + void setWindowSizeFromHost(const uint width, const uint height) { -# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI ui->setSize(width, height); -# else - uiData->window->setSizeForVST3(width, height); -# endif + #else + uiData->window->setSizeFromHost(width, height); + #endif } -#endif + #endif void setWindowTitle(const char* const uiTitle) { diff --git a/dpf/distrho/src/DistrhoUILV2.cpp b/dpf/distrho/src/DistrhoUILV2.cpp index 0bb2158..bfcbc68 100644 --- a/dpf/distrho/src/DistrhoUILV2.cpp +++ b/dpf/distrho/src/DistrhoUILV2.cpp @@ -42,8 +42,11 @@ typedef struct _LV2_Atom_MidiEvent { uint8_t data[3]; /**< MIDI data (body). */ } LV2_Atom_MidiEvent; +#if ! DISTRHO_PLUGIN_WANT_STATE +static constexpr const setStateFunc setStateCallback = nullptr; +#endif #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT -static const sendNoteFunc sendNoteCallback = nullptr; +static constexpr const sendNoteFunc sendNoteCallback = nullptr; #endif // ----------------------------------------------------------------------- @@ -76,19 +79,8 @@ public: const float scaleFactor, const uint32_t bgColor, const uint32_t fgColor) - : fUI(this, winId, sampleRate, - editParameterCallback, - setParameterCallback, - setStateCallback, - sendNoteCallback, - nullptr, // resize is very messy, hosts can do it without extensions - fileRequestCallback, - bundlePath, - dspPtr, - scaleFactor, - bgColor, - fgColor), - fUridMap(uridMap), + : fUridMap(uridMap), + fUridUnmap(getLv2Feature(features, LV2_URID__unmap)), fUiPortMap(getLv2Feature(features, LV2_UI__portMap)), fUiRequestValue(getLv2Feature(features, LV2_UI__requestValue)), fUiTouch(getLv2Feature(features, LV2_UI__touch)), @@ -97,15 +89,23 @@ public: fURIDs(uridMap), fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled") : LV2UI_INVALID_PORT_INDEX), - fWinIdWasNull(winId == 0) + fWinIdWasNull(winId == 0), + fUI(this, winId, sampleRate, + editParameterCallback, + setParameterCallback, + setStateCallback, + sendNoteCallback, + nullptr, // resize is very messy, hosts can do it without extensions + fileRequestCallback, + bundlePath, dspPtr, scaleFactor, bgColor, fgColor) { if (widget != nullptr) *widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE // tell the DSP we're ready to receive msgs setState("__dpf_ui_data__", ""); -#endif + #endif if (winId != 0) return; @@ -169,7 +169,7 @@ public: fUI.parameterChanged(rindex-parameterOffset, value); } -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE else if (format == fURIDs.atomEventTransfer) { const LV2_Atom* const atom = (const LV2_Atom*)buffer; @@ -181,39 +181,44 @@ public: fUI.stateChanged(key, value); } - else if (atom->type == fURIDs.atomObject) + else if (atom->type == fURIDs.atomObject && fUridUnmap != nullptr) { - /* TODO - const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)LV2_ATOM_BODY_CONST(atom); + const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)atom; const LV2_Atom* property = nullptr; - const LV2_Atom* avalue = nullptr; - lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &avalue, 0); + const LV2_Atom* atomvalue = nullptr; + lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &atomvalue, 0); DISTRHO_SAFE_ASSERT_RETURN(property != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(avalue != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(atomvalue != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID,); - DISTRHO_SAFE_ASSERT_RETURN(avalue->type == fURIDs.atomPath || avalue->type == fURIDs.atomString,); + DISTRHO_SAFE_ASSERT_RETURN(atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString,); if (property != nullptr && property->type == fURIDs.atomURID && - avalue != nullptr && (avalue->type == fURIDs.atomPath || avalue->type == fURIDs.atomString)) + atomvalue != nullptr && (atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString)) { - const char* const key = (const char*)LV2_ATOM_BODY_CONST(property); - const char* const value = (const char*)LV2_ATOM_BODY_CONST(avalue); + const LV2_URID dpf_lv2_urid = ((const LV2_Atom_URID*)property)->body; + DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_urid != 0,); + + const char* const dpf_lv2_key = fUridUnmap->unmap(fUridUnmap->handle, dpf_lv2_urid); + DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_key != nullptr,); + + /*constexpr*/ const size_t reqLen = std::strlen(DISTRHO_PLUGIN_URI "#"); + DISTRHO_SAFE_ASSERT_RETURN(std::strlen(dpf_lv2_key) > reqLen,); - d_stdout("received atom object '%s' '%s'", key, value); + const char* const key = dpf_lv2_key + reqLen; + const char* const value = (const char*)LV2_ATOM_BODY_CONST(atomvalue); fUI.stateChanged(key, value); } - */ } else { - d_stdout("received atom not dpfKeyValue"); + d_stdout("DPF :: received atom not handled"); } } -#endif + #endif } // ------------------------------------------------------------------- @@ -269,24 +274,91 @@ public: // ------------------------------------------------------------------- -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #if DISTRHO_PLUGIN_WANT_PROGRAMS void lv2ui_select_program(const uint32_t bank, const uint32_t program) { const uint32_t realProgram = bank * 128 + program; fUI.programLoaded(realProgram); } -#endif + #endif // ------------------------------------------------------------------- -protected: +private: + // LV2 features + const LV2_URID_Map* const fUridMap; + const LV2_URID_Unmap* const fUridUnmap; + const LV2UI_Port_Map* const fUiPortMap; + const LV2UI_Request_Value* const fUiRequestValue; + const LV2UI_Touch* const fUiTouch; + + // LV2 UI stuff + const LV2UI_Controller fController; + const LV2UI_Write_Function fWriteFunction; + + // LV2 URIDs + const struct URIDs { + const LV2_URID_Map* _uridMap; + const LV2_URID dpfKeyValue; + const LV2_URID atomEventTransfer; + const LV2_URID atomFloat; + const LV2_URID atomLong; + const LV2_URID atomObject; + const LV2_URID atomPath; + const LV2_URID atomString; + const LV2_URID atomURID; + const LV2_URID midiEvent; + const LV2_URID paramSampleRate; + const LV2_URID patchProperty; + const LV2_URID patchSet; + const LV2_URID patchValue; + + URIDs(const LV2_URID_Map* const uridMap) + : _uridMap(uridMap), + dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), + atomEventTransfer(map(LV2_ATOM__eventTransfer)), + atomFloat(map(LV2_ATOM__Float)), + atomLong(map(LV2_ATOM__Long)), + atomObject(map(LV2_ATOM__Object)), + atomPath(map(LV2_ATOM__Path)), + atomString(map(LV2_ATOM__String)), + atomURID(map(LV2_ATOM__URID)), + midiEvent(map(LV2_MIDI__MidiEvent)), + paramSampleRate(map(LV2_PARAMETERS__sampleRate)), + patchProperty(map(LV2_PATCH__property)), + patchSet(map(LV2_PATCH__Set)), + patchValue(map(LV2_PATCH__value)) {} + + inline LV2_URID map(const char* const uri) const + { + return _uridMap->map(_uridMap->handle, uri); + } + } fURIDs; + + // index of bypass parameter, if present + const uint32_t fBypassParameterIndex; + + // using ui:showInterface if true + const bool fWinIdWasNull; + + // Plugin UI (after LV2 stuff so the UI can call into us during its constructor) + UIExporter fUI; + + // ---------------------------------------------------------------------------------------------------------------- + // DPF callbacks + void editParameterValue(const uint32_t rindex, const bool started) { if (fUiTouch != nullptr && fUiTouch->touch != nullptr) fUiTouch->touch(fUiTouch->handle, rindex, started); } + static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) + { + static_cast(ptr)->editParameterValue(rindex, started); + } + void setParameterValue(const uint32_t rindex, float value) { DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); @@ -297,6 +369,12 @@ protected: fWriteFunction(fController, rindex, sizeof(float), 0, &value); } + static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) + { + static_cast(ptr)->setParameterValue(rindex, value); + } + + #if DISTRHO_PLUGIN_WANT_STATE void setState(const char* const key, const char* const value) { DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); @@ -336,7 +414,13 @@ protected: free(atomBuf); } -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void setStateCallback(void* const ptr, const char* const key, const char* const value) + { + static_cast(ptr)->setState(key, value); + } + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) { DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); @@ -358,7 +442,12 @@ protected: fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), fURIDs.atomEventTransfer, &atomMidiEvent); } -#endif + + static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + static_cast(ptr)->sendNote(channel, note, velocity); + } + #endif bool fileRequest(const char* const key) { @@ -379,97 +468,10 @@ protected: return r == LV2UI_REQUEST_VALUE_SUCCESS; } -private: - UIExporter fUI; - - // LV2 features - const LV2_URID_Map* const fUridMap; - const LV2UI_Port_Map* const fUiPortMap; - const LV2UI_Request_Value* const fUiRequestValue; - const LV2UI_Touch* const fUiTouch; - - // LV2 UI stuff - const LV2UI_Controller fController; - const LV2UI_Write_Function fWriteFunction; - - // LV2 URIDs - const struct URIDs { - const LV2_URID_Map* _uridMap; - const LV2_URID dpfKeyValue; - const LV2_URID atomEventTransfer; - const LV2_URID atomFloat; - const LV2_URID atomLong; - const LV2_URID atomObject; - const LV2_URID atomPath; - const LV2_URID atomString; - const LV2_URID atomURID; - const LV2_URID midiEvent; - const LV2_URID paramSampleRate; - const LV2_URID patchProperty; - const LV2_URID patchSet; - const LV2_URID patchValue; - - URIDs(const LV2_URID_Map* const uridMap) - : _uridMap(uridMap), - dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), - atomEventTransfer(map(LV2_ATOM__eventTransfer)), - atomFloat(map(LV2_ATOM__Float)), - atomLong(map(LV2_ATOM__Long)), - atomObject(map(LV2_ATOM__Object)), - atomPath(map(LV2_ATOM__Path)), - atomString(map(LV2_ATOM__String)), - atomURID(map(LV2_ATOM__URID)), - midiEvent(map(LV2_MIDI__MidiEvent)), - paramSampleRate(map(LV2_PARAMETERS__sampleRate)), - patchProperty(map(LV2_PATCH__property)), - patchSet(map(LV2_PATCH__Set)), - patchValue(map(LV2_PATCH__value)) {} - - inline LV2_URID map(const char* const uri) const - { - return _uridMap->map(_uridMap->handle, uri); - } - } fURIDs; - - // index of bypass parameter, if present - const uint32_t fBypassParameterIndex; - - // using ui:showInterface if true - const bool fWinIdWasNull; - - // ------------------------------------------------------------------- - // Callbacks - - #define uiPtr ((UiLv2*)ptr) - - static void editParameterCallback(void* ptr, uint32_t rindex, bool started) - { - uiPtr->editParameterValue(rindex, started); - } - - static void setParameterCallback(void* ptr, uint32_t rindex, float value) - { - uiPtr->setParameterValue(rindex, value); - } - - static void setStateCallback(void* ptr, const char* key, const char* value) - { - uiPtr->setState(key, value); - } - -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) - { - uiPtr->sendNote(channel, note, velocity); - } -#endif - static bool fileRequestCallback(void* ptr, const char* key) { - return uiPtr->fileRequest(key); + return static_cast(ptr)->fileRequest(key); } - - #undef uiPtr }; // ----------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoUIPrivateData.hpp b/dpf/distrho/src/DistrhoUIPrivateData.hpp index ee4503e..f46e146 100644 --- a/dpf/distrho/src/DistrhoUIPrivateData.hpp +++ b/dpf/distrho/src/DistrhoUIPrivateData.hpp @@ -215,7 +215,7 @@ public: puglBackendLeave(pData->view); } - // used for temporary windows (VST2/3 get size without active/visible view) + // used for temporary windows (VST/CLAP get size without active/visible view) void setIgnoreIdleCallbacks(const bool ignore = true) { pData->ignoreIdleCallbacks = ignore; @@ -228,8 +228,8 @@ public: puglBackendEnter(pData->view); } - #ifdef DISTRHO_PLUGIN_TARGET_VST3 - void setSizeForVST3(const uint width, const uint height) + #if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP) + void setSizeFromHost(const uint width, const uint height) { puglSetSizeAndDefault(pData->view, width, height); } diff --git a/dpf/distrho/src/DistrhoUIStub.cpp b/dpf/distrho/src/DistrhoUIStub.cpp new file mode 100644 index 0000000..a867970 --- /dev/null +++ b/dpf/distrho/src/DistrhoUIStub.cpp @@ -0,0 +1,127 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * 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 "DistrhoUIInternal.hpp" + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +#if ! DISTRHO_PLUGIN_WANT_STATE +static constexpr const setStateFunc setStateCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static constexpr const sendNoteFunc sendNoteCallback = nullptr; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +/** + * Stub UI class, does nothing but serving as example code for other implementations. + */ +class UIStub +{ +public: + UIStub(const intptr_t winId, + const double sampleRate, + const char* const bundlePath, + void* const dspPtr, + const float scaleFactor) + : fUI(this, winId, sampleRate, + editParameterCallback, + setParameterCallback, + setStateCallback, + sendNoteCallback, + setSizeCallback, + fileRequestCallback, + bundlePath, dspPtr, scaleFactor) + { + } + + // ---------------------------------------------------------------------------------------------------------------- + +private: + // Stub stuff here + + // Plugin UI (after Stub stuff so the UI can call into us during its constructor) + UIExporter fUI; + + // ---------------------------------------------------------------------------------------------------------------- + // DPF callbacks + + void editParameter(uint32_t, bool) const + { + } + + static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) + { + static_cast(ptr)->editParameter(rindex, started); + } + + void setParameterValue(uint32_t, float) + { + } + + static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) + { + static_cast(ptr)->setParameterValue(rindex, value); + } + + void setSize(uint, uint) + { + } + + static void setSizeCallback(void* const ptr, const uint width, const uint height) + { + static_cast(ptr)->setSize(width, height); + } + + #if DISTRHO_PLUGIN_WANT_STATE + void setState(const char*, const char*) + { + } + + static void setStateCallback(void* const ptr, const char* key, const char* value) + { + static_cast(ptr)->setState(key, value); + } + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + } + + static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + static_cast(ptr)->sendNote(channel, note, velocity); + } + #endif + + bool fileRequest(const char*) + { + return true; + } + + static bool fileRequestCallback(void* const ptr, const char* const key) + { + return static_cast(ptr)->fileRequest(key); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/dpf/distrho/src/DistrhoUIVST3.cpp b/dpf/distrho/src/DistrhoUIVST3.cpp index f3c7622..8916048 100644 --- a/dpf/distrho/src/DistrhoUIVST3.cpp +++ b/dpf/distrho/src/DistrhoUIVST3.cpp @@ -49,12 +49,12 @@ START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- -#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT -static constexpr const sendNoteFunc sendNoteCallback = nullptr; -#endif #if ! DISTRHO_PLUGIN_WANT_STATE static constexpr const setStateFunc setStateCallback = nullptr; #endif +#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static constexpr const sendNoteFunc sendNoteCallback = nullptr; +#endif // -------------------------------------------------------------------------------------------------------------------- // Static data, see DistrhoPlugin.cpp @@ -73,13 +73,18 @@ static void applyGeometryConstraints(const uint minimumWidth, const bool keepAspectRatio, v3_view_rect* const rect) { - d_stdout("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | BEFORE", - minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); + d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | BEFORE", + minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); const int32_t minWidth = static_cast(minimumWidth); const int32_t minHeight = static_cast(minimumHeight); if (keepAspectRatio) { + if (rect->right < 1) + rect->right = 1; + if (rect->bottom < 1) + rect->bottom = 1; + const double ratio = static_cast(minWidth) / static_cast(minHeight); const double reqRatio = static_cast(rect->right) / static_cast(rect->bottom); @@ -99,8 +104,8 @@ static void applyGeometryConstraints(const uint minimumWidth, if (minHeight > rect->bottom) rect->bottom = minHeight; - d_stdout("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | AFTER", - minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); + d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | AFTER", + minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); } // -------------------------------------------------------------------------------------------------------------------- @@ -133,35 +138,27 @@ static uint translateVST3Modifiers(const int64_t modifiers) noexcept // -------------------------------------------------------------------------------------------------------------------- +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !DPF_VST3_USING_HOST_RUN_LOOP /** - * Helper class for getting a native idle timer, either through pugl or via native APIs. + * Helper class for getting a native idle timer via native APIs. */ -#if !DPF_VST3_USING_HOST_RUN_LOOP -class NativeIdleCallback : public IdleCallback +class NativeIdleHelper { public: - NativeIdleCallback(UIExporter& ui) - : fUI(ui), - fCallbackRegistered(false) - #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI - #if defined(DISTRHO_OS_MAC) - , fTimerRef(nullptr) - #elif defined(DISTRHO_OS_WINDOWS) - , fTimerWindow(nullptr) - , fTimerWindowClassName() - #endif - #endif + NativeIdleHelper(IdleCallback* const callback) + : fCallback(callback), + #ifdef DISTRHO_OS_MAC + fTimerRef(nullptr) + #else + fTimerWindow(nullptr), + fTimerWindowClassName() + #endif { } void registerNativeIdleCallback() { - DISTRHO_SAFE_ASSERT_RETURN(!fCallbackRegistered,); - fCallbackRegistered = true; - - #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); - #elif defined(DISTRHO_OS_MAC) + #ifdef DISTRHO_OS_MAC constexpr const CFTimeInterval interval = DPF_VST3_TIMER_INTERVAL * 0.0001; CFRunLoopTimerContext context = {}; @@ -171,12 +168,9 @@ public: DISTRHO_SAFE_ASSERT_RETURN(fTimerRef != nullptr,); CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); - #elif defined(DISTRHO_OS_WINDOWS) - /* We cannot assume anything about the native parent window passed as a parameter (winId) to the - * UIVst3 constructor because we do not own it. - * These parent windows have class names like 'reaperPluginHostWrapProc' and 'JUCE_nnnnnn'. - * - * Create invisible window to handle a timer instead. + #else + /* + * Create an invisible window to handle a timer. * There is no need for implementing a window proc because DefWindowProc already calls the * callback function when processing WM_TIMER messages. */ @@ -214,12 +208,10 @@ public: void unregisterNativeIdleCallback() { - #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - fUI.removeIdleCallbackForVST3(this); - #elif defined(DISTRHO_OS_MAC) + #ifdef DISTRHO_OS_MAC CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); CFRelease(fTimerRef); - #elif defined(DISTRHO_OS_WINDOWS) + #else DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,); KillTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID); DestroyWindow(fTimerWindow); @@ -228,27 +220,79 @@ public: } private: - UIExporter& fUI; - bool fCallbackRegistered; + IdleCallback* const fCallback; - #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI - #if defined(DISTRHO_OS_MAC) + #ifdef DISTRHO_OS_MAC CFRunLoopTimerRef fTimerRef; static void platformIdleTimerCallback(CFRunLoopTimerRef, void* const info) { - static_cast(info)->idleCallback(); + static_cast(info)->fCallback->idleCallback(); } - #elif defined(DISTRHO_OS_WINDOWS) + #else HWND fTimerWindow; String fTimerWindowClassName; WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD) { - reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA))->idleCallback(); + reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback(); } #endif - #endif +}; +#endif + +/** + * Helper class for getting a native idle timer, either through pugl or via native APIs. + */ +#if !DPF_VST3_USING_HOST_RUN_LOOP +class NativeIdleCallback : public IdleCallback +{ +public: + NativeIdleCallback(UIExporter& ui) + : fCallbackRegistered(false), + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + fIdleHelper(this) + #else + fUI(ui) + #endif + { + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + // unused + (void)ui; + #endif + } + + void registerNativeIdleCallback() + { + DISTRHO_SAFE_ASSERT_RETURN(!fCallbackRegistered,); + fCallbackRegistered = true; + + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + fIdleHelper.registerNativeIdleCallback(); + #else + fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); + #endif + } + + void unregisterNativeIdleCallback() + { + DISTRHO_SAFE_ASSERT_RETURN(fCallbackRegistered,); + fCallbackRegistered = false; + + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + fIdleHelper.unregisterNativeIdleCallback(); + #else + fUI.removeIdleCallbackForVST3(this); + #endif + } + +private: + bool fCallbackRegistered; + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + NativeIdleHelper fIdleHelper; + #else + UIExporter& fUI; + #endif }; #endif @@ -327,13 +371,13 @@ public: if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight) { - d_stdout("postInit sets new size as %u %u", nextWidth, nextHeight); - fUI.setWindowSizeForVST3(nextWidth, nextHeight); + d_debug("postInit sets new size as %u %u", nextWidth, nextHeight); + fUI.setWindowSizeFromHost(nextWidth, nextHeight); } } else if (fNeedsResizeFromPlugin) { - d_stdout("postInit forcely sets size from plugin as %u %u", fUI.getWidth(), fUI.getHeight()); + d_debug("postInit forcely sets size from plugin as %u %u", fUI.getWidth(), fUI.getHeight()); setSize(fUI.getWidth(), fUI.getHeight()); } @@ -408,7 +452,7 @@ public: #endif } - d_stdout("getSize request returning %i %i", rect->right, rect->bottom); + d_debug("getSize request returning %i %i", rect->right, rect->bottom); return V3_OK; } @@ -426,17 +470,17 @@ public: if (fIsResizingFromPlugin) { - d_stdout("host->plugin onSize request %i %i (plugin resize was active, unsetting now)", - rect.right - rect.left, rect.bottom - rect.top); + d_debug("host->plugin onSize request %i %i (plugin resize was active, unsetting now)", + rect.right - rect.left, rect.bottom - rect.top); fIsResizingFromPlugin = false; } else { - d_stdout("host->plugin onSize request %i %i (OK)", rect.right - rect.left, rect.bottom - rect.top); + d_debug("host->plugin onSize request %i %i (OK)", rect.right - rect.left, rect.bottom - rect.top); } fIsResizingFromHost = true; - fUI.setWindowSizeForVST3(rect.right - rect.left, rect.bottom - rect.top); + fUI.setWindowSizeFromHost(rect.right - rect.left, rect.bottom - rect.top); return V3_OK; } @@ -476,7 +520,7 @@ public: fConnection = point; - d_stdout("requesting current plugin state"); + d_debug("requesting current plugin state"); v3_message** const message = createMessage("init"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); @@ -494,7 +538,7 @@ public: { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - d_stdout("reporting UI closed"); + d_debug("reporting UI closed"); fReadyForPluginData = false; v3_message** const message = createMessage("close"); @@ -556,10 +600,13 @@ public: #endif } + // others like latency and buffer-size do not matter on UI side return V3_OK; } - const uint32_t index = static_cast(rindex) - kVst3InternalParameterBaseCount; + DISTRHO_SAFE_ASSERT_UINT2_RETURN(rindex >= kVst3InternalParameterCount, rindex, kVst3InternalParameterCount, V3_INVALID_ARG); + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + fUI.parameterChanged(index, value); return V3_OK; } @@ -614,7 +661,7 @@ public: } #endif - d_stdout("UIVst3 received unknown msg '%s'", msgid); + d_stderr("UIVst3 received unknown msg '%s'", msgid); return V3_NOT_IMPLEMENTED; } @@ -663,19 +710,19 @@ public: if (fNeedsResizeFromPlugin) { fNeedsResizeFromPlugin = false; - d_stdout("first resize forced behaviour is now stopped"); + d_debug("first resize forced behaviour is now stopped"); } if (fIsResizingFromHost) { fIsResizingFromHost = false; - d_stdout("was resizing from host, now stopped"); + d_debug("was resizing from host, now stopped"); } if (fIsResizingFromPlugin) { fIsResizingFromPlugin = false; - d_stdout("was resizing from plugin, now stopped"); + d_debug("was resizing from plugin, now stopped"); } } @@ -754,9 +801,9 @@ private: v3_cpp_obj_unref(message); } - static void editParameterCallback(void* ptr, uint32_t rindex, bool started) + static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) { - ((UIVst3*)ptr)->editParameter(rindex, started); + static_cast(ptr)->editParameter(rindex, started); } void setParameterValue(const uint32_t rindex, const float realValue) @@ -777,53 +824,37 @@ private: v3_cpp_obj_unref(message); } - static void setParameterCallback(void* ptr, uint32_t rindex, float value) + static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) { - ((UIVst3*)ptr)->setParameterValue(rindex, value); + static_cast(ptr)->setParameterValue(rindex, value); } - void setSize(uint width, uint height) + #if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* const key, const char* const value) { - DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - #ifdef DISTRHO_OS_MAC - const double scaleFactor = fUI.getScaleFactor(); - width /= scaleFactor; - height /= scaleFactor; - #endif + v3_message** const message = createMessage("state-set"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - if (fIsResizingFromHost) - { - if (fNeedsResizeFromPlugin) - { - d_stdout("plugin->host setSize %u %u (FORCED, exception for first resize)", width, height); - } - else - { - d_stdout("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); - return; - } - } - else - { - d_stdout("plugin->host setSize %u %u (OK)", width, height); - } + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); - fIsResizingFromPlugin = true; + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(attrlist)->set_int(attrlist, "key:length", std::strlen(key)); + v3_cpp_obj(attrlist)->set_int(attrlist, "value:length", std::strlen(value)); + v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key)); + v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value)); + v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_view_rect rect; - rect.left = rect.top = 0; - rect.right = width; - rect.bottom = height; - fNextPluginRect = rect; - v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); + v3_cpp_obj_unref(message); } - static void setSizeCallback(void* ptr, uint width, uint height) + static void setStateCallback(void* const ptr, const char* const key, const char* const value) { - ((UIVst3*)ptr)->setSize(width, height); + static_cast(ptr)->setState(key, value); } + #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) @@ -848,38 +879,54 @@ private: v3_cpp_obj_unref(message); } - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) { - ((UIVst3*)ptr)->sendNote(channel, note, velocity); + static_cast(ptr)->sendNote(channel, note, velocity); } #endif - #if DISTRHO_PLUGIN_WANT_STATE - void setState(const char* const key, const char* const value) + void setSize(uint width, uint height) { - DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); - v3_message** const message = createMessage("state-set"); - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + #ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + width /= scaleFactor; + height /= scaleFactor; + #endif - v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); - DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + if (fIsResizingFromHost) + { + if (fNeedsResizeFromPlugin) + { + d_debug("plugin->host setSize %u %u (FORCED, exception for first resize)", width, height); + } + else + { + d_debug("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); + return; + } + } + else + { + d_debug("plugin->host setSize %u %u (OK)", width, height); + } - v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); - v3_cpp_obj(attrlist)->set_int(attrlist, "key:length", std::strlen(key)); - v3_cpp_obj(attrlist)->set_int(attrlist, "value:length", std::strlen(value)); - v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key)); - v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value)); - v3_cpp_obj(fConnection)->notify(fConnection, message); + fIsResizingFromPlugin = true; - v3_cpp_obj_unref(message); + v3_view_rect rect; + rect.left = rect.top = 0; + rect.right = width; + rect.bottom = height; + fNextPluginRect = rect; + v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); } - static void setStateCallback(void* ptr, const char* key, const char* value) + static void setSizeCallback(void* const ptr, const uint width, const uint height) { - ((UIVst3*)ptr)->setState(key, value); + static_cast(ptr)->setSize(width, height); } - #endif }; // -------------------------------------------------------------------------------------------------------------------- @@ -937,13 +984,13 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_connection_point_iid)) { - d_stdout("UI|query_interface_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("UI|query_interface_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); ++point->refcounter; *iface = self; return V3_OK; } - d_stdout("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = NULL; return V3_NO_INTERFACE; @@ -955,7 +1002,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { static v3_result V3_API connect(void* const self, v3_connection_point** const other) { dpf_ui_connection_point* const point = *static_cast(self); - d_stdout("UI|dpf_ui_connection_point::connect => %p %p", self, other); + d_debug("UI|dpf_ui_connection_point::connect => %p %p", self, other); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); @@ -969,10 +1016,11 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) { - d_stdout("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); + d_debug("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); dpf_ui_connection_point* const point = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT(point->other == other); point->other = nullptr; @@ -1026,13 +1074,13 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) { - d_stdout("query_interface_view_content_scale => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_view_content_scale => %p %s %p | OK", self, tuid2str(iid), iface); ++scale->refcounter; *iface = self; return V3_OK; } - d_stdout("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = NULL; return V3_NO_INTERFACE; @@ -1044,7 +1092,7 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { static v3_result V3_API set_content_scale_factor(void* const self, const float factor) { dpf_plugin_view_content_scale* const scale = *static_cast(self); - d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); + d_debug("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); scale->scaleFactor = factor; @@ -1088,13 +1136,13 @@ struct dpf_timer_handler : v3_timer_handler_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_timer_handler_iid)) { - d_stdout("query_interface_timer_handler => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_timer_handler => %p %s %p | OK", self, tuid2str(iid), iface); ++timer->refcounter; *iface = self; return V3_OK; } - d_stdout("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = NULL; return V3_NO_INTERFACE; @@ -1155,7 +1203,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { nextHeight(0), sizeRequestedBeforeBeingAttached(false) { - d_stdout("dpf_plugin_view() with hostApplication %p", hostApplication); + d_debug("dpf_plugin_view() with hostApplication %p", hostApplication); // make sure host application is valid through out this view lifetime if (hostApplication != nullptr) @@ -1183,7 +1231,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { ~dpf_plugin_view() { - d_stdout("~dpf_plugin_view()"); + d_debug("~dpf_plugin_view()"); connection = nullptr; scale = nullptr; @@ -1206,7 +1254,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (v3_tuid_match(iid, v3_funknown_iid) || v3_tuid_match(iid, v3_plugin_view_iid)) { - d_stdout("query_interface_view => %p %s %p | OK", self, tuid2str(iid), iface); + d_debug("query_interface_view => %p %s %p | OK", self, tuid2str(iid), iface); ++view->refcounter; *iface = self; return V3_OK; @@ -1214,8 +1262,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (v3_tuid_match(v3_connection_point_iid, iid)) { - d_stdout("query_interface_view => %p %s %p | OK convert %p", - self, tuid2str(iid), iface, view->connection.get()); + d_debug("query_interface_view => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, view->connection.get()); if (view->connection == nullptr) view->connection = new dpf_ui_connection_point(view->uivst3); @@ -1228,8 +1276,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #ifndef DISTRHO_OS_MAC if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) { - d_stdout("query_interface_view => %p %s %p | OK convert %p", - self, tuid2str(iid), iface, view->scale.get()); + d_debug("query_interface_view => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, view->scale.get()); if (view->scale == nullptr) view->scale = new dpf_plugin_view_content_scale(view->uivst3); @@ -1240,7 +1288,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { } #endif - d_stdout("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + d_debug("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; @@ -1250,7 +1298,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { dpf_plugin_view* const view = *static_cast(self); const int refcount = ++view->refcounter; - d_stdout("dpf_plugin_view::ref => %p | refcount %i", self, refcount); + d_debug("dpf_plugin_view::ref => %p | refcount %i", self, refcount); return refcount; } @@ -1261,7 +1309,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (const int refcount = --view->refcounter) { - d_stdout("dpf_plugin_view::unref => %p | refcount %i", self, refcount); + d_debug("dpf_plugin_view::unref => %p | refcount %i", self, refcount); return refcount; } @@ -1300,7 +1348,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (unclean) return 0; - d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); + d_debug("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); delete view; delete viewptr; @@ -1312,7 +1360,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API is_platform_type_supported(void* const self, const char* const platform_type) { - d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); + d_debug("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); for (size_t i=0; i %p %p %s", self, parent, platform_type); + d_debug("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); dpf_plugin_view* const view = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); @@ -1378,7 +1429,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API removed(void* const self) { - d_stdout("dpf_plugin_view::removed => %p", self); + d_debug("dpf_plugin_view::removed => %p", self); dpf_plugin_view* const view = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); @@ -1413,7 +1464,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_wheel(void* const self, const float distance) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); + d_debug("dpf_plugin_view::on_wheel => %p %f", self, distance); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1430,7 +1481,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_key_down(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); + d_debug("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1447,7 +1498,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_key_up(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); + d_debug("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1463,21 +1514,43 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API get_size(void* const self, v3_view_rect* const rect) { - d_stdout("dpf_plugin_view::get_size => %p", self); + d_debug("dpf_plugin_view::get_size => %p", self); dpf_plugin_view* const view = *static_cast(self); if (UIVst3* const uivst3 = view->uivst3) return uivst3->getSize(rect); - d_stdout("dpf_plugin_view::get_size => %p | V3_NOT_INITIALIZED", self); - std::memset(rect, 0, sizeof(v3_view_rect)); + d_debug("dpf_plugin_view::get_size => %p | NOTE: size request before attach", self); + view->sizeRequestedBeforeBeingAttached = true; - return V3_NOT_INITIALIZED; + + double scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0; + #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) + rect->right = DISTRHO_UI_DEFAULT_WIDTH; + rect->bottom = DISTRHO_UI_DEFAULT_HEIGHT; + if (d_isZero(scaleFactor)) + scaleFactor = 1.0; + #else + UIExporter tmpUI(nullptr, 0, view->sampleRate, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, + view->instancePointer, scaleFactor); + rect->right = tmpUI.getWidth(); + rect->bottom = tmpUI.getHeight(); + scaleFactor = tmpUI.getScaleFactor(); + tmpUI.quit(); + #endif + rect->left = rect->top = 0; + #ifdef DISTRHO_OS_MAC + rect->right /= scaleFactor; + rect->bottom /= scaleFactor; + #endif + + return V3_OK; } static v3_result V3_API on_size(void* const self, v3_view_rect* const rect) { - d_stdout("dpf_plugin_view::on_size => %p {%d,%d,%d,%d}", + d_debug("dpf_plugin_view::on_size => %p {%d,%d,%d,%d}", self, rect->top, rect->left, rect->right, rect->bottom); DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->right > rect->left, rect->right, rect->left, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->bottom > rect->top, rect->bottom, rect->top, V3_INVALID_ARG); @@ -1495,7 +1568,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_focus(void* const self, const v3_bool state) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); + d_debug("dpf_plugin_view::on_focus => %p %u", self, state); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1511,7 +1584,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API set_frame(void* const self, v3_plugin_frame** const frame) { - d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame); + d_debug("dpf_plugin_view::set_frame => %p %p", self, frame); dpf_plugin_view* const view = *static_cast(self); view->frame = frame; @@ -1541,7 +1614,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API check_size_constraint(void* const self, v3_view_rect* const rect) { - d_stdout("dpf_plugin_view::check_size_constraint => %p {%d,%d,%d,%d}", + d_debug("dpf_plugin_view::check_size_constraint => %p {%d,%d,%d,%d}", self, rect->top, rect->left, rect->right, rect->bottom); dpf_plugin_view* const view = *static_cast(self); diff --git a/dpf/distrho/src/clap/audio-buffer.h b/dpf/distrho/src/clap/audio-buffer.h new file mode 100644 index 0000000..4d78f43 --- /dev/null +++ b/dpf/distrho/src/clap/audio-buffer.h @@ -0,0 +1,37 @@ +#pragma once + +#include "private/std.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Sample code for reading a stereo buffer: +// +// bool isLeftConstant = (buffer->constant_mask & (1 << 0)) != 0; +// bool isRightConstant = (buffer->constant_mask & (1 << 1)) != 0; +// +// for (int i = 0; i < N; ++i) { +// float l = data32[0][isLeftConstant ? 0 : i]; +// float r = data32[1][isRightConstant ? 0 : i]; +// } +// +// Note: checking the constant mask is optional, and this implies that +// the buffer must be filled with the constant value. +// Rationale: if a buffer reader doesn't check the constant mask, then it may +// process garbage samples and in result, garbage samples may be transmitted +// to the audio interface with all the bad consequences it can have. +// +// The constant mask is a hint. +typedef struct clap_audio_buffer { + // Either data32 or data64 pointer will be set. + float **data32; + double **data64; + uint32_t channel_count; + uint32_t latency; // latency from/to the audio interface + uint64_t constant_mask; +} clap_audio_buffer_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/entry.h b/dpf/distrho/src/clap/entry.h new file mode 100644 index 0000000..8f3cc1d --- /dev/null +++ b/dpf/distrho/src/clap/entry.h @@ -0,0 +1,68 @@ +#pragma once + +#include "version.h" +#include "private/macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// This interface is the entry point of the dynamic library. +// +// CLAP plugins standard search path: +// +// Linux +// - ~/.clap +// - /usr/lib/clap +// +// Windows +// - %CommonFilesFolder%/CLAP/ +// - %LOCALAPPDATA%/Programs/Common/CLAP/ +// +// MacOS +// - /Library/Audio/Plug-Ins/CLAP +// - ~/Library/Audio/Plug-Ins/CLAP +// +// In addition to the OS-specific default locations above, a CLAP host must query the environment +// for a CLAP_PATH variable, which is a list of directories formatted in the same manner as the host +// OS binary search path (PATH on Unix, separated by `:` and Path on Windows, separated by ';', as +// of this writing). +// +// Each directory should be recursively searched for files and/or bundles as appropriate in your OS +// ending with the extension `.clap`. +// +// Every method must be thread-safe. +typedef struct clap_plugin_entry { + clap_version_t clap_version; // initialized to CLAP_VERSION + + // This function must be called first, and can only be called once. + // + // It should be as fast as possible, in order to perform a very quick scan of the plugin + // descriptors. + // + // It is forbidden to display graphical user interface in this call. + // It is forbidden to perform user interaction in this call. + // + // If the initialization depends upon expensive computation, maybe try to do them ahead of time + // and cache the result. + // + // If init() returns false, then the host must not call deinit() nor any other clap + // related symbols from the DSO. + bool (*init)(const char *plugin_path); + + // No more calls into the DSO must be made after calling deinit(). + void (*deinit)(void); + + // Get the pointer to a factory. See plugin-factory.h for an example. + // + // Returns null if the factory is not provided. + // The returned pointer must *not* be freed by the caller. + const void *(*get_factory)(const char *factory_id); +} clap_plugin_entry_t; + +/* Entry point */ +CLAP_EXPORT extern const clap_plugin_entry_t clap_entry; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/events.h b/dpf/distrho/src/clap/events.h new file mode 100644 index 0000000..db2314f --- /dev/null +++ b/dpf/distrho/src/clap/events.h @@ -0,0 +1,283 @@ +#pragma once + +#include "private/std.h" +#include "fixedpoint.h" +#include "id.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// event header +// must be the first attribute of the event +typedef struct clap_event_header { + uint32_t size; // event size including this header, eg: sizeof (clap_event_note) + uint32_t time; // sample offset within the buffer for this event + uint16_t space_id; // event space, see clap_host_event_registry + uint16_t type; // event type + uint32_t flags; // see clap_event_flags +} clap_event_header_t; + +// The clap core event space +static const CLAP_CONSTEXPR uint16_t CLAP_CORE_EVENT_SPACE_ID = 0; + +enum clap_event_flags { + // Indicate a live user event, for example a user turning a physical knob + // or playing a physical key. + CLAP_EVENT_IS_LIVE = 1 << 0, + + // Indicate that the event should not be recorded. + // For example this is useful when a parameter changes because of a MIDI CC, + // because if the host records both the MIDI CC automation and the parameter + // automation there will be a conflict. + CLAP_EVENT_DONT_RECORD = 1 << 1, +}; + +// Some of the following events overlap, a note on can be expressed with: +// - CLAP_EVENT_NOTE_ON +// - CLAP_EVENT_MIDI +// - CLAP_EVENT_MIDI2 +// +// The preferred way of sending a note event is to use CLAP_EVENT_NOTE_*. +// +// The same event must not be sent twice: it is forbidden to send a the same note on +// encoded with both CLAP_EVENT_NOTE_ON and CLAP_EVENT_MIDI. +// +// The plugins are encouraged to be able to handle note events encoded as raw midi or midi2, +// or implement clap_plugin_event_filter and reject raw midi and midi2 events. +enum { + // NOTE_ON and NOTE_OFF represent a key pressed and key released event, respectively. + // A NOTE_ON with a velocity of 0 is valid and should not be interpreted as a NOTE_OFF. + // + // NOTE_CHOKE is meant to choke the voice(s), like in a drum machine when a closed hihat + // chokes an open hihat. This event can be sent by the host to the plugin. Here are two use cases: + // - a plugin is inside a drum pad in Bitwig Studio's drum machine, and this pad is choked by + // another one + // - the user double clicks the DAW's stop button in the transport which then stops the sound on + // every tracks + // + // NOTE_END is sent by the plugin to the host. The port, channel, key and note_id are those given + // by the host in the NOTE_ON event. In other words, this event is matched against the + // plugin's note input port. + // NOTE_END is useful to help the host to match the plugin's voice life time. + // + // When using polyphonic modulations, the host has to allocate and release voices for its + // polyphonic modulator. Yet only the plugin effectively knows when the host should terminate + // a voice. NOTE_END solves that issue in a non-intrusive and cooperative way. + // + // CLAP assumes that the host will allocate a unique voice on NOTE_ON event for a given port, + // channel and key. This voice will run until the plugin will instruct the host to terminate + // it by sending a NOTE_END event. + // + // Consider the following sequence: + // - process() + // Host->Plugin NoteOn(port:0, channel:0, key:16, time:t0) + // Host->Plugin NoteOn(port:0, channel:0, key:64, time:t0) + // Host->Plugin NoteOff(port:0, channel:0, key:16, t1) + // Host->Plugin NoteOff(port:0, channel:0, key:64, t1) + // # on t2, both notes did terminate + // Host->Plugin NoteOn(port:0, channel:0, key:64, t3) + // # Here the plugin finished processing all the frames and will tell the host + // # to terminate the voice on key 16 but not 64, because a note has been started at t3 + // Plugin->Host NoteEnd(port:0, channel:0, key:16, time:ignored) + // + // These four events use clap_event_note. + CLAP_EVENT_NOTE_ON, + CLAP_EVENT_NOTE_OFF, + CLAP_EVENT_NOTE_CHOKE, + CLAP_EVENT_NOTE_END, + + // Represents a note expression. + // Uses clap_event_note_expression. + CLAP_EVENT_NOTE_EXPRESSION, + + // PARAM_VALUE sets the parameter's value; uses clap_event_param_value. + // PARAM_MOD sets the parameter's modulation amount; uses clap_event_param_mod. + // + // The value heard is: param_value + param_mod. + // + // In case of a concurrent global value/modulation versus a polyphonic one, + // the voice should only use the polyphonic one and the polyphonic modulation + // amount will already include the monophonic signal. + CLAP_EVENT_PARAM_VALUE, + CLAP_EVENT_PARAM_MOD, + + // Indicates that the user started or finished adjusting a knob. + // This is not mandatory to wrap parameter changes with gesture events, but this improves + // the user experience a lot when recording automation or overriding automation playback. + // Uses clap_event_param_gesture. + CLAP_EVENT_PARAM_GESTURE_BEGIN, + CLAP_EVENT_PARAM_GESTURE_END, + + CLAP_EVENT_TRANSPORT, // update the transport info; clap_event_transport + CLAP_EVENT_MIDI, // raw midi event; clap_event_midi + CLAP_EVENT_MIDI_SYSEX, // raw midi sysex event; clap_event_midi_sysex + CLAP_EVENT_MIDI2, // raw midi 2 event; clap_event_midi2 +}; + +// Note on, off, end and choke events. +// In the case of note choke or end events: +// - the velocity is ignored. +// - key and channel are used to match active notes, a value of -1 matches all. +typedef struct clap_event_note { + clap_event_header_t header; + + int32_t note_id; // -1 if unspecified, otherwise >=0 + int16_t port_index; + int16_t channel; // 0..15 + int16_t key; // 0..127 + double velocity; // 0..1 +} clap_event_note_t; + +enum { + // with 0 < x <= 4, plain = 20 * log(x) + CLAP_NOTE_EXPRESSION_VOLUME, + + // pan, 0 left, 0.5 center, 1 right + CLAP_NOTE_EXPRESSION_PAN, + + // relative tuning in semitone, from -120 to +120 + CLAP_NOTE_EXPRESSION_TUNING, + + // 0..1 + CLAP_NOTE_EXPRESSION_VIBRATO, + CLAP_NOTE_EXPRESSION_EXPRESSION, + CLAP_NOTE_EXPRESSION_BRIGHTNESS, + CLAP_NOTE_EXPRESSION_PRESSURE, +}; +typedef int32_t clap_note_expression; + +typedef struct clap_event_note_expression { + clap_event_header_t header; + + clap_note_expression expression_id; + + // target a specific note_id, port, key and channel, -1 for global + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; + + double value; // see expression for the range +} clap_event_note_expression_t; + +typedef struct clap_event_param_value { + clap_event_header_t header; + + // target parameter + clap_id param_id; // @ref clap_param_info.id + void *cookie; // @ref clap_param_info.cookie + + // target a specific note_id, port, key and channel, -1 for global + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; + + double value; +} clap_event_param_value_t; + +typedef struct clap_event_param_mod { + clap_event_header_t header; + + // target parameter + clap_id param_id; // @ref clap_param_info.id + void *cookie; // @ref clap_param_info.cookie + + // target a specific note_id, port, key and channel, -1 for global + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; + + double amount; // modulation amount +} clap_event_param_mod_t; + +typedef struct clap_event_param_gesture { + clap_event_header_t header; + + // target parameter + clap_id param_id; // @ref clap_param_info.id +} clap_event_param_gesture_t; + +enum clap_transport_flags { + CLAP_TRANSPORT_HAS_TEMPO = 1 << 0, + CLAP_TRANSPORT_HAS_BEATS_TIMELINE = 1 << 1, + CLAP_TRANSPORT_HAS_SECONDS_TIMELINE = 1 << 2, + CLAP_TRANSPORT_HAS_TIME_SIGNATURE = 1 << 3, + CLAP_TRANSPORT_IS_PLAYING = 1 << 4, + CLAP_TRANSPORT_IS_RECORDING = 1 << 5, + CLAP_TRANSPORT_IS_LOOP_ACTIVE = 1 << 6, + CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL = 1 << 7, +}; + +typedef struct clap_event_transport { + clap_event_header_t header; + + uint32_t flags; // see clap_transport_flags + + clap_beattime song_pos_beats; // position in beats + clap_sectime song_pos_seconds; // position in seconds + + double tempo; // in bpm + double tempo_inc; // tempo increment for each samples and until the next + // time info event + + clap_beattime loop_start_beats; + clap_beattime loop_end_beats; + clap_sectime loop_start_seconds; + clap_sectime loop_end_seconds; + + clap_beattime bar_start; // start pos of the current bar + int32_t bar_number; // bar at song pos 0 has the number 0 + + uint16_t tsig_num; // time signature numerator + uint16_t tsig_denom; // time signature denominator +} clap_event_transport_t; + +typedef struct clap_event_midi { + clap_event_header_t header; + + uint16_t port_index; + uint8_t data[3]; +} clap_event_midi_t; + +typedef struct clap_event_midi_sysex { + clap_event_header_t header; + + uint16_t port_index; + const uint8_t *buffer; // midi buffer + uint32_t size; +} clap_event_midi_sysex_t; + +// While it is possible to use a series of midi2 event to send a sysex, +// prefer clap_event_midi_sysex if possible for efficiency. +typedef struct clap_event_midi2 { + clap_event_header_t header; + + uint16_t port_index; + uint32_t data[4]; +} clap_event_midi2_t; + +// Input event list, events must be sorted by time. +typedef struct clap_input_events { + void *ctx; // reserved pointer for the list + + uint32_t (*size)(const struct clap_input_events *list); + + // Don't free the returned event, it belongs to the list + const clap_event_header_t *(*get)(const struct clap_input_events *list, uint32_t index); +} clap_input_events_t; + +// Output event list, events must be sorted by time. +typedef struct clap_output_events { + void *ctx; // reserved pointer for the list + + // Pushes a copy of the event + // returns false if the event could not be pushed to the queue (out of memory?) + bool (*try_push)(const struct clap_output_events *list, const clap_event_header_t *event); +} clap_output_events_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/ext/audio-ports.h b/dpf/distrho/src/clap/ext/audio-ports.h new file mode 100644 index 0000000..5d0af46 --- /dev/null +++ b/dpf/distrho/src/clap/ext/audio-ports.h @@ -0,0 +1,116 @@ +#pragma once + +#include "../plugin.h" +#include "../string-sizes.h" + +/// @page Audio Ports +/// +/// This extension provides a way for the plugin to describe its current audio ports. +/// +/// If the plugin does not implement this extension, it won't have audio ports. +/// +/// 32 bits support is required for both host and plugins. 64 bits audio is optional. +/// +/// The plugin is only allowed to change its ports configuration while it is deactivated. + +static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS[] = "clap.audio-ports"; +static CLAP_CONSTEXPR const char CLAP_PORT_MONO[] = "mono"; +static CLAP_CONSTEXPR const char CLAP_PORT_STEREO[] = "stereo"; + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + // This port is the main audio input or output. + // There can be only one main input and main output. + // Main port must be at index 0. + CLAP_AUDIO_PORT_IS_MAIN = 1 << 0, + + // This port can be used with 64 bits audio + CLAP_AUDIO_PORT_SUPPORTS_64BITS = 1 << 1, + + // 64 bits audio is preferred with this port + CLAP_AUDIO_PORT_PREFERS_64BITS = 1 << 2, + + // This port must be used with the same sample size as all the other ports which have this flag. + // In other words if all ports have this flag then the plugin may either be used entirely with + // 64 bits audio or 32 bits audio, but it can't be mixed. + CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE = 1 << 3, +}; + +typedef struct clap_audio_port_info { + // id identifies a port and must be stable. + // id may overlap between input and output ports. + clap_id id; + char name[CLAP_NAME_SIZE]; // displayable name + + uint32_t flags; + uint32_t channel_count; + + // If null or empty then it is unspecified (arbitrary audio). + // This filed can be compared against: + // - CLAP_PORT_MONO + // - CLAP_PORT_STEREO + // - CLAP_PORT_SURROUND (defined in the surround extension) + // - CLAP_PORT_AMBISONIC (defined in the ambisonic extension) + // - CLAP_PORT_CV (defined in the cv extension) + // + // An extension can provide its own port type and way to inspect the channels. + const char *port_type; + + // in-place processing: allow the host to use the same buffer for input and output + // if supported set the pair port id. + // if not supported set to CLAP_INVALID_ID + clap_id in_place_pair; +} clap_audio_port_info_t; + +// The audio ports scan has to be done while the plugin is deactivated. +typedef struct clap_plugin_audio_ports { + // number of ports, for either input or output + // [main-thread] + uint32_t (*count)(const clap_plugin_t *plugin, bool is_input); + + // get info about about an audio port. + // [main-thread] + bool (*get)(const clap_plugin_t *plugin, + uint32_t index, + bool is_input, + clap_audio_port_info_t *info); +} clap_plugin_audio_ports_t; + +enum { + // The ports name did change, the host can scan them right away. + CLAP_AUDIO_PORTS_RESCAN_NAMES = 1 << 0, + + // [!active] The flags did change + CLAP_AUDIO_PORTS_RESCAN_FLAGS = 1 << 1, + + // [!active] The channel_count did change + CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT = 1 << 2, + + // [!active] The port type did change + CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE = 1 << 3, + + // [!active] The in-place pair did change, this requires. + CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR = 1 << 4, + + // [!active] The list of ports have changed: entries have been removed/added. + CLAP_AUDIO_PORTS_RESCAN_LIST = 1 << 5, +}; + +typedef struct clap_host_audio_ports { + // Checks if the host allows a plugin to change a given aspect of the audio ports definition. + // [main-thread] + bool (*is_rescan_flag_supported)(const clap_host_t *host, uint32_t flag); + + // Rescan the full list of audio ports according to the flags. + // It is illegal to ask the host to rescan with a flag that is not supported. + // Certain flags require the plugin to be de-activated. + // [main-thread] + void (*rescan)(const clap_host_t *host, uint32_t flags); +} clap_host_audio_ports_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/ext/gui.h b/dpf/distrho/src/clap/ext/gui.h new file mode 100644 index 0000000..da43612 --- /dev/null +++ b/dpf/distrho/src/clap/ext/gui.h @@ -0,0 +1,219 @@ +#pragma once + +#include "../plugin.h" + +/// @page GUI +/// +/// This extension defines how the plugin will present its GUI. +/// +/// There are two approaches: +/// 1. the plugin creates a window and embeds it into the host's window +/// 2. the plugin creates a floating window +/// +/// Embedding the window gives more control to the host, and feels more integrated. +/// Floating window are sometimes the only option due to technical limitations. +/// +/// Showing the GUI works as follow: +/// 1. clap_plugin_gui->is_api_supported(), check what can work +/// 2. clap_plugin_gui->create(), allocates gui resources +/// 3. if the plugin window is floating +/// 4. -> clap_plugin_gui->set_transient() +/// 5. -> clap_plugin_gui->suggest_title() +/// 6. else +/// 7. -> clap_plugin_gui->set_scale() +/// 8. -> clap_plugin_gui->can_resize() +/// 9. -> if resizable and has known size from previous session, clap_plugin_gui->set_size() +/// 10. -> else clap_plugin_gui->get_size(), gets initial size +/// 11. -> clap_plugin_gui->set_parent() +/// 12. clap_plugin_gui->show() +/// 13. clap_plugin_gui->hide()/show() ... +/// 14. clap_plugin_gui->destroy() when done with the gui +/// +/// Resizing the window (initiated by the plugin, if embedded): +/// 1. Plugins calls clap_host_gui->request_resize() +/// 2. If the host returns true the new size is accepted, +/// the host doesn't have to call clap_plugin_gui->set_size(). +/// If the host returns false, the new size is rejected. +/// +/// Resizing the window (drag, if embedded)): +/// 1. Only possible if clap_plugin_gui->can_resize() returns true +/// 2. Mouse drag -> new_size +/// 3. clap_plugin_gui->adjust_size(new_size) -> working_size +/// 4. clap_plugin_gui->set_size(working_size) + +static CLAP_CONSTEXPR const char CLAP_EXT_GUI[] = "clap.gui"; + +// If your windowing API is not listed here, please open an issue and we'll figure it out. +// https://github.com/free-audio/clap/issues/new + +// uses physical size +// embed using https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent +static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WIN32[] = "win32"; + +// uses logical size, don't call clap_plugin_gui->set_scale() +static const CLAP_CONSTEXPR char CLAP_WINDOW_API_COCOA[] = "cocoa"; + +// uses physical size +// embed using https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html +static const CLAP_CONSTEXPR char CLAP_WINDOW_API_X11[] = "x11"; + +// uses physical size +// embed is currently not supported, use floating windows +static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WAYLAND[] = "wayland"; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *clap_hwnd; +typedef void *clap_nsview; +typedef unsigned long clap_xwnd; + +// Represent a window reference. +typedef struct clap_window { + const char *api; // one of CLAP_WINDOW_API_XXX + union { + clap_nsview cocoa; + clap_xwnd x11; + clap_hwnd win32; + void *ptr; // for anything defined outside of clap + uintptr_t uptr; + }; +} clap_window_t; + +// Information to improve window resizing when initiated by the host or window manager. +typedef struct clap_gui_resize_hints { + bool can_resize_horizontally; + bool can_resize_vertically; + + // only if can resize horizontally and vertically + bool preserve_aspect_ratio; + uint32_t aspect_ratio_width; + uint32_t aspect_ratio_height; +} clap_gui_resize_hints_t; + +// Size (width, height) is in pixels; the corresponding windowing system extension is +// responsible for defining if it is physical pixels or logical pixels. +typedef struct clap_plugin_gui { + // Returns true if the requested gui api is supported + // [main-thread] + bool (*is_api_supported)(const clap_plugin_t *plugin, const char *api, bool is_floating); + + // Returns true if the plugin has a preferred api. + // The host has no obligation to honor the plugin preferrence, this is just a hint. + // [main-thread] + bool (*get_preferred_api)(const clap_plugin_t *plugin, const char **api, bool *is_floating); + + // Create and allocate all resources necessary for the gui. + // + // If is_floating is true, then the window will not be managed by the host. The plugin + // can set its window to stays above the parent window, see set_transient(). + // api may be null or blank for floating window. + // + // If is_floating is false, then the plugin has to embbed its window into the parent window, see + // set_parent(). + // + // After this call, the GUI may not be visible yet; don't forget to call show(). + // [main-thread] + bool (*create)(const clap_plugin_t *plugin, const char *api, bool is_floating); + + // Free all resources associated with the gui. + // [main-thread] + void (*destroy)(const clap_plugin_t *plugin); + + // Set the absolute GUI scaling factor, and override any OS info. + // Should not be used if the windowing api relies upon logical pixels. + // + // If the plugin prefers to work out the scaling factor itself by querying the OS directly, + // then ignore the call. + // + // Returns true if the scaling could be applied + // Returns false if the call was ignored, or the scaling could not be applied. + // [main-thread] + bool (*set_scale)(const clap_plugin_t *plugin, double scale); + + // Get the current size of the plugin UI. + // clap_plugin_gui->create() must have been called prior to asking the size. + // [main-thread] + bool (*get_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); + + // Returns true if the window is resizeable (mouse drag). + // Only for embedded windows. + // [main-thread] + bool (*can_resize)(const clap_plugin_t *plugin); + + // Returns true if the plugin can provide hints on how to resize the window. + // [main-thread] + bool (*get_resize_hints)(const clap_plugin_t *plugin, clap_gui_resize_hints_t *hints); + + // If the plugin gui is resizable, then the plugin will calculate the closest + // usable size which fits in the given size. + // This method does not change the size. + // + // Only for embedded windows. + // [main-thread] + bool (*adjust_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); + + // Sets the window size. Only for embedded windows. + // [main-thread] + bool (*set_size)(const clap_plugin_t *plugin, uint32_t width, uint32_t height); + + // Embbeds the plugin window into the given window. + // [main-thread & !floating] + bool (*set_parent)(const clap_plugin_t *plugin, const clap_window_t *window); + + // Set the plugin floating window to stay above the given window. + // [main-thread & floating] + bool (*set_transient)(const clap_plugin_t *plugin, const clap_window_t *window); + + // Suggests a window title. Only for floating windows. + // [main-thread & floating] + void (*suggest_title)(const clap_plugin_t *plugin, const char *title); + + // Show the window. + // [main-thread] + bool (*show)(const clap_plugin_t *plugin); + + // Hide the window, this method does not free the resources, it just hides + // the window content. Yet it may be a good idea to stop painting timers. + // [main-thread] + bool (*hide)(const clap_plugin_t *plugin); +} clap_plugin_gui_t; + +typedef struct clap_host_gui { + // The host should call get_resize_hints() again. + // [thread-safe] + void (*resize_hints_changed)(const clap_host_t *host); + + /* Request the host to resize the client area to width, height. + * Return true if the new size is accepted, false otherwise. + * The host doesn't have to call set_size(). + * + * Note: if not called from the main thread, then a return value simply means that the host + * acknowledged the request and will process it asynchronously. If the request then can't be + * satisfied then the host will call set_size() to revert the operation. + * + * [thread-safe] */ + bool (*request_resize)(const clap_host_t *host, uint32_t width, uint32_t height); + + /* Request the host to show the plugin gui. + * Return true on success, false otherwise. + * [thread-safe] */ + bool (*request_show)(const clap_host_t *host); + + /* Request the host to hide the plugin gui. + * Return true on success, false otherwise. + * [thread-safe] */ + bool (*request_hide)(const clap_host_t *host); + + // The floating window has been closed, or the connection to the gui has been lost. + // + // If was_destroyed is true, then the host must call clap_plugin_gui->destroy() to acknowledge + // the gui destruction. + // [thread-safe] + void (*closed)(const clap_host_t *host, bool was_destroyed); +} clap_host_gui_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/ext/params.h b/dpf/distrho/src/clap/ext/params.h new file mode 100644 index 0000000..93ab841 --- /dev/null +++ b/dpf/distrho/src/clap/ext/params.h @@ -0,0 +1,296 @@ +#pragma once + +#include "../plugin.h" +#include "../string-sizes.h" + +/// @page Parameters +/// @brief parameters management +/// +/// Main idea: +/// +/// The host sees the plugin as an atomic entity; and acts as a controller on top of its parameters. +/// The plugin is responsible for keeping its audio processor and its GUI in sync. +/// +/// The host can at any time read parameters' value on the [main-thread] using +/// @ref clap_plugin_params.value(). +/// +/// There are two options to communicate parameter value changes, and they are not concurrent. +/// - send automation points during clap_plugin.process() +/// - send automation points during clap_plugin_params.flush(), for parameter changes +/// without processing audio +/// +/// When the plugin changes a parameter value, it must inform the host. +/// It will send @ref CLAP_EVENT_PARAM_VALUE event during process() or flush(). +/// If the user is adjusting the value, don't forget to mark the begining and end +/// of the gesture by sending CLAP_EVENT_PARAM_GESTURE_BEGIN and CLAP_EVENT_PARAM_GESTURE_END +/// events. +/// +/// @note MIDI CCs are tricky because you may not know when the parameter adjustment ends. +/// Also if the host records incoming MIDI CC and parameter change automation at the same time, +/// there will be a conflict at playback: MIDI CC vs Automation. +/// The parameter automation will always target the same parameter because the param_id is stable. +/// The MIDI CC may have a different mapping in the future and may result in a different playback. +/// +/// When a MIDI CC changes a parameter's value, set the flag CLAP_EVENT_DONT_RECORD in +/// clap_event_param.header.flags. That way the host may record the MIDI CC automation, but not the +/// parameter change and there won't be conflict at playback. +/// +/// Scenarios: +/// +/// I. Loading a preset +/// - load the preset in a temporary state +/// - call @ref clap_host_params.rescan() if anything changed +/// - call @ref clap_host_latency.changed() if latency changed +/// - invalidate any other info that may be cached by the host +/// - if the plugin is activated and the preset will introduce breaking changes +/// (latency, audio ports, new parameters, ...) be sure to wait for the host +/// to deactivate the plugin to apply those changes. +/// If there are no breaking changes, the plugin can apply them them right away. +/// The plugin is resonsible for updating both its audio processor and its gui. +/// +/// II. Turning a knob on the DAW interface +/// - the host will send an automation event to the plugin via a process() or flush() +/// +/// III. Turning a knob on the Plugin interface +/// - the plugin is responsible for sending the parameter value to its audio processor +/// - call clap_host_params->request_flush() or clap_host->request_process(). +/// - when the host calls either clap_plugin->process() or clap_plugin_params->flush(), +/// send an automation event and don't forget to set begin_adjust, +/// end_adjust and should_record flags +/// +/// IV. Turning a knob via automation +/// - host sends an automation point during clap_plugin->process() or clap_plugin_params->flush(). +/// - the plugin is responsible for updating its GUI +/// +/// V. Turning a knob via plugin's internal MIDI mapping +/// - the plugin sends a CLAP_EVENT_PARAM_SET output event, set should_record to false +/// - the plugin is responsible to update its GUI +/// +/// VI. Adding or removing parameters +/// - if the plugin is activated call clap_host->restart() +/// - once the plugin isn't active: +/// - apply the new state +/// - if a parameter is gone or is created with an id that may have been used before, +/// call clap_host_params.clear(host, param_id, CLAP_PARAM_CLEAR_ALL) +/// - call clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) + +static CLAP_CONSTEXPR const char CLAP_EXT_PARAMS[] = "clap.params"; + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + // Is this param stepped? (integer values only) + // if so the double value is converted to integer using a cast (equivalent to trunc). + CLAP_PARAM_IS_STEPPED = 1 << 0, + + // Useful for for periodic parameters like a phase + CLAP_PARAM_IS_PERIODIC = 1 << 1, + + // The parameter should not be shown to the user, because it is currently not used. + // It is not necessary to process automation for this parameter. + CLAP_PARAM_IS_HIDDEN = 1 << 2, + + // The parameter can't be changed by the host. + CLAP_PARAM_IS_READONLY = 1 << 3, + + // This parameter is used to merge the plugin and host bypass button. + // It implies that the parameter is stepped. + // min: 0 -> bypass off + // max: 1 -> bypass on + CLAP_PARAM_IS_BYPASS = 1 << 4, + + // When set: + // - automation can be recorded + // - automation can be played back + // + // The host can send live user changes for this parameter regardless of this flag. + // + // If this parameters affect the internal processing structure of the plugin, ie: max delay, fft + // size, ... and the plugins needs to re-allocate its working buffers, then it should call + // host->request_restart(), and perform the change once the plugin is re-activated. + CLAP_PARAM_IS_AUTOMATABLE = 1 << 5, + + // Does this parameter support per note automations? + CLAP_PARAM_IS_AUTOMATABLE_PER_NOTE_ID = 1 << 6, + + // Does this parameter support per key automations? + CLAP_PARAM_IS_AUTOMATABLE_PER_KEY = 1 << 7, + + // Does this parameter support per channel automations? + CLAP_PARAM_IS_AUTOMATABLE_PER_CHANNEL = 1 << 8, + + // Does this parameter support per port automations? + CLAP_PARAM_IS_AUTOMATABLE_PER_PORT = 1 << 9, + + // Does this parameter support the modulation signal? + CLAP_PARAM_IS_MODULATABLE = 1 << 10, + + // Does this parameter support per note modulations? + CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID = 1 << 11, + + // Does this parameter support per key modulations? + CLAP_PARAM_IS_MODULATABLE_PER_KEY = 1 << 12, + + // Does this parameter support per channel modulations? + CLAP_PARAM_IS_MODULATABLE_PER_CHANNEL = 1 << 13, + + // Does this parameter support per port modulations? + CLAP_PARAM_IS_MODULATABLE_PER_PORT = 1 << 14, + + // Any change to this parameter will affect the plugin output and requires to be done via + // process() if the plugin is active. + // + // A simple example would be a DC Offset, changing it will change the output signal and must be + // processed. + CLAP_PARAM_REQUIRES_PROCESS = 1 << 15, +}; +typedef uint32_t clap_param_info_flags; + +/* This describes a parameter */ +typedef struct clap_param_info { + // stable parameter identifier, it must never change. + clap_id id; + + clap_param_info_flags flags; + + // This value is optional and set by the plugin. + // Its purpose is to provide a fast access to the plugin parameter: + // + // Parameter *p = findParameter(param_id); + // param_info->cookie = p; + // + // /* and later on */ + // Parameter *p = (Parameter *)cookie; + // + // It is invalidated on clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) and when the plugin is + // destroyed. + void *cookie; + + // the display name + char name[CLAP_NAME_SIZE]; + + // the module path containing the param, eg:"oscillators/wt1" + // '/' will be used as a separator to show a tree like structure. + char module[CLAP_PATH_SIZE]; + + double min_value; // minimum plain value + double max_value; // maximum plain value + double default_value; // default plain value +} clap_param_info_t; + +typedef struct clap_plugin_params { + // Returns the number of parameters. + // [main-thread] + uint32_t (*count)(const clap_plugin_t *plugin); + + // Copies the parameter's info to param_info and returns true on success. + // [main-thread] + bool (*get_info)(const clap_plugin_t *plugin, + uint32_t param_index, + clap_param_info_t *param_info); + + // Gets the parameter plain value. + // [main-thread] + bool (*get_value)(const clap_plugin_t *plugin, clap_id param_id, double *value); + + // Formats the display text for the given parameter value. + // The host should always format the parameter value to text using this function + // before displaying it to the user. + // [main-thread] + bool (*value_to_text)( + const clap_plugin_t *plugin, clap_id param_id, double value, char *display, uint32_t size); + + // Converts the display text to a parameter value. + // [main-thread] + bool (*text_to_value)(const clap_plugin_t *plugin, + clap_id param_id, + const char *display, + double *value); + + // Flushes a set of parameter changes. + // This method must not be called concurrently to clap_plugin->process(). + // + // [active ? audio-thread : main-thread] + void (*flush)(const clap_plugin_t *plugin, + const clap_input_events_t *in, + const clap_output_events_t *out); +} clap_plugin_params_t; + +enum { + // The parameter values did change, eg. after loading a preset. + // The host will scan all the parameters value. + // The host will not record those changes as automation points. + // New values takes effect immediately. + CLAP_PARAM_RESCAN_VALUES = 1 << 0, + + // The value to text conversion changed, and the text needs to be rendered again. + CLAP_PARAM_RESCAN_TEXT = 1 << 1, + + // The parameter info did change, use this flag for: + // - name change + // - module change + // - is_periodic (flag) + // - is_hidden (flag) + // New info takes effect immediately. + CLAP_PARAM_RESCAN_INFO = 1 << 2, + + // Invalidates everything the host knows about parameters. + // It can only be used while the plugin is deactivated. + // If the plugin is activated use clap_host->restart() and delay any change until the host calls + // clap_plugin->deactivate(). + // + // You must use this flag if: + // - some parameters were added or removed. + // - some parameters had critical changes: + // - is_per_note (flag) + // - is_per_channel (flag) + // - is_readonly (flag) + // - is_bypass (flag) + // - is_stepped (flag) + // - is_modulatable (flag) + // - min_value + // - max_value + // - cookie + CLAP_PARAM_RESCAN_ALL = 1 << 3, +}; +typedef uint32_t clap_param_rescan_flags; + +enum { + // Clears all possible references to a parameter + CLAP_PARAM_CLEAR_ALL = 1 << 0, + + // Clears all automations to a parameter + CLAP_PARAM_CLEAR_AUTOMATIONS = 1 << 1, + + // Clears all modulations to a parameter + CLAP_PARAM_CLEAR_MODULATIONS = 1 << 2, +}; +typedef uint32_t clap_param_clear_flags; + +typedef struct clap_host_params { + // Rescan the full list of parameters according to the flags. + // [main-thread] + void (*rescan)(const clap_host_t *host, clap_param_rescan_flags flags); + + // Clears references to a parameter. + // [main-thread] + void (*clear)(const clap_host_t *host, clap_id param_id, clap_param_clear_flags flags); + + // Request a parameter flush. + // + // The host will then schedule a call to either: + // - clap_plugin.process() + // - clap_plugin_params->flush() + // + // This function is always safe to use and should not be called from an [audio-thread] as the + // plugin would already be within process() or flush(). + // + // [thread-safe,!audio-thread] + void (*request_flush)(const clap_host_t *host); +} clap_host_params_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/fixedpoint.h b/dpf/distrho/src/clap/fixedpoint.h new file mode 100644 index 0000000..fb042d3 --- /dev/null +++ b/dpf/distrho/src/clap/fixedpoint.h @@ -0,0 +1,16 @@ +#pragma once + +#include "private/std.h" +#include "private/macros.h" + +/// We use fixed point representation of beat time and seconds time +/// Usage: +/// double x = ...; // in beats +/// clap_beattime y = round(CLAP_BEATTIME_FACTOR * x); + +// This will never change +static const CLAP_CONSTEXPR int64_t CLAP_BEATTIME_FACTOR = 1LL << 31; +static const CLAP_CONSTEXPR int64_t CLAP_SECTIME_FACTOR = 1LL << 31; + +typedef int64_t clap_beattime; +typedef int64_t clap_sectime; diff --git a/dpf/distrho/src/clap/host.h b/dpf/distrho/src/clap/host.h new file mode 100644 index 0000000..0b67aad --- /dev/null +++ b/dpf/distrho/src/clap/host.h @@ -0,0 +1,41 @@ +#pragma once + +#include "version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clap_host { + clap_version_t clap_version; // initialized to CLAP_VERSION + + void *host_data; // reserved pointer for the host + + // name and version are mandatory. + const char *name; // eg: "Bitwig Studio" + const char *vendor; // eg: "Bitwig GmbH" + const char *url; // eg: "https://bitwig.com" + const char *version; // eg: "4.3" + + // Query an extension. + // [thread-safe] + const void *(*get_extension)(const struct clap_host *host, const char *extension_id); + + // Request the host to deactivate and then reactivate the plugin. + // The operation may be delayed by the host. + // [thread-safe] + void (*request_restart)(const struct clap_host *host); + + // Request the host to activate and start processing the plugin. + // This is useful if you have external IO and need to wake up the plugin from "sleep". + // [thread-safe] + void (*request_process)(const struct clap_host *host); + + // Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread. + // [thread-safe] + void (*request_callback)(const struct clap_host *host); +} clap_host_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/id.h b/dpf/distrho/src/clap/id.h new file mode 100644 index 0000000..3b2b6e6 --- /dev/null +++ b/dpf/distrho/src/clap/id.h @@ -0,0 +1,8 @@ +#pragma once + +#include "private/std.h" +#include "private/macros.h" + +typedef uint32_t clap_id; + +static const CLAP_CONSTEXPR clap_id CLAP_INVALID_ID = UINT32_MAX; diff --git a/dpf/distrho/src/clap/plugin-factory.h b/dpf/distrho/src/clap/plugin-factory.h new file mode 100644 index 0000000..07e8560 --- /dev/null +++ b/dpf/distrho/src/clap/plugin-factory.h @@ -0,0 +1,39 @@ +#pragma once + +#include "plugin.h" + +static const CLAP_CONSTEXPR char CLAP_PLUGIN_FACTORY_ID[] = "clap.plugin-factory"; + +#ifdef __cplusplus +extern "C" { +#endif + +// Every method must be thread-safe. +// It is very important to be able to scan the plugin as quickly as possible. +// +// If the content of the factory may change due to external events, like the user installed +typedef struct clap_plugin_factory { + // Get the number of plugins available. + // [thread-safe] + uint32_t (*get_plugin_count)(const struct clap_plugin_factory *factory); + + // Retrieves a plugin descriptor by its index. + // Returns null in case of error. + // The descriptor must not be freed. + // [thread-safe] + const clap_plugin_descriptor_t *(*get_plugin_descriptor)( + const struct clap_plugin_factory *factory, uint32_t index); + + // Create a clap_plugin by its plugin_id. + // The returned pointer must be freed by calling plugin->destroy(plugin); + // The plugin is not allowed to use the host callbacks in the create method. + // Returns null in case of error. + // [thread-safe] + const clap_plugin_t *(*create_plugin)(const struct clap_plugin_factory *factory, + const clap_host_t *host, + const char *plugin_id); +} clap_plugin_factory_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/plugin-features.h b/dpf/distrho/src/clap/plugin-features.h new file mode 100644 index 0000000..624ab39 --- /dev/null +++ b/dpf/distrho/src/clap/plugin-features.h @@ -0,0 +1,76 @@ +#pragma once + +#include "private/macros.h" + +// This file provides a set of standard plugin features meant to be used +// within clap_plugin_descriptor.features. +// +// For practical reasons we'll avoid spaces and use `-` instead to facilitate +// scripts that generate the feature array. +// +// Non-standard features should be formated as follow: "$namespace:$feature" + +///////////////////// +// Plugin category // +///////////////////// + +// Add this feature if your plugin can process note events and then produce audio +#define CLAP_PLUGIN_FEATURE_INSTRUMENT "instrument" + +// Add this feature if your plugin is an audio effect +#define CLAP_PLUGIN_FEATURE_AUDIO_EFFECT "audio-effect" + +// Add this feature if your plugin is a note effect or a note generator/sequencer +#define CLAP_PLUGIN_FEATURE_NOTE_EFFECT "note-effect" + +// Add this feature if your plugin is an analyzer +#define CLAP_PLUGIN_FEATURE_ANALYZER "analyzer" + +///////////////////////// +// Plugin sub-category // +///////////////////////// + +#define CLAP_PLUGIN_FEATURE_SYNTHESIZER "synthesizer" +#define CLAP_PLUGIN_FEATURE_SAMPLER "sampler" +#define CLAP_PLUGIN_FEATURE_DRUM "drum" // For single drum +#define CLAP_PLUGIN_FEATURE_DRUM_MACHINE "drum-machine" + +#define CLAP_PLUGIN_FEATURE_FILTER "filter" +#define CLAP_PLUGIN_FEATURE_PHASER "phaser" +#define CLAP_PLUGIN_FEATURE_EQUALIZER "equalizer" +#define CLAP_PLUGIN_FEATURE_DEESSER "de-esser" +#define CLAP_PLUGIN_FEATURE_PHASE_VOCODER "phase-vocoder" +#define CLAP_PLUGIN_FEATURE_GRANULAR "granular" +#define CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER "frequency-shifter" +#define CLAP_PLUGIN_FEATURE_PITCH_SHIFTER "pitch-shifter" + +#define CLAP_PLUGIN_FEATURE_DISTORTION "distortion" +#define CLAP_PLUGIN_FEATURE_TRANSIENT_SHAPER "transient-shaper" +#define CLAP_PLUGIN_FEATURE_COMPRESSOR "compressor" +#define CLAP_PLUGIN_FEATURE_LIMITER "limiter" + +#define CLAP_PLUGIN_FEATURE_FLANGER "flanger" +#define CLAP_PLUGIN_FEATURE_CHORUS "chorus" +#define CLAP_PLUGIN_FEATURE_DELAY "delay" +#define CLAP_PLUGIN_FEATURE_REVERB "reverb" + +#define CLAP_PLUGIN_FEATURE_TREMOLO "tremolo" +#define CLAP_PLUGIN_FEATURE_GLITCH "glitch" + +#define CLAP_PLUGIN_FEATURE_UTILITY "utility" +#define CLAP_PLUGIN_FEATURE_PITCH_CORRECTION "pitch-correction" +#define CLAP_PLUGIN_FEATURE_RESTORATION "restoration" // repair the sound + +#define CLAP_PLUGIN_FEATURE_MULTI_EFFECTS "multi-effects" + +#define CLAP_PLUGIN_FEATURE_MIXING "mixing" +#define CLAP_PLUGIN_FEATURE_MASTERING "mastering" + +//////////////////////// +// Audio Capabilities // +//////////////////////// + +#define CLAP_PLUGIN_FEATURE_MONO "mono" +#define CLAP_PLUGIN_FEATURE_STEREO "stereo" +#define CLAP_PLUGIN_FEATURE_SURROUND "surround" +#define CLAP_PLUGIN_FEATURE_AMBISONIC "ambisonic" diff --git a/dpf/distrho/src/clap/plugin.h b/dpf/distrho/src/clap/plugin.h new file mode 100644 index 0000000..04cbc8e --- /dev/null +++ b/dpf/distrho/src/clap/plugin.h @@ -0,0 +1,96 @@ +#pragma once + +#include "private/macros.h" +#include "host.h" +#include "process.h" +#include "plugin-features.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clap_plugin_descriptor { + clap_version_t clap_version; // initialized to CLAP_VERSION + + // Mandatory fields must be set and must not be blank. + // Otherwise the fields can be null or blank, though it is safer to make them blank. + const char *id; // eg: "com.u-he.diva", mandatory + const char *name; // eg: "Diva", mandatory + const char *vendor; // eg: "u-he" + const char *url; // eg: "https://u-he.com/products/diva/" + const char *manual_url; // eg: "https://dl.u-he.com/manuals/plugins/diva/Diva-user-guide.pdf" + const char *support_url; // eg: "https://u-he.com/support/" + const char *version; // eg: "1.4.4" + const char *description; // eg: "The spirit of analogue" + + // Arbitrary list of keywords. + // They can be matched by the host indexer and used to classify the plugin. + // The array of pointers must be null terminated. + // For some standard features see plugin-features.h + const char **features; +} clap_plugin_descriptor_t; + +typedef struct clap_plugin { + const clap_plugin_descriptor_t *desc; + + void *plugin_data; // reserved pointer for the plugin + + // Must be called after creating the plugin. + // If init returns false, the host must destroy the plugin instance. + // [main-thread] + bool (*init)(const struct clap_plugin *plugin); + + // Free the plugin and its resources. + // It is required to deactivate the plugin prior to this call. + // [main-thread & !active] + void (*destroy)(const struct clap_plugin *plugin); + + // Activate and deactivate the plugin. + // In this call the plugin may allocate memory and prepare everything needed for the process + // call. The process's sample rate will be constant and process's frame count will included in + // the [min, max] range, which is bounded by [1, INT32_MAX]. + // Once activated the latency and port configuration must remain constant, until deactivation. + // + // [main-thread & !active_state] + bool (*activate)(const struct clap_plugin *plugin, + double sample_rate, + uint32_t min_frames_count, + uint32_t max_frames_count); + + // [main-thread & active_state] + void (*deactivate)(const struct clap_plugin *plugin); + + // Call start processing before processing. + // [audio-thread & active_state & !processing_state] + bool (*start_processing)(const struct clap_plugin *plugin); + + // Call stop processing before sending the plugin to sleep. + // [audio-thread & active_state & processing_state] + void (*stop_processing)(const struct clap_plugin *plugin); + + // - Clears all buffers, performs a full reset of the processing state (filters, oscillators, + // enveloppes, lfo, ...) and kills all voices. + // - The parameter's value remain unchanged. + // - clap_process.steady_time may jump backward. + // + // [audio-thread & active_state] + void (*reset)(const struct clap_plugin *plugin); + + // process audio, events, ... + // [audio-thread & active_state & processing_state] + clap_process_status (*process)(const struct clap_plugin *plugin, const clap_process_t *process); + + // Query an extension. + // The returned pointer is owned by the plugin. + // [thread-safe] + const void *(*get_extension)(const struct clap_plugin *plugin, const char *id); + + // Called by the host on the main thread in response to a previous call to: + // host->request_callback(host); + // [main-thread] + void (*on_main_thread)(const struct clap_plugin *plugin); +} clap_plugin_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/private/macros.h b/dpf/distrho/src/clap/private/macros.h new file mode 100644 index 0000000..1d62251 --- /dev/null +++ b/dpf/distrho/src/clap/private/macros.h @@ -0,0 +1,36 @@ +#pragma once + +// Define CLAP_EXPORT +#if !defined(CLAP_EXPORT) +# if defined _WIN32 || defined __CYGWIN__ +# ifdef __GNUC__ +# define CLAP_EXPORT __attribute__((dllexport)) +# else +# define CLAP_EXPORT __declspec(dllexport) +# endif +# else +# if __GNUC__ >= 4 || defined(__clang__) +# define CLAP_EXPORT __attribute__((visibility("default"))) +# else +# define CLAP_EXPORT +# endif +# endif +#endif + +#if defined(__cplusplus) && __cplusplus >= 201103L +# define CLAP_HAS_CXX11 +# define CLAP_CONSTEXPR constexpr +#else +# define CLAP_CONSTEXPR +#endif + +#if defined(__cplusplus) && __cplusplus >= 201703L +# define CLAP_HAS_CXX17 +# define CLAP_NODISCARD [[nodiscard]] +#else +# define CLAP_NODISCARD +#endif + +#if defined(__cplusplus) && __cplusplus >= 202002L +# define CLAP_HAS_CXX20 +#endif diff --git a/dpf/distrho/src/clap/private/std.h b/dpf/distrho/src/clap/private/std.h new file mode 100644 index 0000000..38555d7 --- /dev/null +++ b/dpf/distrho/src/clap/private/std.h @@ -0,0 +1,16 @@ +#pragma once + +#include "macros.h" + +#ifdef CLAP_HAS_CXX11 +# include +#else +# include +#endif + +#ifdef __cplusplus +# include +#else +# include +# include +#endif diff --git a/dpf/distrho/src/clap/process.h b/dpf/distrho/src/clap/process.h new file mode 100644 index 0000000..d5bf572 --- /dev/null +++ b/dpf/distrho/src/clap/process.h @@ -0,0 +1,65 @@ +#pragma once + +#include "events.h" +#include "audio-buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + // Processing failed. The output buffer must be discarded. + CLAP_PROCESS_ERROR = 0, + + // Processing succeeded, keep processing. + CLAP_PROCESS_CONTINUE = 1, + + // Processing succeeded, keep processing if the output is not quiet. + CLAP_PROCESS_CONTINUE_IF_NOT_QUIET = 2, + + // Rely upon the plugin's tail to determine if the plugin should continue to process. + // see clap_plugin_tail + CLAP_PROCESS_TAIL = 3, + + // Processing succeeded, but no more processing is required, + // until the next event or variation in audio input. + CLAP_PROCESS_SLEEP = 4, +}; +typedef int32_t clap_process_status; + +typedef struct clap_process { + // A steady sample time counter. + // This field can be used to calculate the sleep duration between two process calls. + // This value may be specific to this plugin instance and have no relation to what + // other plugin instances may receive. + // + // Set to -1 if not available, otherwise the value must be greater or equal to 0, + // and must be increased by at least `frames_count` for the next call to process. + int64_t steady_time; + + // Number of frames to process + uint32_t frames_count; + + // time info at sample 0 + // If null, then this is a free running host, no transport events will be provided + const clap_event_transport_t *transport; + + // Audio buffers, they must have the same count as specified + // by clap_plugin_audio_ports->get_count(). + // The index maps to clap_plugin_audio_ports->get_info(). + const clap_audio_buffer_t *audio_inputs; + clap_audio_buffer_t *audio_outputs; + uint32_t audio_inputs_count; + uint32_t audio_outputs_count; + + // Input and output events. + // + // Events must be sorted by time. + // The input event list can't be modified. + const clap_input_events_t *in_events; + const clap_output_events_t *out_events; +} clap_process_t; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/string-sizes.h b/dpf/distrho/src/clap/string-sizes.h new file mode 100644 index 0000000..4334c6d --- /dev/null +++ b/dpf/distrho/src/clap/string-sizes.h @@ -0,0 +1,21 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + // String capacity for names that can be displayed to the user. + CLAP_NAME_SIZE = 256, + + // String capacity for describing a path, like a parameter in a module hierarchy or path within a + // set of nested track groups. + // + // This is not suited for describing a file path on the disk, as NTFS allows up to 32K long + // paths. + CLAP_PATH_SIZE = 1024, +}; + +#ifdef __cplusplus +} +#endif diff --git a/dpf/distrho/src/clap/version.h b/dpf/distrho/src/clap/version.h new file mode 100644 index 0000000..7005ad4 --- /dev/null +++ b/dpf/distrho/src/clap/version.h @@ -0,0 +1,34 @@ +#pragma once + +#include "private/macros.h" +#include "private/std.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct clap_version { + // This is the major ABI and API design + // Version 0.X.Y correspond to the development stage, API and ABI are not stable + // Version 1.X.Y correspont to the release stage, API and ABI are stable + uint32_t major; + uint32_t minor; + uint32_t revision; +} clap_version_t; + +#ifdef __cplusplus +} +#endif + +#define CLAP_VERSION_MAJOR ((uint32_t)1) +#define CLAP_VERSION_MINOR ((uint32_t)1) +#define CLAP_VERSION_REVISION ((uint32_t)1) +#define CLAP_VERSION_INIT {CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION} + +static const CLAP_CONSTEXPR clap_version_t CLAP_VERSION = CLAP_VERSION_INIT; + +CLAP_NODISCARD static inline CLAP_CONSTEXPR bool +clap_version_is_compatible(const clap_version_t v) { + // versions 0.x.y were used during development stage and aren't compatible + return v.major >= 1; +} diff --git a/dpf/distrho/src/travesty/audio_processor.h b/dpf/distrho/src/travesty/audio_processor.h index 8783d8e..cc2d087 100644 --- a/dpf/distrho/src/travesty/audio_processor.h +++ b/dpf/distrho/src/travesty/audio_processor.h @@ -30,6 +30,23 @@ typedef uint64_t v3_speaker_arrangement; enum { V3_SPEAKER_L = 1 << 0, V3_SPEAKER_R = 1 << 1, + V3_SPEAKER_C = 1 << 2, + V3_SPEAKER_LFE = 1 << 3, + V3_SPEAKER_LS = 1 << 4, + V3_SPEAKER_RS = 1 << 5, + V3_SPEAKER_LC = 1 << 6, + V3_SPEAKER_RC = 1 << 7, + V3_SPEAKER_S = 1 << 8, + V3_SPEAKER_SL = 1 << 9, + V3_SPEAKER_SR = 1 << 10, + V3_SPEAKER_TC = 1 << 11, + V3_SPEAKER_TFL = 1 << 12, + V3_SPEAKER_TFC = 1 << 13, + V3_SPEAKER_TFR = 1 << 14, + V3_SPEAKER_TRL = 1 << 15, + V3_SPEAKER_TRC = 1 << 16, + V3_SPEAKER_TRR = 1 << 17, + V3_SPEAKER_LFE2 = 1 << 18, V3_SPEAKER_M = 1 << 19 }; diff --git a/dpf/utils/package-osx-bundles.sh b/dpf/utils/package-osx-bundles.sh index cb3ae62..cb10b56 100755 --- a/dpf/utils/package-osx-bundles.sh +++ b/dpf/utils/package-osx-bundles.sh @@ -44,7 +44,10 @@ cd .. DPF_UTILS_DIR=$(dirname ${0}) -sed -e "s|@name@|${NAME}|" ${DPF_UTILS_DIR}/plugin.pkg/welcome.txt.in > build/welcome.txt +# can be overridden by environment variables +WELCOME_TXT=${WELCOME_TXT:=${DPF_UTILS_DIR}/plugin.pkg/welcome.txt.in} + +sed -e "s|@name@|${NAME}|" "${WELCOME_TXT}" > build/welcome.txt sed -e "s|@builddir@|${PWD}/build|" \ -e "s|@lv2bundleref@|dpf-${SNAME}-lv2bundles.pkg|" \ -e "s|@vst2bundleref@|dpf-${SNAME}-vst2bundles.pkg|" \ diff --git a/dpf/utils/plugin.vst/Contents/Info.plist b/dpf/utils/plugin.bundle/Contents/Info.plist similarity index 100% rename from dpf/utils/plugin.vst/Contents/Info.plist rename to dpf/utils/plugin.bundle/Contents/Info.plist diff --git a/dpf/utils/plugin.vst/Contents/PkgInfo b/dpf/utils/plugin.bundle/Contents/PkgInfo similarity index 100% rename from dpf/utils/plugin.vst/Contents/PkgInfo rename to dpf/utils/plugin.bundle/Contents/PkgInfo diff --git a/dpf/utils/plugin.vst/Contents/Resources/empty.lproj b/dpf/utils/plugin.bundle/Contents/Resources/empty.lproj similarity index 100% rename from dpf/utils/plugin.vst/Contents/Resources/empty.lproj rename to dpf/utils/plugin.bundle/Contents/Resources/empty.lproj diff --git a/dpf/utils/res2c.py b/dpf/utils/res2c.py index 978af8b..f36312e 100755 --- a/dpf/utils/res2c.py +++ b/dpf/utils/res2c.py @@ -40,13 +40,13 @@ def res2c(namespace, filenames): for filename in filenames: shortFilename = filename.rsplit(os.sep, 1)[-1].split(".", 1)[0] - shortFilename = shortFilename.replace("-", "_") + shortFilename = shortFilename.replace("-", "_").replace("@","_") resData = open(filename, 'rb').read() print("Generating data for \"%s\"" % (filename)) - fdH.write(" extern const char* %sData;\n" % shortFilename) + fdH.write(" extern const unsigned char* %sData;\n" % shortFilename) fdH.write(" const unsigned int %sDataSize = %i;\n" % (shortFilename, len(resData))) if tempIndex != len(filenames): @@ -70,7 +70,7 @@ def res2c(namespace, filenames): curColumn += 1 fdC.write("};\n") - fdC.write("const char* %s::%sData = (const char*)temp_%s_%i;\n" % (namespace, shortFilename, shortFilename, tempIndex)) + fdC.write("const unsigned char* %s::%sData = (const unsigned char*)temp_%s_%i;\n" % (namespace, shortFilename, shortFilename, tempIndex)) if tempIndex != len(filenames): fdC.write("\n") @@ -89,25 +89,33 @@ def res2c(namespace, filenames): # ----------------------------------------------------- if __name__ == '__main__': - if len(sys.argv) != 3: - print("Usage: %s " % sys.argv[0]) + if len(sys.argv) not in (3, 4): + print("Usage: %s [output-folder=$CWD]" % sys.argv[0]) quit() namespace = sys.argv[1].replace("-","_") resFolder = sys.argv[2] + outFolder = sys.argv[3] if len(sys.argv) == 4 else None if not os.path.exists(resFolder): print("Folder '%s' does not exist" % resFolder) quit() + if outFolder is not None and not os.path.exists(outFolder): + print("Output folder '%s' does not exist" % outFolder) + quit() + # find resource files resFiles = [] for root, dirs, files in os.walk(resFolder): for name in files: - resFiles.append(os.path.join(root, name)) + resFiles.append(os.path.abspath(os.path.join(root, name))) resFiles.sort() + if outFolder is not None: + os.chdir(outFolder) + # create code now res2c(namespace, resFiles) diff --git a/dpf/utils/symbols/clap.def b/dpf/utils/symbols/clap.def new file mode 100644 index 0000000..3880022 --- /dev/null +++ b/dpf/utils/symbols/clap.def @@ -0,0 +1,2 @@ +EXPORTS +clap_entry diff --git a/dpf/utils/symbols/clap.exp b/dpf/utils/symbols/clap.exp new file mode 100644 index 0000000..cfd6965 --- /dev/null +++ b/dpf/utils/symbols/clap.exp @@ -0,0 +1 @@ +_clap_entry diff --git a/dpf/utils/symbols/clap.version b/dpf/utils/symbols/clap.version new file mode 100644 index 0000000..66c9184 --- /dev/null +++ b/dpf/utils/symbols/clap.version @@ -0,0 +1,4 @@ +{ + global: clap_entry; + local: *; +}; diff --git a/dpf/utils/valgrind-dpf.supp b/dpf/utils/valgrind-dpf.supp index a7b8f61..cf10237 100644 --- a/dpf/utils/valgrind-dpf.supp +++ b/dpf/utils/valgrind-dpf.supp @@ -21,8 +21,7 @@ { libdl is full of leaks Memcheck:Leak - fun:calloc - fun:allocate_dtv + ... fun:_dl_allocate_tls ... } @@ -32,6 +31,12 @@ ... fun:call_init.part.0 } +{ + libdl is really something else + Memcheck:Addr8 + ... + fun:dl_open_worker +} { ignore XInitThreads Memcheck:Leak @@ -46,3 +51,19 @@ fun:XrmGetStringDatabase ... } +{ + ignore XPutImage + Memcheck:Param + writev(vector[...]) + ... + fun:XPutImage + ... +} +{ + ignore mesa stuff + Memcheck:Value8 + ... + obj:/usr/lib/x86_64-linux-gnu/dri/swrast_dri.so + fun:start_thread + fun:clone +}