Browse Source

Updte dpf

Signed-off-by: falkTX <falktx@falktx.com>
tags/v1.6
falkTX 2 years ago
parent
commit
e07df08cd8
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
72 changed files with 5041 additions and 945 deletions
  1. +87
    -57
      dpf/Makefile.base.mk
  2. +52
    -4
      dpf/Makefile.plugins.mk
  3. +4
    -4
      dpf/cmake/DPF-plugin.cmake
  4. +1
    -1
      dpf/dgl/Color.hpp
  5. +28
    -5
      dpf/dgl/EventHandlers.hpp
  6. +71
    -0
      dpf/dgl/Layout.hpp
  7. +1
    -0
      dpf/dgl/Makefile
  8. +2
    -2
      dpf/dgl/NanoVG.hpp
  9. +6
    -0
      dpf/dgl/SubWidget.hpp
  10. +26
    -2
      dpf/dgl/Widget.hpp
  11. +1
    -1
      dpf/dgl/src/Color.cpp
  12. +96
    -44
      dpf/dgl/src/EventHandlers.cpp
  13. +2
    -2
      dpf/dgl/src/ImageBaseWidgets.cpp
  14. +201
    -0
      dpf/dgl/src/Layout.cpp
  15. +3
    -3
      dpf/dgl/src/NanoVG.cpp
  16. +10
    -1
      dpf/dgl/src/SubWidget.cpp
  17. +17
    -1
      dpf/dgl/src/Widget.cpp
  18. +4
    -1
      dpf/dgl/src/WidgetPrivateData.cpp
  19. +2
    -1
      dpf/dgl/src/WidgetPrivateData.hpp
  20. +6
    -1
      dpf/dgl/src/nanovg/nanovg.c
  21. +1
    -1
      dpf/dgl/src/nanovg/nanovg.h
  22. +0
    -4
      dpf/dgl/src/pugl-upstream/.clang-tidy
  23. +0
    -1
      dpf/dgl/src/pugl-upstream/src/.clang-tidy
  24. +1
    -1
      dpf/dgl/src/pugl-upstream/src/mac.m
  25. +95
    -12
      dpf/distrho/DistrhoInfo.hpp
  26. +72
    -2
      dpf/distrho/DistrhoPlugin.hpp
  27. +3
    -1
      dpf/distrho/DistrhoPluginMain.cpp
  28. +10
    -5
      dpf/distrho/DistrhoUIMain.cpp
  29. +1
    -1
      dpf/distrho/src/DistrhoDefines.h
  30. +22
    -16
      dpf/distrho/src/DistrhoPlugin.cpp
  31. +1471
    -0
      dpf/distrho/src/DistrhoPluginCLAP.cpp
  32. +11
    -0
      dpf/distrho/src/DistrhoPluginChecks.h
  33. +61
    -2
      dpf/distrho/src/DistrhoPluginInternal.hpp
  34. +89
    -57
      dpf/distrho/src/DistrhoPluginJACK.cpp
  35. +55
    -55
      dpf/distrho/src/DistrhoPluginLV2.cpp
  36. +97
    -0
      dpf/distrho/src/DistrhoPluginStub.cpp
  37. +15
    -50
      dpf/distrho/src/DistrhoPluginVST.hpp
  38. +22
    -14
      dpf/distrho/src/DistrhoPluginVST2.cpp
  39. +352
    -285
      dpf/distrho/src/DistrhoPluginVST3.cpp
  40. +16
    -6
      dpf/distrho/src/DistrhoUI.cpp
  41. +15
    -15
      dpf/distrho/src/DistrhoUIInternal.hpp
  42. +127
    -125
      dpf/distrho/src/DistrhoUILV2.cpp
  43. +3
    -3
      dpf/distrho/src/DistrhoUIPrivateData.hpp
  44. +127
    -0
      dpf/distrho/src/DistrhoUIStub.cpp
  45. +223
    -150
      dpf/distrho/src/DistrhoUIVST3.cpp
  46. +37
    -0
      dpf/distrho/src/clap/audio-buffer.h
  47. +68
    -0
      dpf/distrho/src/clap/entry.h
  48. +283
    -0
      dpf/distrho/src/clap/events.h
  49. +116
    -0
      dpf/distrho/src/clap/ext/audio-ports.h
  50. +219
    -0
      dpf/distrho/src/clap/ext/gui.h
  51. +296
    -0
      dpf/distrho/src/clap/ext/params.h
  52. +16
    -0
      dpf/distrho/src/clap/fixedpoint.h
  53. +41
    -0
      dpf/distrho/src/clap/host.h
  54. +8
    -0
      dpf/distrho/src/clap/id.h
  55. +39
    -0
      dpf/distrho/src/clap/plugin-factory.h
  56. +76
    -0
      dpf/distrho/src/clap/plugin-features.h
  57. +96
    -0
      dpf/distrho/src/clap/plugin.h
  58. +36
    -0
      dpf/distrho/src/clap/private/macros.h
  59. +16
    -0
      dpf/distrho/src/clap/private/std.h
  60. +65
    -0
      dpf/distrho/src/clap/process.h
  61. +21
    -0
      dpf/distrho/src/clap/string-sizes.h
  62. +34
    -0
      dpf/distrho/src/clap/version.h
  63. +17
    -0
      dpf/distrho/src/travesty/audio_processor.h
  64. +4
    -1
      dpf/utils/package-osx-bundles.sh
  65. +0
    -0
      dpf/utils/plugin.bundle/Contents/Info.plist
  66. +0
    -0
      dpf/utils/plugin.bundle/Contents/PkgInfo
  67. +0
    -0
      dpf/utils/plugin.bundle/Contents/Resources/empty.lproj
  68. +14
    -6
      dpf/utils/res2c.py
  69. +2
    -0
      dpf/utils/symbols/clap.def
  70. +1
    -0
      dpf/utils/symbols/clap.exp
  71. +4
    -0
      dpf/utils/symbols/clap.version
  72. +23
    -2
      dpf/utils/valgrind-dpf.supp

+ 87
- 57
dpf/Makefile.base.mk View File

@@ -8,6 +8,23 @@ AR ?= ar
CC ?= gcc CC ?= gcc
CXX ?= g++ 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 # Protect against multiple inclusion


@@ -16,7 +33,24 @@ ifneq ($(DPF_MAKEFILE_BASE_INCLUDED),true)
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) TARGET_MACHINE := $(shell $(CC) -dumpmachine)


@@ -57,7 +91,7 @@ endif # HAIKU
endif # BSD endif # BSD


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# Auto-detect the processor
# Auto-detect target processor


TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE))) TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE)))


@@ -75,15 +109,21 @@ CPU_I386_OR_X86_64 = true
endif endif
ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) ifneq (,$(filter arm%,$(TARGET_PROCESSOR)))
CPU_ARM = true CPU_ARM = true
CPU_ARM_OR_AARCH64 = true
CPU_ARM_OR_ARM64 = true
endif endif
ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) ifneq (,$(filter arm64%,$(TARGET_PROCESSOR)))
CPU_ARM64 = true CPU_ARM64 = true
CPU_ARM_OR_AARCH64 = true
CPU_ARM_OR_ARM64 = true
endif endif
ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) 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 endif


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
@@ -168,48 +208,56 @@ endif


BASE_FLAGS = -Wall -Wextra -pipe -MD -MP BASE_FLAGS = -Wall -Wextra -pipe -MD -MP
BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections 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 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 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 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 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) 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 += -O3
LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1
LINK_OPTS += -Wl,--gc-sections
else else
LINK_OPTS += -Wl,-O1,--gc-sections
LINK_OPTS += -Wl,--as-needed LINK_OPTS += -Wl,--as-needed
ifneq ($(SKIP_STRIPPING),true)
LINK_OPTS += -Wl,--strip-all
endif
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 endif

ifeq ($(SKIP_STRIPPING),true)
BASE_FLAGS += -g
endif endif


ifeq ($(NOOPT),true) ifeq ($(NOOPT),true)
@@ -217,22 +265,6 @@ ifeq ($(NOOPT),true)
BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections
endif 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) ifeq ($(DEBUG),true)
BASE_FLAGS += -DDEBUG -O0 -g BASE_FLAGS += -DDEBUG -O0 -g
LINK_OPTS = LINK_OPTS =
@@ -244,11 +276,6 @@ BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden
CXXFLAGS += -fvisibility-inlines-hidden CXXFLAGS += -fvisibility-inlines-hidden
endif endif


ifeq ($(STATIC_BUILD),true)
BASE_FLAGS += -DSTATIC_BUILD
# LINK_OPTS += -static
endif

ifeq ($(WITH_LTO),true) ifeq ($(WITH_LTO),true)
BASE_FLAGS += -fno-strict-aliasing -flto BASE_FLAGS += -fno-strict-aliasing -flto
LINK_OPTS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch 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 += -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 += -Wpointer-arith -Wabi=98 -Winit-self -Wuninitialized -Wstrict-overflow=5
# BASE_FLAGS += -Wfloat-equal # BASE_FLAGS += -Wfloat-equal
ifeq ($(CC),clang)
ifeq ($(CLANG),true)
BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command 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 BASE_FLAGS += -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-exit-time-destructors -Wno-float-equal
else else
@@ -300,7 +327,7 @@ BASE_FLAGS += -Wcast-align -Wunsafe-loop-optimizations
endif endif
ifneq ($(MACOS),true) ifneq ($(MACOS),true)
BASE_FLAGS += -Wmissing-declarations -Wsign-conversion BASE_FLAGS += -Wmissing-declarations -Wsign-conversion
ifneq ($(CC),clang)
ifeq ($(GCC),true)
BASE_FLAGS += -Wlogical-op BASE_FLAGS += -Wlogical-op
endif endif
endif endif
@@ -621,11 +648,14 @@ all:
print_available = @echo $(1): $(shell echo $($(1)) | grep -q true && echo Yes || echo No) print_available = @echo $(1): $(shell echo $($(1)) | grep -q true && echo Yes || echo No)


features: features:
@echo === Detected Compiler
$(call print_available,CLANG)
$(call print_available,GCC)
@echo === Detected CPU @echo === Detected CPU
$(call print_available,CPU_AARCH64)
$(call print_available,CPU_ARM) $(call print_available,CPU_ARM)
$(call print_available,CPU_ARM32)
$(call print_available,CPU_ARM64) $(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)
$(call print_available,CPU_I386_OR_X86_64) $(call print_available,CPU_I386_OR_X86_64)
@echo === Detected OS @echo === Detected OS


+ 52
- 4
dpf/Makefile.plugins.mk View File

@@ -239,6 +239,18 @@ VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3
endif endif
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 # Set plugin binary file targets


@@ -264,6 +276,7 @@ vst2 = $(TARGET_DIR)/$(VST2_FILENAME)
ifneq ($(VST3_FILENAME),) ifneq ($(VST3_FILENAME),)
vst3 = $(TARGET_DIR)/$(VST3_FILENAME) vst3 = $(TARGET_DIR)/$(VST3_FILENAME)
endif endif
clap = $(TARGET_DIR)/$(CLAP_FILENAME)
shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT)
static = $(TARGET_DIR)/$(NAME).a 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)/Info.plist
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/PkgInfo vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/PkgInfo
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Resources/empty.lproj 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 endif


