@@ -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 | ||||
@@ -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 | ||||
@@ -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() | ||||
@@ -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. | ||||
@@ -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 | ||||
@@ -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 |
@@ -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 \ | ||||
@@ -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. | ||||
@@ -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". | ||||
@@ -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. | ||||
@@ -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; | ||||
@@ -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) | ||||
@@ -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> | ||||
@@ -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 |
@@ -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, | ||||
@@ -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); | ||||
@@ -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); | ||||
@@ -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) | ||||
@@ -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; | ||||
@@ -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); | ||||
@@ -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. | ||||
@@ -1,4 +0,0 @@ | |||||
Checks: > | |||||
*, | |||||
FormatStyle: file | |||||
WarningsAsErrors: '*' |
@@ -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/.*' |
@@ -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 | ||||
@@ -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" | |||||
/** @} */ | /** @} */ | ||||
@@ -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 | ||||
@@ -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)) | ||||
@@ -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 | ||||
@@ -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; | ||||
@@ -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 | ||||
{ | { | ||||
@@ -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 | ||||
@@ -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; | ||||
@@ -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 | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- |
@@ -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 | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -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 | |||||
// -------------------------------------------------------------------------------------------------------------------- |
@@ -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 | ||||
@@ -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; | ||||
} | } | ||||
} | } | ||||
@@ -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 | ||||
@@ -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) | ||||
{ | { | ||||
@@ -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 | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -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); | ||||
} | } | ||||
@@ -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 |
@@ -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); | ||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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; |
@@ -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 |
@@ -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; |
@@ -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 |
@@ -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" |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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; | |||||
} |
@@ -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 | ||||
}; | }; | ||||
@@ -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|" \ | ||||
@@ -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) |
@@ -0,0 +1,2 @@ | |||||
EXPORTS | |||||
clap_entry |
@@ -0,0 +1 @@ | |||||
_clap_entry |
@@ -0,0 +1,4 @@ | |||||
{ | |||||
global: clap_entry; | |||||
local: *; | |||||
}; |
@@ -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 | |||||
} |