ifneq ($(HAVE_DGL),true) 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_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp
SYMBOLS_VST2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst2.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_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 SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp
else ifeq ($(WASM),true) else ifeq ($(WASM),true)
SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" 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_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']"
SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']" SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']"
SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']" SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']"
SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']"
SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']"
else ifeq ($(WINDOWS),true) else ifeq ($(WINDOWS),true)
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def 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_LV2 = $(DPF_PATH)/utils/symbols/lv2.def
SYMBOLS_VST2 = $(DPF_PATH)/utils/symbols/vst2.def SYMBOLS_VST2 = $(DPF_PATH)/utils/symbols/vst2.def
SYMBOLS_VST3 = $(DPF_PATH)/utils/symbols/vst3.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 SYMBOLS_SHARED = $(DPF_PATH)/utils/symbols/shared.def
else ifneq ($(DEBUG),true) else ifneq ($(DEBUG),true)
SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version 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_LV2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2.version
SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version
SYMBOLS_VST3 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst3.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 SYMBOLS_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version
endif endif


@@ -521,6 +541,28 @@ endif
@echo "Creating VST3 plugin for $(NAME)" @echo "Creating VST3 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ $(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 # Shared


@@ -557,19 +599,23 @@ $(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/I
-@mkdir -p $(shell dirname $@) -@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ $(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 $@) -@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ $(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 $@) -@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ $(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 $@) -@mkdir -p $(shell dirname $@)
$(SILENT)cp $< $@ $(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 $@) -@mkdir -p $(shell dirname $@)
$(SILENT)cp $< $@ $(SILENT)cp $< $@


@@ -586,6 +632,7 @@ endif
-include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.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_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.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_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST3.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_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d




+ 4
- 4
dpf/cmake/DPF-plugin.cmake View File

@@ -336,9 +336,9 @@ function(dpf__build_vst2 NAME DGL_LIBRARY)
OUTPUT_NAME "${NAME}" OUTPUT_NAME "${NAME}"
SUFFIX "") SUFFIX "")
set(INFO_PLIST_PROJECT_NAME "${NAME}") 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) "${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") DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst/Contents")
endif() endif()
endfunction() endfunction()
@@ -425,9 +425,9 @@ function(dpf__build_vst3 NAME DGL_LIBRARY)
if(APPLE) if(APPLE)
# Uses the same macOS bundle template as VST2 # Uses the same macOS bundle template as VST2
set(INFO_PLIST_PROJECT_NAME "${NAME}") 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) "${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") DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents")
endif() endif()
endfunction() endfunction()


+ 1
- 1
dpf/dgl/Color.hpp View File

@@ -68,7 +68,7 @@ struct Color {
/** /**
Create a new color based on this one but with a different alpha value. 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. Create a color specified by hue, saturation and lightness.


+ 28
- 5
dpf/dgl/EventHandlers.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -94,7 +94,8 @@ class KnobEventHandler
public: public:
enum Orientation { enum Orientation {
Horizontal, Horizontal,
Vertical
Vertical,
Both
}; };


// NOTE hover not implemented yet // NOTE hover not implemented yet
@@ -112,6 +113,7 @@ public:
virtual void knobDragStarted(SubWidget* widget) = 0; virtual void knobDragStarted(SubWidget* widget) = 0;
virtual void knobDragFinished(SubWidget* widget) = 0; virtual void knobDragFinished(SubWidget* widget) = 0;
virtual void knobValueChanged(SubWidget* widget, float value) = 0; virtual void knobValueChanged(SubWidget* widget, float value) = 0;
virtual void knobDoubleClicked(SubWidget*) {};
}; };


explicit KnobEventHandler(SubWidget* self); explicit KnobEventHandler(SubWidget* self);
@@ -119,6 +121,9 @@ public:
KnobEventHandler& operator=(const KnobEventHandler& other); KnobEventHandler& operator=(const KnobEventHandler& other);
virtual ~KnobEventHandler(); 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 // returns raw value, is assumed to be scaled if using log
float getValue() const noexcept; float getValue() const noexcept;


@@ -139,12 +144,15 @@ public:
void setUsingLogScale(bool yesNo) noexcept; void setUsingLogScale(bool yesNo) noexcept;


Orientation getOrientation() const noexcept; Orientation getOrientation() const noexcept;
void setOrientation(const Orientation orientation) noexcept;
void setOrientation(Orientation orientation) noexcept;


void setCallback(Callback* callback) 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); bool scrollEvent(const Widget::ScrollEvent& ev);


protected: 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 END_NAMESPACE_DGL


#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED #endif // DGL_EVENT_HANDLERS_HPP_INCLUDED


+ 71
- 0
dpf/dgl/Layout.hpp View File

@@ -0,0 +1,71 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_LAYOUT_HPP_INCLUDED
#define DGL_LAYOUT_HPP_INCLUDED

#include "Geometry.hpp"

#include <list>

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<bool horizontal>
struct Layout
{
std::list<SubWidgetWithSizeHint> widgets;
uint setAbsolutePos(int x, int y, uint padding);
void setSize(uint size, uint padding);
};

typedef Layout<true> HorizontalLayout;
typedef Layout<false> VerticalLayout;

struct HorizontallyStackedVerticalLayout
{
std::list<VerticalLayout*> items;
Size<uint> adjustSize(uint padding); // TODO
void setAbsolutePos(int x, int y, uint padding);
};

struct VerticallyStackedHorizontalLayout
{
std::list<HorizontalLayout*> items;
Size<uint> adjustSize(uint padding);
void setAbsolutePos(int x, int y, uint padding);
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_LAYOUT_HPP_INCLUDED

+ 1
- 0
dpf/dgl/Makefile View File

@@ -34,6 +34,7 @@ OBJS_common = \
../build/dgl/Geometry.cpp.o \ ../build/dgl/Geometry.cpp.o \
../build/dgl/ImageBase.cpp.o \ ../build/dgl/ImageBase.cpp.o \
../build/dgl/ImageBaseWidgets.cpp.o \ ../build/dgl/ImageBaseWidgets.cpp.o \
../build/dgl/Layout.cpp.o \
../build/dgl/Resources.cpp.o \ ../build/dgl/Resources.cpp.o \
../build/dgl/SubWidget.cpp.o \ ../build/dgl/SubWidget.cpp.o \
../build/dgl/SubWidgetPrivateData.cpp.o \ ../build/dgl/SubWidgetPrivateData.cpp.o \


+ 2
- 2
dpf/dgl/NanoVG.hpp View File

@@ -591,14 +591,14 @@ public:
/** /**
Creates image by loading it from the specified chunk of memory. 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. Creates image by loading it from the specified chunk of memory.
Overloaded function for convenience. Overloaded function for convenience.
@see ImageFlags @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. Creates image from specified raw format image data.


+ 6
- 0
dpf/dgl/SubWidget.hpp View File

@@ -137,6 +137,12 @@ public:
*/ */
void repaint() noexcept override; 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. 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". Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front".


+ 26
- 2
dpf/dgl/Widget.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,6 +19,8 @@


#include "Geometry.hpp" #include "Geometry.hpp"


#include <list>

START_NAMESPACE_DGL START_NAMESPACE_DGL


// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
@@ -62,7 +64,7 @@ public:
uint mod; uint mod;
/** Event flags. @see EventFlag */ /** Event flags. @see EventFlag */
uint flags; uint flags;
/** Event timestamp (if any). */
/** Event timestamp in milliseconds (if any). */
uint time; uint time;


/** Constructor for default/null values */ /** Constructor for default/null values */
@@ -331,16 +333,33 @@ public:


/** /**
Get the Id associated with this widget. Get the Id associated with this widget.
Returns 0 by default.
@see setId @see setId
*/ */
uint getId() const noexcept; 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. Set an Id to be associated with this widget.
@see getId @see getId
*/ */
void setId(uint id) noexcept; 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. Get the application associated with this widget's window.
This is the same as calling `getTopLevelWidget()->getApp()`. This is the same as calling `getTopLevelWidget()->getApp()`.
@@ -367,6 +386,11 @@ public:
*/ */
TopLevelWidget* getTopLevelWidget() const noexcept; TopLevelWidget* getTopLevelWidget() const noexcept;


/**
Get list of children (a subwidgets) that belong to this widget.
*/
std::list<SubWidget*> getChildren() const noexcept;

/** /**
Request repaint of this widget's area to the window this widget belongs to. Request repaint of this widget's area to the window this widget belongs to.
On the raw Widget class this function does nothing. On the raw Widget class this function does nothing.


+ 1
- 1
dpf/dgl/src/Color.cpp View File

@@ -114,7 +114,7 @@ Color::Color(const Color& color1, const Color& color2, const float u) noexcept
interpolate(color2, u); interpolate(color2, u);
} }


Color Color::withAlpha(const float alpha2) noexcept
Color Color::withAlpha(const float alpha2) const noexcept
{ {
Color color(*this); Color color(*this);
color.alpha = alpha2; color.alpha = alpha2;


+ 96
- 44
dpf/dgl/src/EventHandlers.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -274,6 +274,7 @@ struct KnobEventHandler::PrivateData {
SubWidget* const widget; SubWidget* const widget;
KnobEventHandler::Callback* callback; KnobEventHandler::Callback* callback;


float accel;
float minimum; float minimum;
float maximum; float maximum;
float step; float step;
@@ -287,13 +288,15 @@ struct KnobEventHandler::PrivateData {


double lastX; double lastX;
double lastY; double lastY;
uint lastClickTime;


PrivateData(KnobEventHandler* const s, SubWidget* const w) PrivateData(KnobEventHandler* const s, SubWidget* const w)
: self(s), : self(s),
widget(w), widget(w),
callback(nullptr), callback(nullptr),
minimum(0.0f),
maximum(1.0f),
accel(200.f),
minimum(0.f),
maximum(1.f),
step(0.0f), step(0.0f),
value(0.5f), value(0.5f),
valueDef(value), valueDef(value),
@@ -303,12 +306,14 @@ struct KnobEventHandler::PrivateData {
orientation(Vertical), orientation(Vertical),
state(kKnobStateDefault), state(kKnobStateDefault),
lastX(0.0), lastX(0.0),
lastY(0.0) {}
lastY(0.0),
lastClickTime(0) {}


PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other) PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other)
: self(s), : self(s),
widget(w), widget(w),
callback(other->callback), callback(other->callback),
accel(other->accel),
minimum(other->minimum), minimum(other->minimum),
maximum(other->maximum), maximum(other->maximum),
step(other->step), step(other->step),
@@ -320,11 +325,13 @@ struct KnobEventHandler::PrivateData {
orientation(other->orientation), orientation(other->orientation),
state(kKnobStateDefault), state(kKnobStateDefault),
lastX(0.0), lastX(0.0),
lastY(0.0) {}
lastY(0.0),
lastClickTime(0) {}


void assignFrom(PrivateData* const other) void assignFrom(PrivateData* const other)
{ {
callback = other->callback; callback = other->callback;
accel = other->accel;
minimum = other->minimum; minimum = other->minimum;
maximum = other->maximum; maximum = other->maximum;
step = other->step; step = other->step;
@@ -337,6 +344,7 @@ struct KnobEventHandler::PrivateData {
state = kKnobStateDefault; state = kKnobStateDefault;
lastX = 0.0; lastX = 0.0;
lastY = 0.0; lastY = 0.0;
lastClickTime = 0;
} }


inline float logscale(const float v) const inline float logscale(const float v) const
@@ -353,7 +361,7 @@ struct KnobEventHandler::PrivateData {
return std::log(v/a)/b; 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) if (ev.button != 1)
return false; return false;
@@ -370,9 +378,21 @@ struct KnobEventHandler::PrivateData {
return true; 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; state |= kKnobStateDragging;
lastX = ev.pos.getX();
lastY = ev.pos.getY();
widget->repaint(); widget->repaint();


if (callback != nullptr) if (callback != nullptr)
@@ -394,62 +414,87 @@ struct KnobEventHandler::PrivateData {
return false; return false;
} }


bool motionEvent(const Widget::MotionEvent& ev)
bool motionEvent(const Widget::MotionEvent& ev, const double scaleFactor)
{ {
if ((state & kKnobStateDragging) == 0x0) if ((state & kKnobStateDragging) == 0x0)
return false; 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; return false;


const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel;
valueTmp += (maximum - minimum) / divisor * movDiff;

if (usingLog) if (usingLog)
value2 = logscale(value2);
valueTmp = logscale(valueTmp);


if (value2 < minimum)
float value2;
bool valueChanged = false;

if (valueTmp < minimum)
{ {
valueTmp = value2 = minimum; valueTmp = value2 = minimum;
valueChanged = true;
} }
else if (value2 > maximum)
else if (valueTmp > maximum)
{ {
valueTmp = value2 = maximum; valueTmp = value2 = maximum;
valueChanged = true;
} }
else else
{ {
valueTmp = value2;

if (d_isNotZero(step)) 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; return true;
} }
@@ -460,7 +505,7 @@ struct KnobEventHandler::PrivateData {
return false; return false;


const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f; 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) float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp)
+ ((maximum - minimum) / d * 10.f * dir); + ((maximum - minimum) / d * 10.f * dir);


@@ -553,6 +598,11 @@ KnobEventHandler::~KnobEventHandler()
delete pData; delete pData;
} }


bool KnobEventHandler::isInteger() const noexcept
{
return d_isEqual(pData->step, 1.f);
}

float KnobEventHandler::getValue() const noexcept float KnobEventHandler::getValue() const noexcept
{ {
return pData->value; return pData->value;
@@ -596,9 +646,6 @@ KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept


void KnobEventHandler::setOrientation(const Orientation orientation) noexcept void KnobEventHandler::setOrientation(const Orientation orientation) noexcept
{ {
if (pData->orientation == orientation)
return;

pData->orientation = orientation; pData->orientation = orientation;
} }


@@ -607,14 +654,19 @@ void KnobEventHandler::setCallback(Callback* const callback) noexcept
pData->callback = callback; 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) bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev)


+ 2
- 2
dpf/dgl/src/ImageBaseWidgets.cpp View File

@@ -395,7 +395,7 @@ bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
{ {
if (SubWidget::onMouse(ev)) if (SubWidget::onMouse(ev))
return true; return true;
return KnobEventHandler::mouseEvent(ev);
return KnobEventHandler::mouseEvent(ev, getTopLevelWidget()->getScaleFactor());
} }


template <class ImageType> template <class ImageType>
@@ -403,7 +403,7 @@ bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev)
{ {
if (SubWidget::onMotion(ev)) if (SubWidget::onMotion(ev))
return true; return true;
return KnobEventHandler::motionEvent(ev);
return KnobEventHandler::motionEvent(ev, getTopLevelWidget()->getScaleFactor());
} }


template <class ImageType> template <class ImageType>


+ 201
- 0
dpf/dgl/src/Layout.cpp View File

@@ -0,0 +1,201 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../Layout.hpp"
#include "../SubWidget.hpp"

START_NAMESPACE_DGL

typedef std::list<SubWidgetWithSizeHint>::iterator SubWidgetWithSizeHintIterator;
typedef std::list<HorizontalLayout*>::iterator HorizontalLayoutIterator;
typedef std::list<VerticalLayout*>::iterator VerticalLayoutIterator;

// --------------------------------------------------------------------------------------------------------------------

template<> // horizontal
uint Layout<true>::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<false>::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<true>::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<false>::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<uint> 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<uint>(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

+ 3
- 3
dpf/dgl/src/NanoVG.cpp View File

@@ -664,18 +664,18 @@ NanoImage::Handle NanoVG::createImageFromFile(const char* filename, int imageFla
return NanoImage::Handle(fContext, nvgCreateImage(fContext, filename, imageFlags)); 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<int>(imageFlags)); return createImageFromMemory(data, dataSize, static_cast<int>(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(); if (fContext == nullptr) return NanoImage::Handle();
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle());
DISTRHO_SAFE_ASSERT_RETURN(dataSize > 0, NanoImage::Handle()); DISTRHO_SAFE_ASSERT_RETURN(dataSize > 0, NanoImage::Handle());


return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast<int>(dataSize)));
return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data, static_cast<int>(dataSize)));
} }


NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data,


+ 10
- 1
dpf/dgl/src/SubWidget.cpp View File

@@ -139,12 +139,21 @@ void SubWidget::repaint() noexcept
if (TopLevelWidget* const topw = getTopLevelWidget()) if (TopLevelWidget* const topw = getTopLevelWidget())
{ {
if (pData->needsFullViewportForDrawing) 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 else
topw->repaint(getConstrainedAbsoluteArea()); topw->repaint(getConstrainedAbsoluteArea());
} }
} }


void SubWidget::toBottom()
{
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets);

subwidgets.remove(this);
subwidgets.insert(subwidgets.begin(), this);
}

void SubWidget::toFront() void SubWidget::toFront()
{ {
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets); std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets);


+ 17
- 1
dpf/dgl/src/Widget.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * 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; return pData->topLevelWidget;
} }


std::list<SubWidget*> Widget::getChildren() const noexcept
{
return pData->subWidgets;
}

void Widget::repaint() noexcept void Widget::repaint() noexcept
{ {
} }
@@ -157,11 +162,22 @@ uint Widget::getId() const noexcept
return pData->id; return pData->id;
} }


const char* Widget::getName() const noexcept
{
return pData->name != nullptr ? pData->name : "";
}

void Widget::setId(uint id) noexcept void Widget::setId(uint id) noexcept
{ {
pData->id = id; 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) bool Widget::onKeyboard(const KeyboardEvent& ev)
{ {
return pData->giveKeyboardEventForSubWidgets(ev); return pData->giveKeyboardEventForSubWidgets(ev);


+ 4
- 1
dpf/dgl/src/WidgetPrivateData.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * 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), topLevelWidget(tlw),
parentWidget(nullptr), parentWidget(nullptr),
id(0), id(0),
name(nullptr),
needsScaling(false), needsScaling(false),
visible(true), visible(true),
size(0, 0), size(0, 0),
@@ -43,6 +44,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw)
topLevelWidget(findTopLevelWidget(pw)), topLevelWidget(findTopLevelWidget(pw)),
parentWidget(pw), parentWidget(pw),
id(0), id(0),
name(nullptr),
needsScaling(false), needsScaling(false),
visible(true), visible(true),
size(0, 0), size(0, 0),
@@ -51,6 +53,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw)
Widget::PrivateData::~PrivateData() Widget::PrivateData::~PrivateData()
{ {
subWidgets.clear(); subWidgets.clear();
std::free(name);
} }


void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor) void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor)


+ 2
- 1
dpf/dgl/src/WidgetPrivateData.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -30,6 +30,7 @@ struct Widget::PrivateData {
TopLevelWidget* const topLevelWidget; TopLevelWidget* const topLevelWidget;
Widget* const parentWidget; Widget* const parentWidget;
uint id; uint id;
char* name;
bool needsScaling; bool needsScaling;
bool visible; bool visible;
Size<uint> size; Size<uint> size;


+ 6
- 1
dpf/dgl/src/nanovg/nanovg.c View File

@@ -23,6 +23,11 @@


#include "nanovg.h" #include "nanovg.h"
#define FONTSTASH_IMPLEMENTATION #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" #include "fontstash.h"


#ifndef NVG_NO_STB #ifndef NVG_NO_STB
@@ -869,7 +874,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags)
return image; 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; int w, h, n, image;
unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);


+ 1
- 1
dpf/dgl/src/nanovg/nanovg.h View File

@@ -385,7 +385,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);


// Creates image by loading it from the specified chunk of memory. // Creates image by loading it from the specified chunk of memory.
// Returns handle to the image. // 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. // Creates image from specified image data and texture format.
// Returns handle to the image. // Returns handle to the image.


+ 0
- 4
dpf/dgl/src/pugl-upstream/.clang-tidy View File

@@ -1,4 +0,0 @@
Checks: >
*,
FormatStyle: file
WarningsAsErrors: '*'

+ 0
- 1
dpf/dgl/src/pugl-upstream/src/.clang-tidy View File

@@ -8,7 +8,6 @@ Checks: >
-hicpp-signed-bitwise, -hicpp-signed-bitwise,
-llvm-header-guard, -llvm-header-guard,
-llvmlibc-*, -llvmlibc-*,
-performance-no-int-to-ptr,
-readability-function-cognitive-complexity, -readability-function-cognitive-complexity,
FormatStyle: file FormatStyle: file
HeaderFilterRegex: 'pugl/.*' HeaderFilterRegex: 'pugl/.*'

+ 1
- 1
dpf/dgl/src/pugl-upstream/src/mac.m View File

@@ -1378,7 +1378,7 @@ puglProcessEvents(PuglView* view)
double double
puglGetTime(const PuglWorld* world) puglGetTime(const PuglWorld* world)
{ {
return (mach_absolute_time() / 1e9) - world->startTime;
return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9) - world->startTime;
} }


PuglStatus PuglStatus


+ 95
- 12
dpf/distrho/DistrhoInfo.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * 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 Let's begin with some examples.@n
Here is one of a stereo audio plugin that simply mutes the host output: Here is one of a stereo audio plugin that simply mutes the host output:
@code @code
/* DPF plugin include */
#include "DistrhoPlugin.hpp"

/* Make DPF related classes available for us to use without any extra namespace references */ /* Make DPF related classes available for us to use without any extra namespace references */
USE_NAMESPACE_DISTRHO; USE_NAMESPACE_DISTRHO;


@@ -115,7 +118,7 @@ START_NAMESPACE_DISTRHO


/** /**
Get the plugin unique Id. 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 int64_t getUniqueId() const override
{ {
@@ -157,7 +160,7 @@ START_NAMESPACE_DISTRHO
A plugin is nothing without parameters.@n A plugin is nothing without parameters.@n
In DPF parameters can be inputs or outputs.@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 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. 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 (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) void initProgramName(uint32_t index, String& programName)
{ {
// we only have one program so we can skip checking the index // we only have one program so we can skip checking the index
@@ -374,6 +377,8 @@ START_NAMESPACE_DISTRHO
return fGainL; return fGainL;
case 1; case 1;
return fGainR; return fGainR;
default:
return 0.f;
} }
} }


@@ -618,6 +623,24 @@ START_NAMESPACE_DISTRHO
*/ */
#define DISTRHO_UI_CUSTOM_WIDGET_TYPE #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 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. 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 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:AllpassPlugin
- lv2:AmplifierPlugin - lv2:AmplifierPlugin
- lv2:AnalyserPlugin - lv2:AnalyserPlugin
@@ -688,7 +710,7 @@ START_NAMESPACE_DISTRHO


/** /**
Custom VST3 categories for the plugin.@n 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: Each effect category can be one of the following values:


@@ -723,9 +745,70 @@ START_NAMESPACE_DISTRHO
- Instrument|Synth - Instrument|Synth
- Instrument|Synth|Sampler - 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"


/** @} */ /** @} */




+ 72
- 2
dpf/distrho/DistrhoPlugin.hpp View File

@@ -362,7 +362,7 @@ struct ParameterRanges {
*/ */
float getNormalizedValue(const float& value) const noexcept 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) if (normValue <= 0.0f)
return 0.0f; return 0.0f;
@@ -371,6 +371,21 @@ struct ParameterRanges {
return normValue; 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. Get a value normalized to 0.0<->1.0, fixed within range.
*/ */
@@ -381,7 +396,7 @@ struct ParameterRanges {
if (value >= max) if (value >= max)
return 1.0f; return 1.0f;


const float normValue((value - min) / (max - min));
const float normValue = (value - min) / (max - min);


if (normValue <= 0.0f) if (normValue <= 0.0f)
return 0.0f; return 0.0f;
@@ -391,6 +406,27 @@ struct ParameterRanges {
return normValue; 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. Get a proper value previously normalized to 0.0<->1.0.
*/ */
@@ -403,6 +439,20 @@ struct ParameterRanges {


return value * (max - min) + min; 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. @note This value is optional and only used for LV2.
*/ */
String description; 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; 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 #if DISTRHO_PLUGIN_WANT_TIMEPOS
/** /**
Get the current host transport time position.@n Get the current host transport time position.@n


+ 3
- 1
dpf/distrho/DistrhoPluginMain.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,6 +18,8 @@


#if defined(DISTRHO_PLUGIN_TARGET_CARLA) #if defined(DISTRHO_PLUGIN_TARGET_CARLA)
# include "src/DistrhoPluginCarla.cpp" # include "src/DistrhoPluginCarla.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP)
# include "src/DistrhoPluginCLAP.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_JACK) #elif defined(DISTRHO_PLUGIN_TARGET_JACK)
# include "src/DistrhoPluginJACK.cpp" # include "src/DistrhoPluginJACK.cpp"
#elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI)) #elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI))


+ 10
- 5
dpf/distrho/DistrhoUIMain.cpp View File

@@ -17,24 +17,29 @@
#include "src/DistrhoUI.cpp" #include "src/DistrhoUI.cpp"


#if defined(DISTRHO_PLUGIN_TARGET_CARLA) #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) #elif defined(DISTRHO_PLUGIN_TARGET_JACK)
// nothing
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) #elif defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 0
# include "src/DistrhoUIDSSI.cpp" # include "src/DistrhoUIDSSI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_LV2) #elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# include "src/DistrhoUILV2.cpp" # include "src/DistrhoUILV2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST2) #elif defined(DISTRHO_PLUGIN_TARGET_VST2)
// nothing
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#elif defined(DISTRHO_PLUGIN_TARGET_VST3) #elif defined(DISTRHO_PLUGIN_TARGET_VST3)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
# include "src/DistrhoUIVST3.cpp" # include "src/DistrhoUIVST3.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC) #elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC)
// nothing
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#else #else
# error unsupported format # error unsupported format
#endif #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 # ifdef DISTRHO_PLUGIN_TARGET_DSSI
# define DISTRHO_IS_STANDALONE 1 # define DISTRHO_IS_STANDALONE 1
# else # else


+ 1
- 1
dpf/distrho/src/DistrhoDefines.h View File

@@ -211,7 +211,7 @@ private: \
#endif #endif


/* Useful macros */ /* Useful macros */
#define ARRAY_SIZE(ARRAY) sizeof(ARRAY)/sizeof(ARRAY[0])
#define ARRAY_SIZE(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))


/* Useful typedefs */ /* Useful typedefs */
typedef unsigned char uchar; typedef unsigned char uchar;


+ 22
- 16
dpf/distrho/src/DistrhoPlugin.cpp View File

@@ -25,6 +25,7 @@ uint32_t d_nextBufferSize = 0;
double d_nextSampleRate = 0.0; double d_nextSampleRate = 0.0;
const char* d_nextBundlePath = nullptr; const char* d_nextBundlePath = nullptr;
bool d_nextPluginIsDummy = false; bool d_nextPluginIsDummy = false;
bool d_nextPluginIsSelfTest = false;
bool d_nextCanRequestParameterValueChanges = false; bool d_nextCanRequestParameterValueChanges = false;


/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
@@ -42,45 +43,45 @@ const PortGroupWithId PluginExporter::sFallbackPortGroup;
Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount)
: pData(new PrivateData()) : 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]; 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) if (parameterCount > 0)
{ {
pData->parameterCount = parameterCount; pData->parameterCount = parameterCount;
pData->parameters = new Parameter[parameterCount];
pData->parameters = new Parameter[parameterCount];
} }


if (programCount > 0) if (programCount > 0)
{ {
#if DISTRHO_PLUGIN_WANT_PROGRAMS
#if DISTRHO_PLUGIN_WANT_PROGRAMS
pData->programCount = programCount; pData->programCount = programCount;
pData->programNames = new String[programCount]; pData->programNames = new String[programCount];
#else
#else
d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1");
DPF_ABORT DPF_ABORT
#endif
#endif
} }


if (stateCount > 0) if (stateCount > 0)
{ {
#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
pData->stateCount = stateCount; 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"); d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1");
DPF_ABORT DPF_ABORT
#endif
#endif
} }


#undef DPF_ABORT
#undef DPF_ABORT
} }


Plugin::~Plugin() Plugin::~Plugin()
@@ -111,6 +112,11 @@ bool Plugin::isDummyInstance() const noexcept
return pData->isDummy; return pData->isDummy;
} }


bool Plugin::isSelfTestInstance() const noexcept
{
return pData->isSelfTest;
}

#if DISTRHO_PLUGIN_WANT_TIMEPOS #if DISTRHO_PLUGIN_WANT_TIMEPOS
const TimePosition& Plugin::getTimePosition() const noexcept const TimePosition& Plugin::getTimePosition() const noexcept
{ {


+ 1471
- 0
dpf/distrho/src/DistrhoPluginCLAP.cpp
File diff suppressed because it is too large
View File


+ 11
- 0
dpf/distrho/src/DistrhoPluginChecks.h View File

@@ -183,6 +183,17 @@
# define DISTRHO_PLUGIN_HAS_UI 0 # define DISTRHO_PLUGIN_HAS_UI 0
#endif #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 // Prevent users from messing about with DPF internals




+ 61
- 2
dpf/distrho/src/DistrhoPluginInternal.hpp View File

@@ -39,6 +39,7 @@ extern uint32_t d_nextBufferSize;
extern double d_nextSampleRate; extern double d_nextSampleRate;
extern const char* d_nextBundlePath; extern const char* d_nextBundlePath;
extern bool d_nextPluginIsDummy; extern bool d_nextPluginIsDummy;
extern bool d_nextPluginIsSelfTest;
extern bool d_nextCanRequestParameterValueChanges; extern bool d_nextCanRequestParameterValueChanges;


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -67,7 +68,8 @@ struct PortGroupWithId : PortGroup {
groupId(kPortGroupNone) {} groupId(kPortGroupNone) {}
}; };


static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup)
static inline
void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup)
{ {
switch (groupId) 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<typename T>
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<float>(dst, value, "%f", size);
}

static inline
void snprintf_f32(char* const dst, const double value, const size_t size)
{
return snprintf_t<double>(dst, value, "%f", size);
}

static inline
void snprintf_i32(char* const dst, const int32_t value, const size_t size)
{
return snprintf_t<int32_t>(dst, value, "%d", size);
}

static inline
void snprintf_u32(char* const dst, const uint32_t value, const size_t size)
{
return snprintf_t<uint32_t>(dst, value, "%u", size);
}

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Plugin private data // Plugin private data


struct Plugin::PrivateData { struct Plugin::PrivateData {
const bool canRequestParameterValueChanges; const bool canRequestParameterValueChanges;
const bool isDummy; const bool isDummy;
const bool isSelfTest;
bool isProcessing; bool isProcessing;


#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
@@ -136,6 +188,7 @@ struct Plugin::PrivateData {
PrivateData() noexcept PrivateData() noexcept
: canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges), : canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges),
isDummy(d_nextPluginIsDummy), isDummy(d_nextPluginIsDummy),
isSelfTest(d_nextPluginIsSelfTest),
isProcessing(false), isProcessing(false),
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioPorts(nullptr), audioPorts(nullptr),
@@ -324,7 +377,8 @@ public:
# if DISTRHO_PLUGIN_WANT_STATE # if DISTRHO_PLUGIN_WANT_STATE
if (fData->stateCount != 0) if (fData->stateCount != 0)
{ {
if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState)
if ((void*)(fPlugin->*(static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))) ==
(void*)static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))
{ {
d_stderr2("DPF warning: Plugins with state must implement `initState`"); d_stderr2("DPF warning: Plugins with state must implement `initState`");
abort(); abort();
@@ -593,6 +647,11 @@ public:
return (getParameterHints(index) & kParameterIsOutput) != 0x0; 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 bool isParameterTrigger(const uint32_t index) const noexcept
{ {
return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger; return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger;


+ 89
- 57
dpf/distrho/src/DistrhoPluginJACK.cpp View File

@@ -38,6 +38,12 @@
#include "jackbridge/JackBridge.cpp" #include "jackbridge/JackBridge.cpp"
#include "lv2/lv2.h" #include "lv2/lv2.h"


#ifdef DISTRHO_OS_MAC
# define Point CocoaPoint
# include <CoreFoundation/CoreFoundation.h>
# undef Point
#endif

#ifndef DISTRHO_OS_WINDOWS #ifndef DISTRHO_OS_WINDOWS
# include <signal.h> # include <signal.h>
# include <unistd.h> # include <unistd.h>
@@ -789,11 +795,11 @@ protected:


while (! shouldThreadExit()) while (! shouldThreadExit())
{ {
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
plugin.run(inputs, outputs, 128, nullptr, 0); plugin.run(inputs, outputs, 128, nullptr, 0);
#else
#else
plugin.run(inputs, outputs, 128); plugin.run(inputs, outputs, 128);
#endif
#endif
d_msleep(100); d_msleep(100);
} }


@@ -807,7 +813,7 @@ bool runSelfTests()
{ {
d_nextBufferSize = 512; d_nextBufferSize = 512;
d_nextSampleRate = 44100.0; d_nextSampleRate = 44100.0;
PluginExporter plugin(nullptr, nullptr, nullptr);
PluginExporter plugin(nullptr, nullptr, nullptr, nullptr);
d_nextBufferSize = 0; d_nextBufferSize = 0;
d_nextSampleRate = 0.0; d_nextSampleRate = 0.0;
} }
@@ -818,7 +824,17 @@ bool runSelfTests()


// simple processing // 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.activate();
plugin.deactivate(); plugin.deactivate();
plugin.setBufferSize(128); plugin.setBufferSize(128);
@@ -833,20 +849,26 @@ bool runSelfTests()
for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
outputs[i] = buffer; outputs[i] = buffer;


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
plugin.run(inputs, outputs, 128, nullptr, 0); plugin.run(inputs, outputs, 128, nullptr, 0);
#else
#else
plugin.run(inputs, outputs, 128); plugin.run(inputs, outputs, 128);
#endif
#endif


plugin.deactivate(); plugin.deactivate();

#if DISTRHO_PLUGIN_HAS_UI
ui.plugin_idle();
#endif
} }


return true;

// multi-threaded processing with UI // multi-threaded processing with UI
{ {
PluginExporter pluginA(nullptr, nullptr, nullptr);
PluginExporter pluginB(nullptr, nullptr, nullptr);
PluginExporter pluginC(nullptr, nullptr, nullptr);
PluginExporter pluginA(nullptr, nullptr, nullptr, nullptr);
PluginExporter pluginB(nullptr, nullptr, nullptr, nullptr);
PluginExporter pluginC(nullptr, nullptr, nullptr, nullptr);
PluginProcessTestingThread procTestA(pluginA); PluginProcessTestingThread procTestA(pluginA);
PluginProcessTestingThread procTestB(pluginB); PluginProcessTestingThread procTestB(pluginB);
PluginProcessTestingThread procTestC(pluginC); PluginProcessTestingThread procTestC(pluginC);
@@ -860,7 +882,7 @@ bool runSelfTests()
// stop the 2nd instance now // stop the 2nd instance now
procTestB.stopThread(5000); procTestB.stopThread(5000);


#if DISTRHO_PLUGIN_HAS_UI
#if DISTRHO_PLUGIN_HAS_UI
// start UI in the middle of this // start UI in the middle of this
{ {
UIExporter uiA(nullptr, 0, pluginA.getSampleRate(), UIExporter uiA(nullptr, 0, pluginA.getSampleRate(),
@@ -887,7 +909,7 @@ bool runSelfTests()
d_msleep(100); d_msleep(100);
} }
} }
#endif
#endif


procTestA.stopThread(5000); procTestA.stopThread(5000);
procTestC.stopThread(5000); procTestC.stopThread(5000);
@@ -905,12 +927,47 @@ int main(int argc, char* argv[])
{ {
USE_NAMESPACE_DISTRHO; USE_NAMESPACE_DISTRHO;


#ifdef DPF_RUNTIME_TESTING
initSignalHandler();

#if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD)
// find plugin bundle
static String bundlePath;
if (bundlePath.isEmpty())
{
String tmpPath(getBinaryFilename());
tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
#ifdef DISTRHO_OS_MAC
if (tmpPath.endsWith("/MacOS"))
{
tmpPath.truncate(tmpPath.rfind('/'));
if (tmpPath.endsWith("/Contents"))
{
tmpPath.truncate(tmpPath.rfind('/'));
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
}
#else
if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0)
{
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
#endif
}
#endif

if (argc == 2 && std::strcmp(argv[1], "selftest") == 0) if (argc == 2 && std::strcmp(argv[1], "selftest") == 0)
{
#ifdef DPF_RUNTIME_TESTING
return runSelfTests() ? 0 : 1; return runSelfTests() ? 0 : 1;
#endif
#else
d_stderr2("Code was built without DPF_RUNTIME_TESTING macro enabled, selftest option is not available");
return 1;
#endif
}


#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
/* the code below is based on /* 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/ * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/
*/ */
@@ -938,7 +995,7 @@ int main(int argc, char* argv[])


hasConsole = true; hasConsole = true;
} }
#endif
#endif


jack_status_t status = jack_status_t(0x0); jack_status_t status = jack_status_t(0x0);
jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status);
@@ -982,7 +1039,17 @@ int main(int argc, char* argv[])
else else
d_stderr("Failed to create the JACK client, cannot continue!"); d_stderr("Failed to create the JACK client, cannot continue!");


#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
#if defined(DISTRHO_OS_MAC)
CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr,
DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8);
CFStringRef errorStringRef = CFStringCreateWithCString(nullptr,
String("Failed to create JACK client, reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8);

CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel,
nullptr, nullptr, nullptr,
errorTitleRef, errorStringRef,
nullptr, nullptr, nullptr, nullptr);
#elif defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
// make sure message box is high-dpi aware // make sure message box is high-dpi aware
if (const HMODULE user32 = LoadLibrary("user32.dll")) if (const HMODULE user32 = LoadLibrary("user32.dll"))
{ {
@@ -1007,49 +1074,19 @@ int main(int argc, char* argv[])
return 1; return 1;
} }


initSignalHandler();

d_nextBufferSize = jackbridge_get_buffer_size(client); d_nextBufferSize = jackbridge_get_buffer_size(client);
d_nextSampleRate = jackbridge_get_sample_rate(client); d_nextSampleRate = jackbridge_get_sample_rate(client);
d_nextCanRequestParameterValueChanges = true; d_nextCanRequestParameterValueChanges = true;


#if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD)
// find plugin bundle
static String bundlePath;
if (bundlePath.isEmpty())
{
String tmpPath(getBinaryFilename());
tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
#ifdef DISTRHO_OS_MAC
if (tmpPath.endsWith("/MacOS"))
{
tmpPath.truncate(tmpPath.rfind('/'));
if (tmpPath.endsWith("/Contents"))
{
tmpPath.truncate(tmpPath.rfind('/'));
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
}
#else
if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0)
{
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
#endif
}
#endif

uintptr_t winId = 0; uintptr_t winId = 0;
#if DISTRHO_PLUGIN_HAS_UI
#if DISTRHO_PLUGIN_HAS_UI
if (argc == 3 && std::strcmp(argv[1], "embed") == 0) if (argc == 3 && std::strcmp(argv[1], "embed") == 0)
winId = static_cast<uintptr_t>(std::atoll(argv[2])); winId = static_cast<uintptr_t>(std::atoll(argv[2]));
#endif
#endif


const PluginJack p(client, winId); 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 /* 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/ * 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 ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
SendInput(1, &ip, sizeof(INPUT)); SendInput(1, &ip, sizeof(INPUT));
} }
#endif
#endif


return 0; return 0;

#ifndef DPF_RUNTIME_TESTING
// unused
(void)argc; (void)argv;
#endif
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------

+ 55
- 55
dpf/distrho/src/DistrhoPluginLV2.cpp View File

@@ -620,22 +620,22 @@ public:
// Run plugin // Run plugin
if (sampleCount != 0) if (sampleCount != 0)
{ {
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
fRunCount = mod_license_run_begin(fRunCount, sampleCount); 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); fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount);
#else
#else
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); 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<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
mod_license_run_silence(fRunCount, fPortAudioOuts[i], sampleCount, i); mod_license_run_silence(fRunCount, fPortAudioOuts[i], sampleCount, i);
#endif
#endif


#if DISTRHO_PLUGIN_WANT_TIMEPOS
#if DISTRHO_PLUGIN_WANT_TIMEPOS
// update timePos for next callback // update timePos for next callback
if (d_isNotZero(fLastPositionData.speed)) if (d_isNotZero(fLastPositionData.speed))
{ {
@@ -691,12 +691,12 @@ public:


fPlugin.setTimePosition(fTimePosition); fPlugin.setTimePosition(fTimePosition);
} }
#endif
#endif
} }


updateParameterOutputsAndTriggers(); updateParameterOutputsAndTriggers();


#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
fEventsOutData.initIfNeeded(fURIDs.atomSequence); fEventsOutData.initIfNeeded(fURIDs.atomSequence);


LV2_Atom_Event* aev; LV2_Atom_Event* aev;
@@ -797,11 +797,11 @@ public:
break; break;
} }
} }
#endif
#endif


#if DISTRHO_LV2_USE_EVENTS_OUT
#if DISTRHO_LV2_USE_EVENTS_OUT
fEventsOutData.endRun(); fEventsOutData.endRun();
#endif
#endif
} }


// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -860,7 +860,7 @@ public:


// ------------------------------------------------------------------- // -------------------------------------------------------------------


#if DISTRHO_PLUGIN_WANT_PROGRAMS
#if DISTRHO_PLUGIN_WANT_PROGRAMS
const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) const LV2_Program_Descriptor* lv2_get_program(const uint32_t index)
{ {
if (index >= fPlugin.getProgramCount()) if (index >= fPlugin.getProgramCount())
@@ -895,30 +895,30 @@ public:
setPortControlValue(i, fLastControlValues[i]); setPortControlValue(i, fLastControlValues[i]);
} }


# if DISTRHO_PLUGIN_WANT_FULL_STATE
#if DISTRHO_PLUGIN_WANT_FULL_STATE
// Update state // Update state
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{ {
const String& key = cit->first; const String& key = cit->first;
fStateMap[key] = fPlugin.getStateValue(key); 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) 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 // Update current state
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{ {
const String& key = cit->first; const String& key = cit->first;
fStateMap[key] = fPlugin.getStateValue(key); fStateMap[key] = fPlugin.getStateValue(key);
} }
# endif
#endif


String lv2key; String lv2key;
LV2_URID urid; LV2_URID urid;
@@ -1021,11 +1021,11 @@ public:


setState(key, value); setState(key, value);


#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
// signal msg needed for UI // signal msg needed for UI
if ((hints & kStateIsOnlyForDSP) == 0x0) if ((hints & kStateIsOnlyForDSP) == 0x0)
fNeededUiSends[i] = true; fNeededUiSends[i] = true;
#endif
#endif
} }


return LV2_STATE_SUCCESS; return LV2_STATE_SUCCESS;
@@ -1090,16 +1090,16 @@ public:
{ {
return LV2_WORKER_SUCCESS; return LV2_WORKER_SUCCESS;
} }
#endif
#endif


// ------------------------------------------------------------------- // -------------------------------------------------------------------


#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
void* lv2_get_instance_pointer() void* lv2_get_instance_pointer()
{ {
return fPlugin.getInstancePointer(); return fPlugin.getInstancePointer();
} }
#endif
#endif


// ------------------------------------------------------------------- // -------------------------------------------------------------------


@@ -1107,36 +1107,36 @@ private:
PluginExporter fPlugin; PluginExporter fPlugin;
const bool fUsingNominal; // if false use maxBlockLength const bool fUsingNominal; // if false use maxBlockLength


#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
uint32_t fRunCount; uint32_t fRunCount;
#endif
#endif


// LV2 ports // LV2 ports
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
const float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; const float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS];
#else
#else
const float** fPortAudioIns; const float** fPortAudioIns;
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
#else
#else
float** fPortAudioOuts; float** fPortAudioOuts;
#endif
#endif
float** fPortControls; float** fPortControls;
#if DISTRHO_LV2_USE_EVENTS_IN
#if DISTRHO_LV2_USE_EVENTS_IN
LV2_Atom_Sequence* fPortEventsIn; LV2_Atom_Sequence* fPortEventsIn;
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
float* fPortLatency; float* fPortLatency;
#endif
#endif


// Temporary data // Temporary data
float* fLastControlValues; float* fLastControlValues;
double fSampleRate; double fSampleRate;
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
MidiEvent fMidiEvents[kMaxMidiEvents]; MidiEvent fMidiEvents[kMaxMidiEvents];
#endif
#if DISTRHO_PLUGIN_WANT_TIMEPOS
#endif
#if DISTRHO_PLUGIN_WANT_TIMEPOS
TimePosition fTimePosition; TimePosition fTimePosition;


struct Lv2PositionData { struct Lv2PositionData {
@@ -1160,9 +1160,9 @@ private:
ticksPerBeat(-1.0) {} ticksPerBeat(-1.0) {}


} fLastPositionData; } fLastPositionData;
#endif
#endif


#if DISTRHO_LV2_USE_EVENTS_OUT
#if DISTRHO_LV2_USE_EVENTS_OUT
struct Lv2EventsOutData { struct Lv2EventsOutData {
uint32_t capacity, offset; uint32_t capacity, offset;
LV2_Atom_Sequence* port; LV2_Atom_Sequence* port;
@@ -1198,7 +1198,7 @@ private:
} }


} fEventsOutData; } fEventsOutData;
#endif
#endif


// LV2 URIDs // LV2 URIDs
struct URIDs { struct URIDs {
@@ -1262,13 +1262,13 @@ private:
} fURIDs; } fURIDs;


// LV2 features // 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; const LV2_ControlInputPort_Change_Request* const fCtrlInPortChangeReq;
#endif
#endif
const LV2_URID_Map* const fUridMap; const LV2_URID_Map* const fUridMap;
const LV2_Worker_Schedule* const fWorker; const LV2_Worker_Schedule* const fWorker;


#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
LV2_Atom_Forge fAtomForge; LV2_Atom_Forge fAtomForge;
StringToStringMap fStateMap; StringToStringMap fStateMap;
UridToStringMap fUridStateMap; UridToStringMap fUridStateMap;
@@ -1321,7 +1321,7 @@ private:
d_stderr("Failed to find plugin state with key \"%s\"", key); d_stderr("Failed to find plugin state with key \"%s\"", key);
return false; return false;
} }
#endif
#endif


void updateParameterOutputsAndTriggers() void updateParameterOutputsAndTriggers()
{ {
@@ -1341,13 +1341,13 @@ private:
} }
} }


#if DISTRHO_PLUGIN_WANT_LATENCY
#if DISTRHO_PLUGIN_WANT_LATENCY
if (fPortLatency != nullptr) if (fPortLatency != nullptr)
*fPortLatency = fPlugin.getLatency(); *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) bool requestParameterValueChange(const uint32_t index, const float value)
{ {
if (fCtrlInPortChangeReq == nullptr) if (fCtrlInPortChangeReq == nullptr)
@@ -1359,16 +1359,16 @@ private:
{ {
return (((PluginLv2*)ptr)->requestParameterValueChange(index, value) == 0); 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) static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value)
{ {
return ((PluginLv2*)ptr)->updateState(key, 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) bool writeMidi(const MidiEvent& midiEvent)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fEventsOutData.port != nullptr, false); DISTRHO_SAFE_ASSERT_RETURN(fEventsOutData.port != nullptr, false);
@@ -1398,7 +1398,7 @@ private:
{ {
return ((PluginLv2*)ptr)->writeMidi(midiEvent); return ((PluginLv2*)ptr)->writeMidi(midiEvent);
} }
#endif
#endif
}; };


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


+ 97
- 0
dpf/distrho/src/DistrhoPluginStub.cpp View File

@@ -0,0 +1,97 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "DistrhoPluginInternal.hpp"

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

// --------------------------------------------------------------------------------------------------------------------

+ 15
- 50
dpf/distrho/src/DistrhoPluginVST.hpp View File

@@ -67,24 +67,24 @@ START_NAMESPACE_DISTRHO
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------


enum Vst3InternalParameters { enum Vst3InternalParameters {
#if DPF_VST3_USES_SEPARATE_CONTROLLER
#if DPF_VST3_USES_SEPARATE_CONTROLLER
kVst3InternalParameterBufferSize, kVst3InternalParameterBufferSize,
kVst3InternalParameterSampleRate, kVst3InternalParameterSampleRate,
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
kVst3InternalParameterLatency, kVst3InternalParameterLatency,
#endif
#if DISTRHO_PLUGIN_WANT_PROGRAMS
#endif
#if DISTRHO_PLUGIN_WANT_PROGRAMS
kVst3InternalParameterProgram, kVst3InternalParameterProgram,
#endif
#endif
kVst3InternalParameterBaseCount, kVst3InternalParameterBaseCount,
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount,
kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16,
kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end
#else
#else
kVst3InternalParameterCount = kVst3InternalParameterBaseCount kVst3InternalParameterCount = kVst3InternalParameterBaseCount
#endif
#endif
}; };


#if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT #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 static inline
void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length) 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<typename T> template<typename T>
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<typename T>
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,); 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 static inline
void snprintf_f32(char* const dst, const float value, const size_t size)
{
return snprintf_t<float>(dst, value, "%f", size);
}

static inline
void snprintf_i32(char* const dst, const int32_t value, const size_t size)
{
return snprintf_t<int32_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<uint32_t>(dst, value, "%u", size);
return snprintf_utf16_t<float>(dst, value, "%f", size);
} }


static inline 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<float>(dst, value, "%f", size);
return snprintf_utf16_t<double>(dst, value, "%f", size);
} }


static inline static inline


+ 22
- 14
dpf/distrho/src/DistrhoPluginVST2.cpp View File

@@ -65,7 +65,7 @@ START_NAMESPACE_DISTRHO


typedef std::map<const String, String> StringMap; typedef std::map<const String, String> StringMap;


static const int kVstMidiEventSize = static_cast<int>(sizeof(VstMidiEvent));
static constexpr const int kVstMidiEventSize = static_cast<int>(sizeof(VstMidiEvent));


#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
static const writeMidiFunc writeMidiCallback = nullptr; static const writeMidiFunc writeMidiCallback = nullptr;
@@ -117,7 +117,7 @@ struct ParameterAndNotesHelper
} }


#if DISTRHO_PLUGIN_WANT_STATE #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 #endif
}; };


@@ -575,17 +575,25 @@ public:
} }
else 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(), UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, 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(); 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.right /= scaleFactor;
fVstRect.bottom /= scaleFactor; fVstRect.bottom /= scaleFactor;
# endif
tmpUI.quit();
#endif
} }
*(ERect**)ptr = &fVstRect; *(ERect**)ptr = &fVstRect;
return 1; return 1;
@@ -995,7 +1003,7 @@ public:
else else
fTimePosition.bbt.beatsPerMinute = 120.0; 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 double ppqPos = std::abs(vstTimeInfo->ppqPos);
const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator; const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator;
@@ -1131,7 +1139,7 @@ private:
{ {
if (fPlugin.isParameterOutput(i)) 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); curValue = fPlugin.getParameterValue(i);


if (d_isEqual(curValue, parameterValues[i])) if (d_isEqual(curValue, parameterValues[i]))
@@ -1230,12 +1238,12 @@ private:
// functions called from the UI side, may block // functions called from the UI side, may block


# if DISTRHO_PLUGIN_HAS_UI # 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 # else
void setStateFromUI(const char* const key, const char* const newValue)
void setStateFromUI(const char* const key, const char* const value)
# endif # endif
{ {
fPlugin.setState(key, newValue);
fPlugin.setState(key, value);


// check if we want to save this key // check if we want to save this key
if (! fPlugin.wantStateKey(key)) if (! fPlugin.wantStateKey(key))
@@ -1248,7 +1256,7 @@ private:


if (dkey == key) if (dkey == key)
{ {
it->second = newValue;
it->second = value;
return; return;
} }
} }


+ 352
- 285
dpf/distrho/src/DistrhoPluginVST3.cpp
File diff suppressed because it is too large
View File


+ 16
- 6
dpf/distrho/src/DistrhoUI.cpp View File

@@ -202,13 +202,23 @@ UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint hei
* UI */ * UI */


UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize) 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) uiData(UI::PrivateData::s_nextPrivateData)
{ {
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
if (width != 0 && height != 0) if (width != 0 && height != 0)
{ {
#ifndef DISTRHO_UI_DEFAULT_WIDTH
Widget::setSize(width, height); Widget::setSize(width, height);
#endif


if (automaticallyScaleAndSetAsMinimumSize) if (automaticallyScaleAndSetAsMinimumSize)
setGeometryConstraints(width, height, true, true, true); setGeometryConstraints(width, height, true, true, true);
@@ -405,19 +415,19 @@ void UI::onResize(const ResizeEvent& ev)
#endif #endif
} }


// NOTE: only used for VST3
// NOTE: only used for VST3 and CLAP
void UI::requestSizeChange(const uint width, const uint height) 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) if (uiData->initializing)
uiData->window->setSizeForVST3(width, height);
uiData->window->setSizeFromHost(width, height);
else else
uiData->setSizeCallback(width, height); uiData->setSizeCallback(width, height);
# else
#else
// unused // unused
(void)width; (void)width;
(void)height; (void)height;
# endif
#endif
} }
#endif #endif




+ 15
- 15
dpf/distrho/src/DistrhoUIInternal.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * 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 * 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() void idleForVST3()
{ {
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
@@ -271,7 +271,7 @@ public:
ui->uiIdle(); ui->uiIdle();
} }


# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs) void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs)
{ {
uiData->window->addIdleCallback(cb, timerFrequencyInMs); uiData->window->addIdleCallback(cb, timerFrequencyInMs);
@@ -281,31 +281,31 @@ public:
{ {
uiData->window->removeIdleCallback(cb); uiData->window->removeIdleCallback(cb);
} }
# endif
#endif
#endif
#endif


// ------------------------------------------------------------------- // -------------------------------------------------------------------


void setWindowOffset(const int x, const int y) void setWindowOffset(const int x, const int y)
{ {
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO // TODO
(void)x; (void)y; (void)x; (void)y;
#else
#else
uiData->window->setOffset(x, y); 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); 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) void setWindowTitle(const char* const uiTitle)
{ {


+ 127
- 125
dpf/distrho/src/DistrhoUILV2.cpp View File

@@ -42,8 +42,11 @@ typedef struct _LV2_Atom_MidiEvent {
uint8_t data[3]; /**< MIDI data (body). */ uint8_t data[3]; /**< MIDI data (body). */
} LV2_Atom_MidiEvent; } LV2_Atom_MidiEvent;


#if ! DISTRHO_PLUGIN_WANT_STATE
static constexpr const setStateFunc setStateCallback = nullptr;
#endif
#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
static const sendNoteFunc sendNoteCallback = nullptr;
static constexpr const sendNoteFunc sendNoteCallback = nullptr;
#endif #endif


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -76,19 +79,8 @@ public:
const float scaleFactor, const float scaleFactor,
const uint32_t bgColor, const uint32_t bgColor,
const uint32_t fgColor) 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<LV2_URID_Unmap>(features, LV2_URID__unmap)),
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)), fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)), fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)), fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
@@ -97,15 +89,23 @@ public:
fURIDs(uridMap), fURIDs(uridMap),
fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled") fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled")
: LV2UI_INVALID_PORT_INDEX), : 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) if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); *widget = (LV2UI_Widget)fUI.getNativeWindowHandle();


#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
// tell the DSP we're ready to receive msgs // tell the DSP we're ready to receive msgs
setState("__dpf_ui_data__", ""); setState("__dpf_ui_data__", "");
#endif
#endif


if (winId != 0) if (winId != 0)
return; return;
@@ -169,7 +169,7 @@ public:


fUI.parameterChanged(rindex-parameterOffset, value); fUI.parameterChanged(rindex-parameterOffset, value);
} }
#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
else if (format == fURIDs.atomEventTransfer) else if (format == fURIDs.atomEventTransfer)
{ {
const LV2_Atom* const atom = (const LV2_Atom*)buffer; const LV2_Atom* const atom = (const LV2_Atom*)buffer;
@@ -181,39 +181,44 @@ public:


fUI.stateChanged(key, value); 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* 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(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(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 && 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); fUI.stateChanged(key, value);
} }
*/
} }
else 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) void lv2ui_select_program(const uint32_t bank, const uint32_t program)
{ {
const uint32_t realProgram = bank * 128 + program; const uint32_t realProgram = bank * 128 + program;


fUI.programLoaded(realProgram); 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) void editParameterValue(const uint32_t rindex, const bool started)
{ {
if (fUiTouch != nullptr && fUiTouch->touch != nullptr) if (fUiTouch != nullptr && fUiTouch->touch != nullptr)
fUiTouch->touch(fUiTouch->handle, rindex, started); fUiTouch->touch(fUiTouch->handle, rindex, started);
} }


static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started)
{
static_cast<UiLv2*>(ptr)->editParameterValue(rindex, started);
}

void setParameterValue(const uint32_t rindex, float value) void setParameterValue(const uint32_t rindex, float value)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
@@ -297,6 +369,12 @@ protected:
fWriteFunction(fController, rindex, sizeof(float), 0, &value); fWriteFunction(fController, rindex, sizeof(float), 0, &value);
} }


static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
{
static_cast<UiLv2*>(ptr)->setParameterValue(rindex, value);
}

#if DISTRHO_PLUGIN_WANT_STATE
void setState(const char* const key, const char* const value) void setState(const char* const key, const char* const value)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
@@ -336,7 +414,13 @@ protected:
free(atomBuf); free(atomBuf);
} }


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
static void setStateCallback(void* const ptr, const char* const key, const char* const value)
{
static_cast<UiLv2*>(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) void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
@@ -358,7 +442,12 @@ protected:
fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom),
fURIDs.atomEventTransfer, &atomMidiEvent); fURIDs.atomEventTransfer, &atomMidiEvent);
} }
#endif

static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
{
static_cast<UiLv2*>(ptr)->sendNote(channel, note, velocity);
}
#endif


bool fileRequest(const char* const key) bool fileRequest(const char* const key)
{ {
@@ -379,97 +468,10 @@ protected:
return r == LV2UI_REQUEST_VALUE_SUCCESS; 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) static bool fileRequestCallback(void* ptr, const char* key)
{ {
return uiPtr->fileRequest(key);
return static_cast<UiLv2*>(ptr)->fileRequest(key);
} }

#undef uiPtr
}; };


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


+ 3
- 3
dpf/distrho/src/DistrhoUIPrivateData.hpp View File

@@ -215,7 +215,7 @@ public:
puglBackendLeave(pData->view); 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) void setIgnoreIdleCallbacks(const bool ignore = true)
{ {
pData->ignoreIdleCallbacks = ignore; pData->ignoreIdleCallbacks = ignore;
@@ -228,8 +228,8 @@ public:
puglBackendEnter(pData->view); 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); puglSetSizeAndDefault(pData->view, width, height);
} }


+ 127
- 0
dpf/distrho/src/DistrhoUIStub.cpp View File

@@ -0,0 +1,127 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "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<UIStub*>(ptr)->editParameter(rindex, started);
}

void setParameterValue(uint32_t, float)
{
}

static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
{
static_cast<UIStub*>(ptr)->setParameterValue(rindex, value);
}

void setSize(uint, uint)
{
}

static void setSizeCallback(void* const ptr, const uint width, const uint height)
{
static_cast<UIStub*>(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<UIStub*>(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<UIStub*>(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<UIStub*>(ptr)->fileRequest(key);
}
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

+ 223
- 150
dpf/distrho/src/DistrhoUIVST3.cpp View File

@@ -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 #if ! DISTRHO_PLUGIN_WANT_STATE
static constexpr const setStateFunc setStateCallback = nullptr; static constexpr const setStateFunc setStateCallback = nullptr;
#endif #endif
#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
static constexpr const sendNoteFunc sendNoteCallback = nullptr;
#endif


// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// Static data, see DistrhoPlugin.cpp // Static data, see DistrhoPlugin.cpp
@@ -73,13 +73,18 @@ static void applyGeometryConstraints(const uint minimumWidth,
const bool keepAspectRatio, const bool keepAspectRatio,
v3_view_rect* const rect) 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<int32_t>(minimumWidth); const int32_t minWidth = static_cast<int32_t>(minimumWidth);
const int32_t minHeight = static_cast<int32_t>(minimumHeight); const int32_t minHeight = static_cast<int32_t>(minimumHeight);


if (keepAspectRatio) if (keepAspectRatio)
{ {
if (rect->right < 1)
rect->right = 1;
if (rect->bottom < 1)
rect->bottom = 1;

const double ratio = static_cast<double>(minWidth) / static_cast<double>(minHeight); const double ratio = static_cast<double>(minWidth) / static_cast<double>(minHeight);
const double reqRatio = static_cast<double>(rect->right) / static_cast<double>(rect->bottom); const double reqRatio = static_cast<double>(rect->right) / static_cast<double>(rect->bottom);


@@ -99,8 +104,8 @@ static void applyGeometryConstraints(const uint minimumWidth,
if (minHeight > rect->bottom) if (minHeight > rect->bottom)
rect->bottom = minHeight; 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: 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() 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; constexpr const CFTimeInterval interval = DPF_VST3_TIMER_INTERVAL * 0.0001;


CFRunLoopTimerContext context = {}; CFRunLoopTimerContext context = {};
@@ -171,12 +168,9 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fTimerRef != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fTimerRef != nullptr,);


CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); 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 * There is no need for implementing a window proc because DefWindowProc already calls the
* callback function when processing WM_TIMER messages. * callback function when processing WM_TIMER messages.
*/ */
@@ -214,12 +208,10 @@ public:


void unregisterNativeIdleCallback() void unregisterNativeIdleCallback()
{ {
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
fUI.removeIdleCallbackForVST3(this);
#elif defined(DISTRHO_OS_MAC)
#ifdef DISTRHO_OS_MAC
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes);
CFRelease(fTimerRef); CFRelease(fTimerRef);
#elif defined(DISTRHO_OS_WINDOWS)
#else
DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,);
KillTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID); KillTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID);
DestroyWindow(fTimerWindow); DestroyWindow(fTimerWindow);
@@ -228,27 +220,79 @@ public:
} }


private: 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; CFRunLoopTimerRef fTimerRef;


static void platformIdleTimerCallback(CFRunLoopTimerRef, void* const info) static void platformIdleTimerCallback(CFRunLoopTimerRef, void* const info)
{ {
static_cast<NativeIdleCallback*>(info)->idleCallback();
static_cast<NativeIdleHelper*>(info)->fCallback->idleCallback();
} }
#elif defined(DISTRHO_OS_WINDOWS)
#else
HWND fTimerWindow; HWND fTimerWindow;
String fTimerWindowClassName; String fTimerWindowClassName;


WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD) WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD)
{ {
reinterpret_cast<NativeIdleCallback*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->idleCallback();
reinterpret_cast<NativeIdleHelper*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback();
} }
#endif #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 #endif


@@ -327,13 +371,13 @@ public:


if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight) 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) 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()); setSize(fUI.getWidth(), fUI.getHeight());
} }


@@ -408,7 +452,7 @@ public:
#endif #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; return V3_OK;
} }


@@ -426,17 +470,17 @@ public:


if (fIsResizingFromPlugin) 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; fIsResizingFromPlugin = false;
} }
else 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; 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; return V3_OK;
} }


@@ -476,7 +520,7 @@ public:


fConnection = point; fConnection = point;


d_stdout("requesting current plugin state");
d_debug("requesting current plugin state");


v3_message** const message = createMessage("init"); v3_message** const message = createMessage("init");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
@@ -494,7 +538,7 @@ public:
{ {
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);


d_stdout("reporting UI closed");
d_debug("reporting UI closed");
fReadyForPluginData = false; fReadyForPluginData = false;


v3_message** const message = createMessage("close"); v3_message** const message = createMessage("close");
@@ -556,10 +600,13 @@ public:
#endif #endif
} }


// others like latency and buffer-size do not matter on UI side
return V3_OK; return V3_OK;
} }


const uint32_t index = static_cast<uint32_t>(rindex) - kVst3InternalParameterBaseCount;
DISTRHO_SAFE_ASSERT_UINT2_RETURN(rindex >= kVst3InternalParameterCount, rindex, kVst3InternalParameterCount, V3_INVALID_ARG);
const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);

fUI.parameterChanged(index, value); fUI.parameterChanged(index, value);
return V3_OK; return V3_OK;
} }
@@ -614,7 +661,7 @@ public:
} }
#endif #endif


d_stdout("UIVst3 received unknown msg '%s'", msgid);
d_stderr("UIVst3 received unknown msg '%s'", msgid);


return V3_NOT_IMPLEMENTED; return V3_NOT_IMPLEMENTED;
} }
@@ -663,19 +710,19 @@ public:
if (fNeedsResizeFromPlugin) if (fNeedsResizeFromPlugin)
{ {
fNeedsResizeFromPlugin = false; fNeedsResizeFromPlugin = false;
d_stdout("first resize forced behaviour is now stopped");
d_debug("first resize forced behaviour is now stopped");
} }


if (fIsResizingFromHost) if (fIsResizingFromHost)
{ {
fIsResizingFromHost = false; fIsResizingFromHost = false;
d_stdout("was resizing from host, now stopped");
d_debug("was resizing from host, now stopped");
} }


if (fIsResizingFromPlugin) if (fIsResizingFromPlugin)
{ {
fIsResizingFromPlugin = false; 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); 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<UIVst3*>(ptr)->editParameter(rindex, started);
} }


void setParameterValue(const uint32_t rindex, const float realValue) void setParameterValue(const uint32_t rindex, const float realValue)
@@ -777,53 +824,37 @@ private:
v3_cpp_obj_unref(message); 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<UIVst3*>(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<UIVst3*>(ptr)->setState(key, value);
} }
#endif


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
@@ -848,38 +879,54 @@ private:
v3_cpp_obj_unref(message); 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<UIVst3*>(ptr)->sendNote(channel, note, velocity);
} }
#endif #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<UIVst3*>(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) || if (v3_tuid_match(iid, v3_funknown_iid) ||
v3_tuid_match(iid, v3_connection_point_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; ++point->refcounter;
*iface = self; *iface = self;
return V3_OK; 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; *iface = NULL;
return V3_NO_INTERFACE; 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) static v3_result V3_API connect(void* const self, v3_connection_point** const other)
{ {
dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(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); 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) 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<dpf_ui_connection_point**>(self); dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self);


DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG);
DISTRHO_SAFE_ASSERT(point->other == other);


point->other = nullptr; 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) || if (v3_tuid_match(iid, v3_funknown_iid) ||
v3_tuid_match(iid, v3_plugin_view_content_scale_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; ++scale->refcounter;
*iface = self; *iface = self;
return V3_OK; 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; *iface = NULL;
return V3_NO_INTERFACE; 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) static v3_result V3_API set_content_scale_factor(void* const self, const float factor)
{ {
dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(self); dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(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; scale->scaleFactor = factor;


@@ -1088,13 +1136,13 @@ struct dpf_timer_handler : v3_timer_handler_cpp {
if (v3_tuid_match(iid, v3_funknown_iid) || if (v3_tuid_match(iid, v3_funknown_iid) ||
v3_tuid_match(iid, v3_timer_handler_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; ++timer->refcounter;
*iface = self; *iface = self;
return V3_OK; 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; *iface = NULL;
return V3_NO_INTERFACE; return V3_NO_INTERFACE;
@@ -1155,7 +1203,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
nextHeight(0), nextHeight(0),
sizeRequestedBeforeBeingAttached(false) 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 // make sure host application is valid through out this view lifetime
if (hostApplication != nullptr) if (hostApplication != nullptr)
@@ -1183,7 +1231,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {


~dpf_plugin_view() ~dpf_plugin_view()
{ {
d_stdout("~dpf_plugin_view()");
d_debug("~dpf_plugin_view()");


connection = nullptr; connection = nullptr;
scale = nullptr; scale = nullptr;
@@ -1206,7 +1254,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
if (v3_tuid_match(iid, v3_funknown_iid) || if (v3_tuid_match(iid, v3_funknown_iid) ||
v3_tuid_match(iid, v3_plugin_view_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; ++view->refcounter;
*iface = self; *iface = self;
return V3_OK; return V3_OK;
@@ -1214,8 +1262,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp {


if (v3_tuid_match(v3_connection_point_iid, iid)) 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) if (view->connection == nullptr)
view->connection = new dpf_ui_connection_point(view->uivst3); 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 #ifndef DISTRHO_OS_MAC
if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) 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) if (view->scale == nullptr)
view->scale = new dpf_plugin_view_content_scale(view->uivst3); view->scale = new dpf_plugin_view_content_scale(view->uivst3);
@@ -1240,7 +1288,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
} }
#endif #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; *iface = nullptr;
return V3_NO_INTERFACE; return V3_NO_INTERFACE;
@@ -1250,7 +1298,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
{ {
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
const int refcount = ++view->refcounter; 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; return refcount;
} }


@@ -1261,7 +1309,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {


if (const int refcount = --view->refcounter) 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; return refcount;
} }


@@ -1300,7 +1348,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
if (unclean) if (unclean)
return 0; 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 view;
delete viewptr; 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) 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<ARRAY_SIZE(kSupportedPlatforms); ++i) for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i)
{ {
@@ -1321,11 +1369,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
} }


return V3_NOT_IMPLEMENTED; return V3_NOT_IMPLEMENTED;

// unused unless debug
(void)self;
} }


static v3_result V3_API attached(void* const self, void* const parent, const char* const platform_type) static v3_result V3_API attached(void* const self, void* const parent, const char* const platform_type)
{ {
d_stdout("dpf_plugin_view::attached => %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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); 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) 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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); 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) static v3_result V3_API on_wheel(void* const self, const float distance)
{ {
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI #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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);


UIVst3* const uivst3 = view->uivst3; 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) 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 #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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);


UIVst3* const uivst3 = view->uivst3; 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) 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 #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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);


UIVst3* const uivst3 = view->uivst3; 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) 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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);


if (UIVst3* const uivst3 = view->uivst3) if (UIVst3* const uivst3 = view->uivst3)
return uivst3->getSize(rect); 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; 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) 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); 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->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); 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) static v3_result V3_API on_focus(void* const self, const v3_bool state)
{ {
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI #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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);


UIVst3* const uivst3 = view->uivst3; 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) 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<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);


view->frame = frame; 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) 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); self, rect->top, rect->left, rect->right, rect->bottom);
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);




+ 37
- 0
dpf/distrho/src/clap/audio-buffer.h View File

@@ -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

+ 68
- 0
dpf/distrho/src/clap/entry.h View File

@@ -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

+ 283
- 0
dpf/distrho/src/clap/events.h View File

@@ -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

+ 116
- 0
dpf/distrho/src/clap/ext/audio-ports.h View File

@@ -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

+ 219
- 0
dpf/distrho/src/clap/ext/gui.h View File

@@ -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

+ 296
- 0
dpf/distrho/src/clap/ext/params.h View File

@@ -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

+ 16
- 0
dpf/distrho/src/clap/fixedpoint.h View File

@@ -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;

+ 41
- 0
dpf/distrho/src/clap/host.h View File

@@ -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

+ 8
- 0
dpf/distrho/src/clap/id.h View File

@@ -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;

+ 39
- 0
dpf/distrho/src/clap/plugin-factory.h View File

@@ -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

+ 76
- 0
dpf/distrho/src/clap/plugin-features.h View File

@@ -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"

+ 96
- 0
dpf/distrho/src/clap/plugin.h View File

@@ -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

+ 36
- 0
dpf/distrho/src/clap/private/macros.h View File

@@ -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

+ 16
- 0
dpf/distrho/src/clap/private/std.h View File

@@ -0,0 +1,16 @@
#pragma once

#include "macros.h"

#ifdef CLAP_HAS_CXX11
# include <cstdint>
#else
# include <stdint.h>
#endif

#ifdef __cplusplus
# include <cstddef>
#else
# include <stddef.h>
# include <stdbool.h>
#endif

+ 65
- 0
dpf/distrho/src/clap/process.h View File

@@ -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

+ 21
- 0
dpf/distrho/src/clap/string-sizes.h View File

@@ -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

+ 34
- 0
dpf/distrho/src/clap/version.h View File

@@ -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;
}

+ 17
- 0
dpf/distrho/src/travesty/audio_processor.h View File

@@ -30,6 +30,23 @@ typedef uint64_t v3_speaker_arrangement;
enum { enum {
V3_SPEAKER_L = 1 << 0, V3_SPEAKER_L = 1 << 0,
V3_SPEAKER_R = 1 << 1, 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 V3_SPEAKER_M = 1 << 19
}; };




+ 4
- 1
dpf/utils/package-osx-bundles.sh View File

@@ -44,7 +44,10 @@ cd ..


DPF_UTILS_DIR=$(dirname ${0}) 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|" \ sed -e "s|@builddir@|${PWD}/build|" \
-e "s|@lv2bundleref@|dpf-${SNAME}-lv2bundles.pkg|" \ -e "s|@lv2bundleref@|dpf-${SNAME}-lv2bundles.pkg|" \
-e "s|@vst2bundleref@|dpf-${SNAME}-vst2bundles.pkg|" \ -e "s|@vst2bundleref@|dpf-${SNAME}-vst2bundles.pkg|" \


dpf/utils/plugin.vst/Contents/Info.plist → dpf/utils/plugin.bundle/Contents/Info.plist View File


dpf/utils/plugin.vst/Contents/PkgInfo → dpf/utils/plugin.bundle/Contents/PkgInfo View File


dpf/utils/plugin.vst/Contents/Resources/empty.lproj → dpf/utils/plugin.bundle/Contents/Resources/empty.lproj View File


+ 14
- 6
dpf/utils/res2c.py View File

@@ -40,13 +40,13 @@ def res2c(namespace, filenames):


for filename in filenames: for filename in filenames:
shortFilename = filename.rsplit(os.sep, 1)[-1].split(".", 1)[0] shortFilename = filename.rsplit(os.sep, 1)[-1].split(".", 1)[0]
shortFilename = shortFilename.replace("-", "_")
shortFilename = shortFilename.replace("-", "_").replace("@","_")


resData = open(filename, 'rb').read() resData = open(filename, 'rb').read()


print("Generating data for \"%s\"" % (filename)) 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))) fdH.write(" const unsigned int %sDataSize = %i;\n" % (shortFilename, len(resData)))


if tempIndex != len(filenames): if tempIndex != len(filenames):
@@ -70,7 +70,7 @@ def res2c(namespace, filenames):
curColumn += 1 curColumn += 1


fdC.write("};\n") 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): if tempIndex != len(filenames):
fdC.write("\n") fdC.write("\n")
@@ -89,25 +89,33 @@ def res2c(namespace, filenames):
# ----------------------------------------------------- # -----------------------------------------------------


if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: %s <namespace> <resource-folder>" % sys.argv[0])
if len(sys.argv) not in (3, 4):
print("Usage: %s <namespace> <resource-folder> [output-folder=$CWD]" % sys.argv[0])
quit() quit()


namespace = sys.argv[1].replace("-","_") namespace = sys.argv[1].replace("-","_")
resFolder = sys.argv[2] resFolder = sys.argv[2]
outFolder = sys.argv[3] if len(sys.argv) == 4 else None


if not os.path.exists(resFolder): if not os.path.exists(resFolder):
print("Folder '%s' does not exist" % resFolder) print("Folder '%s' does not exist" % resFolder)
quit() quit()


if outFolder is not None and not os.path.exists(outFolder):
print("Output folder '%s' does not exist" % outFolder)
quit()

# find resource files # find resource files
resFiles = [] resFiles = []


for root, dirs, files in os.walk(resFolder): for root, dirs, files in os.walk(resFolder):
for name in files: for name in files:
resFiles.append(os.path.join(root, name))
resFiles.append(os.path.abspath(os.path.join(root, name)))


resFiles.sort() resFiles.sort()


if outFolder is not None:
os.chdir(outFolder)

# create code now # create code now
res2c(namespace, resFiles) res2c(namespace, resFiles)

+ 2
- 0
dpf/utils/symbols/clap.def View File

@@ -0,0 +1,2 @@
EXPORTS
clap_entry

+ 1
- 0
dpf/utils/symbols/clap.exp View File

@@ -0,0 +1 @@
_clap_entry

+ 4
- 0
dpf/utils/symbols/clap.version View File

@@ -0,0 +1,4 @@
{
global: clap_entry;
local: *;
};

+ 23
- 2
dpf/utils/valgrind-dpf.supp View File

@@ -21,8 +21,7 @@
{ {
libdl is full of leaks libdl is full of leaks
Memcheck:Leak Memcheck:Leak
fun:calloc
fun:allocate_dtv
...
fun:_dl_allocate_tls fun:_dl_allocate_tls
... ...
} }
@@ -32,6 +31,12 @@
... ...
fun:call_init.part.0 fun:call_init.part.0
} }
{
libdl is really something else
Memcheck:Addr8
...
fun:dl_open_worker
}
{ {
ignore XInitThreads ignore XInitThreads
Memcheck:Leak Memcheck:Leak
@@ -46,3 +51,19 @@
fun:XrmGetStringDatabase 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
}

Loading…
Cancel
Save