@@ -8,6 +8,23 @@ AR ?= ar | |||
CC ?= gcc | |||
CXX ?= g++ | |||
# Before including this file, a few variables can be set in order to tweak build behaviour: | |||
# DEBUG=true | |||
# NOOPT=true | |||
# SKIP_STRIPPING=true | |||
# NVG_DISABLE_SKIPPING_WHITESPACE=true | |||
# NVG_FONT_TEXTURE_FLAGS=0 | |||
# FILE_BROWSER_DISABLED=true | |||
# WINDOWS_ICON_ID=0 | |||
# USE_GLES2=true | |||
# USE_GLES3=true | |||
# USE_OPENGL3=true | |||
# USE_NANOVG_FBO=true | |||
# USE_NANOVG_FREETYPE=true | |||
# STATIC_BUILD=true | |||
# FORCE_NATIVE_AUDIO_FALLBACK=true | |||
# SKIP_NATIVE_AUDIO_FALLBACK=true | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Protect against multiple inclusion | |||
@@ -16,7 +33,24 @@ ifneq ($(DPF_MAKEFILE_BASE_INCLUDED),true) | |||
DPF_MAKEFILE_BASE_INCLUDED = true | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Auto-detect OS if not defined | |||
# Auto-detect target compiler if not defined | |||
TARGET_COMPILER = $(shell echo '\#ifdef __clang__\nclang\n\#else\ngcc\n\#endif' | $(CC) -E -P -x c - 2>/dev/null) | |||
ifneq ($(CLANG),true) | |||
ifneq ($(GCC),true) | |||
ifneq (,$(findstring clang,$(TARGET_COMPILER))) | |||
CLANG = true | |||
else | |||
GCC = true | |||
endif | |||
endif | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Auto-detect target OS if not defined | |||
TARGET_MACHINE := $(shell $(CC) -dumpmachine) | |||
@@ -57,7 +91,7 @@ endif # HAIKU | |||
endif # BSD | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Auto-detect the processor | |||
# Auto-detect target processor | |||
TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE))) | |||
@@ -75,15 +109,21 @@ CPU_I386_OR_X86_64 = true | |||
endif | |||
ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) | |||
CPU_ARM = true | |||
CPU_ARM_OR_AARCH64 = true | |||
CPU_ARM_OR_ARM64 = true | |||
endif | |||
ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) | |||
CPU_ARM64 = true | |||
CPU_ARM_OR_AARCH64 = true | |||
CPU_ARM_OR_ARM64 = true | |||
endif | |||
ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) | |||
CPU_AARCH64 = true | |||
CPU_ARM_OR_AARCH64 = true | |||
CPU_ARM64 = true | |||
CPU_ARM_OR_ARM64 = true | |||
endif | |||
ifeq ($(CPU_ARM),true) | |||
ifneq ($(CPU_ARM64),true) | |||
CPU_ARM32 = true | |||
endif | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
@@ -168,48 +208,56 @@ endif | |||
BASE_FLAGS = -Wall -Wextra -pipe -MD -MP | |||
BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections | |||
LINK_OPTS = -fdata-sections -ffunction-sections | |||
ifeq ($(CPU_I386_OR_X86_64),true) | |||
BASE_OPTS += -mtune=generic | |||
ifeq ($(WASM),true) | |||
BASE_OPTS += -msse -msse2 -msse3 -msimd128 | |||
else | |||
BASE_OPTS += -msse -msse2 -mfpmath=sse | |||
endif | |||
ifeq ($(GCC),true) | |||
BASE_FLAGS += -fno-gnu-unique | |||
endif | |||
ifeq ($(CPU_ARM),true) | |||
ifneq ($(CPU_ARM64),true) | |||
BASE_OPTS += -mfpu=neon-vfpv4 -mfloat-abi=hard | |||
endif | |||
ifeq ($(SKIP_STRIPPING),true) | |||
BASE_FLAGS += -g | |||
endif | |||
ifeq ($(MACOS),true) | |||
# MacOS linker flags | |||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip,-dead_strip_dylibs | |||
ifneq ($(SKIP_STRIPPING),true) | |||
LINK_OPTS += -Wl,-x | |||
ifeq ($(STATIC_BUILD),true) | |||
BASE_FLAGS += -DSTATIC_BUILD | |||
endif | |||
ifeq ($(WINDOWS),true) | |||
# Assume we want posix | |||
BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS=1 -D__USE_MINGW_ANSI_STDIO=1 | |||
# Needed for windows, see https://github.com/falkTX/Carla/issues/855 | |||
BASE_FLAGS += -mstackrealign | |||
else | |||
# Not needed for Windows | |||
BASE_FLAGS += -fPIC -DPIC | |||
endif | |||
# Common linker flags | |||
LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections | |||
ifeq ($(WASM),true) | |||
BASE_OPTS += -msse -msse2 -msse3 -msimd128 | |||
else ifeq ($(CPU_ARM32),true) | |||
BASE_OPTS += -mfpu=neon-vfpv4 -mfloat-abi=hard | |||
else ifeq ($(CPU_I386_OR_X86_64),true) | |||
BASE_OPTS += -mtune=generic -msse -msse2 -mfpmath=sse | |||
endif | |||
ifeq ($(MACOS),true) | |||
LINK_OPTS += -Wl,-dead_strip,-dead_strip_dylibs | |||
else ifeq ($(WASM),true) | |||
LINK_OPTS += -O3 | |||
LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1 | |||
LINK_OPTS += -Wl,--gc-sections | |||
else | |||
LINK_OPTS += -Wl,-O1,--gc-sections | |||
LINK_OPTS += -Wl,--as-needed | |||
ifneq ($(SKIP_STRIPPING),true) | |||
LINK_OPTS += -Wl,--strip-all | |||
endif | |||
endif | |||
ifneq ($(SKIP_STRIPPING),true) | |||
ifeq ($(MACOS),true) | |||
LINK_OPTS += -Wl,-x | |||
else ifeq ($(WASM),true) | |||
LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1 | |||
else | |||
LINK_OPTS += -Wl,--strip-all | |||
endif | |||
ifeq ($(SKIP_STRIPPING),true) | |||
BASE_FLAGS += -g | |||
endif | |||
ifeq ($(NOOPT),true) | |||
@@ -217,22 +265,6 @@ ifeq ($(NOOPT),true) | |||
BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections | |||
endif | |||
ifneq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||
ifneq ($(BSD),true) | |||
BASE_FLAGS += -fno-gnu-unique | |||
endif | |||
endif | |||
ifeq ($(WINDOWS),true) | |||
# Assume we want posix | |||
BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS=1 -D__USE_MINGW_ANSI_STDIO=1 | |||
# Needed for windows, see https://github.com/falkTX/Carla/issues/855 | |||
BASE_FLAGS += -mstackrealign | |||
else | |||
# Not needed for Windows | |||
BASE_FLAGS += -fPIC -DPIC | |||
endif | |||
ifeq ($(DEBUG),true) | |||
BASE_FLAGS += -DDEBUG -O0 -g | |||
LINK_OPTS = | |||
@@ -244,11 +276,6 @@ BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden | |||
CXXFLAGS += -fvisibility-inlines-hidden | |||
endif | |||
ifeq ($(STATIC_BUILD),true) | |||
BASE_FLAGS += -DSTATIC_BUILD | |||
# LINK_OPTS += -static | |||
endif | |||
ifeq ($(WITH_LTO),true) | |||
BASE_FLAGS += -fno-strict-aliasing -flto | |||
LINK_OPTS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch | |||
@@ -292,7 +319,7 @@ ifeq ($(TESTBUILD),true) | |||
BASE_FLAGS += -Werror -Wcast-qual -Wconversion -Wformat -Wformat-security -Wredundant-decls -Wshadow -Wstrict-overflow -fstrict-overflow -Wundef -Wwrite-strings | |||
BASE_FLAGS += -Wpointer-arith -Wabi=98 -Winit-self -Wuninitialized -Wstrict-overflow=5 | |||
# BASE_FLAGS += -Wfloat-equal | |||
ifeq ($(CC),clang) | |||
ifeq ($(CLANG),true) | |||
BASE_FLAGS += -Wdocumentation -Wdocumentation-unknown-command | |||
BASE_FLAGS += -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-exit-time-destructors -Wno-float-equal | |||
else | |||
@@ -300,7 +327,7 @@ BASE_FLAGS += -Wcast-align -Wunsafe-loop-optimizations | |||
endif | |||
ifneq ($(MACOS),true) | |||
BASE_FLAGS += -Wmissing-declarations -Wsign-conversion | |||
ifneq ($(CC),clang) | |||
ifeq ($(GCC),true) | |||
BASE_FLAGS += -Wlogical-op | |||
endif | |||
endif | |||
@@ -621,11 +648,14 @@ all: | |||
print_available = @echo $(1): $(shell echo $($(1)) | grep -q true && echo Yes || echo No) | |||
features: | |||
@echo === Detected Compiler | |||
$(call print_available,CLANG) | |||
$(call print_available,GCC) | |||
@echo === Detected CPU | |||
$(call print_available,CPU_AARCH64) | |||
$(call print_available,CPU_ARM) | |||
$(call print_available,CPU_ARM32) | |||
$(call print_available,CPU_ARM64) | |||
$(call print_available,CPU_ARM_OR_AARCH64) | |||
$(call print_available,CPU_ARM_OR_ARM64) | |||
$(call print_available,CPU_I386) | |||
$(call print_available,CPU_I386_OR_X86_64) | |||
@echo === Detected OS | |||
@@ -239,6 +239,18 @@ VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3 | |||
endif | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set CLAP filename, either single binary or inside a bundle | |||
ifeq ($(MACOS),true) | |||
CLAP_CONTENTS = $(NAME).clap/Contents | |||
CLAP_FILENAME = $(CLAP_CONTENTS)/MacOS/$(NAME) | |||
else ifeq ($(USE_CLAP_BUNDLE),true) | |||
CLAP_FILENAME = $(NAME).clap/$(NAME).clap | |||
else | |||
CLAP_FILENAME = $(NAME).clap | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Set plugin binary file targets | |||
@@ -264,6 +276,7 @@ vst2 = $(TARGET_DIR)/$(VST2_FILENAME) | |||
ifneq ($(VST3_FILENAME),) | |||
vst3 = $(TARGET_DIR)/$(VST3_FILENAME) | |||
endif | |||
clap = $(TARGET_DIR)/$(CLAP_FILENAME) | |||
shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) | |||
static = $(TARGET_DIR)/$(NAME).a | |||
@@ -274,6 +287,9 @@ vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Resources/empty.lproj | |||
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Info.plist | |||
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/PkgInfo | |||
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Resources/empty.lproj | |||
clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Info.plist | |||
clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/PkgInfo | |||
clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Resources/empty.lproj | |||
endif | |||
ifneq ($(HAVE_DGL),true) | |||
@@ -298,6 +314,7 @@ SYMBOLS_LV2UI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2-ui.exp | |||
SYMBOLS_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp | |||
SYMBOLS_VST2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst2.exp | |||
SYMBOLS_VST3 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst3.exp | |||
SYMBOLS_CLAP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/clap.exp | |||
SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp | |||
else ifeq ($(WASM),true) | |||
SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" | |||
@@ -307,6 +324,7 @@ SYMBOLS_LV2UI = -sEXPORTED_FUNCTIONS="['lv2ui_descriptor']" | |||
SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']" | |||
SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']" | |||
SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']" | |||
SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']" | |||
SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" | |||
else ifeq ($(WINDOWS),true) | |||
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def | |||
@@ -316,6 +334,7 @@ SYMBOLS_LV2UI = $(DPF_PATH)/utils/symbols/lv2-ui.def | |||
SYMBOLS_LV2 = $(DPF_PATH)/utils/symbols/lv2.def | |||
SYMBOLS_VST2 = $(DPF_PATH)/utils/symbols/vst2.def | |||
SYMBOLS_VST3 = $(DPF_PATH)/utils/symbols/vst3.def | |||
SYMBOLS_CLAP = $(DPF_PATH)/utils/symbols/clap.def | |||
SYMBOLS_SHARED = $(DPF_PATH)/utils/symbols/shared.def | |||
else ifneq ($(DEBUG),true) | |||
SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version | |||
@@ -325,6 +344,7 @@ SYMBOLS_LV2UI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2-ui.version | |||
SYMBOLS_LV2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2.version | |||
SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version | |||
SYMBOLS_VST3 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst3.version | |||
SYMBOLS_CLAP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/clap.version | |||
SYMBOLS_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version | |||
endif | |||
@@ -521,6 +541,28 @@ endif | |||
@echo "Creating VST3 plugin for $(NAME)" | |||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# CLAP | |||
ifeq ($(HAVE_DGL),true) | |||
ifneq ($(HAIKU),true) | |||
ifneq ($(WASM),true) | |||
CLAP_LIBS = -lpthread | |||
endif | |||
endif | |||
endif | |||
clap: $(clap) $(clapfiles) | |||
ifeq ($(HAVE_DGL),true) | |||
$(clap): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.o $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.o $(DGL_LIB) | |||
else | |||
$(clap): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.o | |||
endif | |||
-@mkdir -p $(shell dirname $@) | |||
@echo "Creating CLAP plugin for $(NAME)" | |||
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(CLAP_LIBS) $(SHARED) $(SYMBOLS_CLAP) -o $@ | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Shared | |||
@@ -557,19 +599,23 @@ $(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/I | |||
-@mkdir -p $(shell dirname $@) | |||
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ | |||
$(TARGET_DIR)/%.vst/Contents/Info.plist: $(DPF_PATH)/utils/plugin.vst/Contents/Info.plist | |||
$(TARGET_DIR)/%.vst/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist | |||
-@mkdir -p $(shell dirname $@) | |||
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ | |||
$(TARGET_DIR)/%.vst3/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist | |||
-@mkdir -p $(shell dirname $@) | |||
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ | |||
$(TARGET_DIR)/%.vst3/Contents/Info.plist: $(DPF_PATH)/utils/plugin.vst/Contents/Info.plist | |||
$(TARGET_DIR)/%.clap/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist | |||
-@mkdir -p $(shell dirname $@) | |||
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ | |||
$(TARGET_DIR)/%/Contents/PkgInfo: $(DPF_PATH)/utils/plugin.vst/Contents/PkgInfo | |||
$(TARGET_DIR)/%/Contents/PkgInfo: $(DPF_PATH)/utils/plugin.bundle/Contents/PkgInfo | |||
-@mkdir -p $(shell dirname $@) | |||
$(SILENT)cp $< $@ | |||
$(TARGET_DIR)/%/Resources/empty.lproj: $(DPF_PATH)/utils/plugin.vst/Contents/Resources/empty.lproj | |||
$(TARGET_DIR)/%/Resources/empty.lproj: $(DPF_PATH)/utils/plugin.bundle/Contents/Resources/empty.lproj | |||
-@mkdir -p $(shell dirname $@) | |||
$(SILENT)cp $< $@ | |||
@@ -586,6 +632,7 @@ endif | |||
-include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d | |||
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d | |||
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d | |||
-include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d | |||
-include $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.d | |||
-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d | |||
@@ -594,6 +641,7 @@ endif | |||
-include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d | |||
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d | |||
-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d | |||
-include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d | |||
-include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d | |||
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d | |||
@@ -336,9 +336,9 @@ function(dpf__build_vst2 NAME DGL_LIBRARY) | |||
OUTPUT_NAME "${NAME}" | |||
SUFFIX "") | |||
set(INFO_PLIST_PROJECT_NAME "${NAME}") | |||
configure_file("${DPF_ROOT_DIR}/utils/plugin.vst/Contents/Info.plist" | |||
configure_file("${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/Info.plist" | |||
"${PROJECT_BINARY_DIR}/bin/${NAME}.vst/Contents/Info.plist" @ONLY) | |||
file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst/Contents/PkgInfo" | |||
file(COPY "${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/PkgInfo" | |||
DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst/Contents") | |||
endif() | |||
endfunction() | |||
@@ -425,9 +425,9 @@ function(dpf__build_vst3 NAME DGL_LIBRARY) | |||
if(APPLE) | |||
# Uses the same macOS bundle template as VST2 | |||
set(INFO_PLIST_PROJECT_NAME "${NAME}") | |||
configure_file("${DPF_ROOT_DIR}/utils/plugin.vst/Contents/Info.plist" | |||
configure_file("${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/Info.plist" | |||
"${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/Info.plist" @ONLY) | |||
file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst/Contents/PkgInfo" | |||
file(COPY "${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/PkgInfo" | |||
DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents") | |||
endif() | |||
endfunction() | |||
@@ -68,7 +68,7 @@ struct Color { | |||
/** | |||
Create a new color based on this one but with a different alpha value. | |||
*/ | |||
Color withAlpha(float alpha) noexcept; | |||
Color withAlpha(float alpha) const noexcept; | |||
/** | |||
Create a color specified by hue, saturation and lightness. | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -94,7 +94,8 @@ class KnobEventHandler | |||
public: | |||
enum Orientation { | |||
Horizontal, | |||
Vertical | |||
Vertical, | |||
Both | |||
}; | |||
// NOTE hover not implemented yet | |||
@@ -112,6 +113,7 @@ public: | |||
virtual void knobDragStarted(SubWidget* widget) = 0; | |||
virtual void knobDragFinished(SubWidget* widget) = 0; | |||
virtual void knobValueChanged(SubWidget* widget, float value) = 0; | |||
virtual void knobDoubleClicked(SubWidget*) {}; | |||
}; | |||
explicit KnobEventHandler(SubWidget* self); | |||
@@ -119,6 +121,9 @@ public: | |||
KnobEventHandler& operator=(const KnobEventHandler& other); | |||
virtual ~KnobEventHandler(); | |||
// if setStep(1) has been called before, this returns true | |||
bool isInteger() const noexcept; | |||
// returns raw value, is assumed to be scaled if using log | |||
float getValue() const noexcept; | |||
@@ -139,12 +144,15 @@ public: | |||
void setUsingLogScale(bool yesNo) noexcept; | |||
Orientation getOrientation() const noexcept; | |||
void setOrientation(const Orientation orientation) noexcept; | |||
void setOrientation(Orientation orientation) noexcept; | |||
void setCallback(Callback* callback) noexcept; | |||
bool mouseEvent(const Widget::MouseEvent& ev); | |||
bool motionEvent(const Widget::MotionEvent& ev); | |||
// default 200, higher means slower | |||
void setMouseDeceleration(float accel) noexcept; | |||
bool mouseEvent(const Widget::MouseEvent& ev, double scaleFactor = 1.0); | |||
bool motionEvent(const Widget::MotionEvent& ev, double scaleFactor = 1.0); | |||
bool scrollEvent(const Widget::ScrollEvent& ev); | |||
protected: | |||
@@ -168,6 +176,21 @@ private: | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
class SliderEventHandler | |||
{ | |||
public: | |||
explicit SliderEventHandler(SubWidget* self); | |||
virtual ~SliderEventHandler(); | |||
private: | |||
struct PrivateData; | |||
PrivateData* const pData; | |||
DISTRHO_LEAK_DETECTOR(SliderEventHandler) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED | |||
@@ -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/ImageBase.cpp.o \ | |||
../build/dgl/ImageBaseWidgets.cpp.o \ | |||
../build/dgl/Layout.cpp.o \ | |||
../build/dgl/Resources.cpp.o \ | |||
../build/dgl/SubWidget.cpp.o \ | |||
../build/dgl/SubWidgetPrivateData.cpp.o \ | |||
@@ -591,14 +591,14 @@ public: | |||
/** | |||
Creates image by loading it from the specified chunk of memory. | |||
*/ | |||
NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, ImageFlags imageFlags); | |||
NanoImage::Handle createImageFromMemory(const uchar* data, uint dataSize, ImageFlags imageFlags); | |||
/** | |||
Creates image by loading it from the specified chunk of memory. | |||
Overloaded function for convenience. | |||
@see ImageFlags | |||
*/ | |||
NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, int imageFlags); | |||
NanoImage::Handle createImageFromMemory(const uchar* data, uint dataSize, int imageFlags); | |||
/** | |||
Creates image from specified raw format image data. | |||
@@ -137,6 +137,12 @@ public: | |||
*/ | |||
void repaint() noexcept override; | |||
/** | |||
Pushes this widget to the "bottom" of the parent widget. | |||
Makes the widget behave as if it was the first to be registered on the parent widget, thus being "on bottom". | |||
*/ | |||
virtual void toBottom(); | |||
/** | |||
Bring this widget to the "front" of the parent widget. | |||
Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front". | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,6 +19,8 @@ | |||
#include "Geometry.hpp" | |||
#include <list> | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -62,7 +64,7 @@ public: | |||
uint mod; | |||
/** Event flags. @see EventFlag */ | |||
uint flags; | |||
/** Event timestamp (if any). */ | |||
/** Event timestamp in milliseconds (if any). */ | |||
uint time; | |||
/** Constructor for default/null values */ | |||
@@ -331,16 +333,33 @@ public: | |||
/** | |||
Get the Id associated with this widget. | |||
Returns 0 by default. | |||
@see setId | |||
*/ | |||
uint getId() const noexcept; | |||
/** | |||
Get the name associated with this widget. | |||
This is complately optional, mostly useful for debugging purposes. | |||
Returns an empty string by default. | |||
@see setName | |||
*/ | |||
const char* getName() const noexcept; | |||
/** | |||
Set an Id to be associated with this widget. | |||
@see getId | |||
*/ | |||
void setId(uint id) noexcept; | |||
/** | |||
Set a name to be associated with this widget. | |||
This is complately optional, only useful for debugging purposes. | |||
@note name must not be null | |||
@see getName | |||
*/ | |||
void setName(const char* name) noexcept; | |||
/** | |||
Get the application associated with this widget's window. | |||
This is the same as calling `getTopLevelWidget()->getApp()`. | |||
@@ -367,6 +386,11 @@ public: | |||
*/ | |||
TopLevelWidget* getTopLevelWidget() const noexcept; | |||
/** | |||
Get list of children (a subwidgets) that belong to this widget. | |||
*/ | |||
std::list<SubWidget*> getChildren() const noexcept; | |||
/** | |||
Request repaint of this widget's area to the window this widget belongs to. | |||
On the raw Widget class this function does nothing. | |||
@@ -114,7 +114,7 @@ Color::Color(const Color& color1, const Color& color2, const float u) noexcept | |||
interpolate(color2, u); | |||
} | |||
Color Color::withAlpha(const float alpha2) noexcept | |||
Color Color::withAlpha(const float alpha2) const noexcept | |||
{ | |||
Color color(*this); | |||
color.alpha = alpha2; | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -274,6 +274,7 @@ struct KnobEventHandler::PrivateData { | |||
SubWidget* const widget; | |||
KnobEventHandler::Callback* callback; | |||
float accel; | |||
float minimum; | |||
float maximum; | |||
float step; | |||
@@ -287,13 +288,15 @@ struct KnobEventHandler::PrivateData { | |||
double lastX; | |||
double lastY; | |||
uint lastClickTime; | |||
PrivateData(KnobEventHandler* const s, SubWidget* const w) | |||
: self(s), | |||
widget(w), | |||
callback(nullptr), | |||
minimum(0.0f), | |||
maximum(1.0f), | |||
accel(200.f), | |||
minimum(0.f), | |||
maximum(1.f), | |||
step(0.0f), | |||
value(0.5f), | |||
valueDef(value), | |||
@@ -303,12 +306,14 @@ struct KnobEventHandler::PrivateData { | |||
orientation(Vertical), | |||
state(kKnobStateDefault), | |||
lastX(0.0), | |||
lastY(0.0) {} | |||
lastY(0.0), | |||
lastClickTime(0) {} | |||
PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other) | |||
: self(s), | |||
widget(w), | |||
callback(other->callback), | |||
accel(other->accel), | |||
minimum(other->minimum), | |||
maximum(other->maximum), | |||
step(other->step), | |||
@@ -320,11 +325,13 @@ struct KnobEventHandler::PrivateData { | |||
orientation(other->orientation), | |||
state(kKnobStateDefault), | |||
lastX(0.0), | |||
lastY(0.0) {} | |||
lastY(0.0), | |||
lastClickTime(0) {} | |||
void assignFrom(PrivateData* const other) | |||
{ | |||
callback = other->callback; | |||
accel = other->accel; | |||
minimum = other->minimum; | |||
maximum = other->maximum; | |||
step = other->step; | |||
@@ -337,6 +344,7 @@ struct KnobEventHandler::PrivateData { | |||
state = kKnobStateDefault; | |||
lastX = 0.0; | |||
lastY = 0.0; | |||
lastClickTime = 0; | |||
} | |||
inline float logscale(const float v) const | |||
@@ -353,7 +361,7 @@ struct KnobEventHandler::PrivateData { | |||
return std::log(v/a)/b; | |||
} | |||
bool mouseEvent(const Widget::MouseEvent& ev) | |||
bool mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor) | |||
{ | |||
if (ev.button != 1) | |||
return false; | |||
@@ -370,9 +378,21 @@ struct KnobEventHandler::PrivateData { | |||
return true; | |||
} | |||
lastX = ev.pos.getX() / scaleFactor; | |||
lastY = ev.pos.getY() / scaleFactor; | |||
if (lastClickTime > 0 && ev.time > lastClickTime && ev.time - lastClickTime <= 300) | |||
{ | |||
lastClickTime = 0; | |||
if (callback != nullptr) | |||
callback->knobDoubleClicked(widget); | |||
return true; | |||
} | |||
lastClickTime = ev.time; | |||
state |= kKnobStateDragging; | |||
lastX = ev.pos.getX(); | |||
lastY = ev.pos.getY(); | |||
widget->repaint(); | |||
if (callback != nullptr) | |||
@@ -394,62 +414,87 @@ struct KnobEventHandler::PrivateData { | |||
return false; | |||
} | |||
bool motionEvent(const Widget::MotionEvent& ev) | |||
bool motionEvent(const Widget::MotionEvent& ev, const double scaleFactor) | |||
{ | |||
if ((state & kKnobStateDragging) == 0x0) | |||
return false; | |||
bool doVal = false; | |||
float d, value2 = 0.0f; | |||
float movDiff; | |||
if (orientation == Horizontal) | |||
switch (orientation) | |||
{ | |||
if (const double movX = ev.pos.getX() - lastX) | |||
case Horizontal: | |||
movDiff = ev.pos.getX() / scaleFactor - lastX; | |||
break; | |||
case Vertical: | |||
movDiff = lastY - ev.pos.getY() / scaleFactor; | |||
break; | |||
case Both: | |||
{ | |||
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movX)); | |||
doVal = true; | |||
} | |||
} | |||
else if (orientation == Vertical) | |||
{ | |||
if (const double movY = lastY - ev.pos.getY()) | |||
{ | |||
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movY)); | |||
doVal = true; | |||
const float movDiffX = ev.pos.getX() / scaleFactor - lastX; | |||
const float movDiffY = lastY - ev.pos.getY() / scaleFactor; | |||
movDiff = std::abs(movDiffX) > std::abs(movDiffY) ? movDiffX : movDiffY; | |||
} | |||
break; | |||
default: | |||
return false; | |||
} | |||
if (! doVal) | |||
if (d_isZero(movDiff)) | |||
return false; | |||
const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel; | |||
valueTmp += (maximum - minimum) / divisor * movDiff; | |||
if (usingLog) | |||
value2 = logscale(value2); | |||
valueTmp = logscale(valueTmp); | |||
if (value2 < minimum) | |||
float value2; | |||
bool valueChanged = false; | |||
if (valueTmp < minimum) | |||
{ | |||
valueTmp = value2 = minimum; | |||
valueChanged = true; | |||
} | |||
else if (value2 > maximum) | |||
else if (valueTmp > maximum) | |||
{ | |||
valueTmp = value2 = maximum; | |||
valueChanged = true; | |||
} | |||
else | |||
{ | |||
valueTmp = value2; | |||
if (d_isNotZero(step)) | |||
{ | |||
const float rest = std::fmod(value2, step); | |||
value2 -= rest + (rest > step/2.0f ? step : 0.0f); | |||
if (std::abs(valueTmp - value) >= step) | |||
{ | |||
const float rest = std::fmod(valueTmp, step); | |||
valueChanged = true; | |||
value2 = valueTmp - rest; | |||
if (rest < 0 && rest < step * -0.5f) | |||
value2 -= step; | |||
else if (rest > 0 && rest > step * 0.5f) | |||
value2 += step; | |||
if (value2 < minimum) | |||
value2 = minimum; | |||
else if (value2 > maximum) | |||
value2 = maximum; | |||
} | |||
} | |||
else | |||
{ | |||
value2 = valueTmp; | |||
valueChanged = true; | |||
} | |||
} | |||
setValue(value2, true); | |||
if (valueChanged) | |||
setValue(value2, true); | |||
lastX = ev.pos.getX(); | |||
lastY = ev.pos.getY(); | |||
lastX = ev.pos.getX() / scaleFactor; | |||
lastY = ev.pos.getY() / scaleFactor; | |||
return true; | |||
} | |||
@@ -460,7 +505,7 @@ struct KnobEventHandler::PrivateData { | |||
return false; | |||
const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f; | |||
const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f; | |||
const float d = (ev.mod & kModifierControl) ? accel * 10.f : accel; | |||
float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) | |||
+ ((maximum - minimum) / d * 10.f * dir); | |||
@@ -553,6 +598,11 @@ KnobEventHandler::~KnobEventHandler() | |||
delete pData; | |||
} | |||
bool KnobEventHandler::isInteger() const noexcept | |||
{ | |||
return d_isEqual(pData->step, 1.f); | |||
} | |||
float KnobEventHandler::getValue() const noexcept | |||
{ | |||
return pData->value; | |||
@@ -596,9 +646,6 @@ KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept | |||
void KnobEventHandler::setOrientation(const Orientation orientation) noexcept | |||
{ | |||
if (pData->orientation == orientation) | |||
return; | |||
pData->orientation = orientation; | |||
} | |||
@@ -607,14 +654,19 @@ void KnobEventHandler::setCallback(Callback* const callback) noexcept | |||
pData->callback = callback; | |||
} | |||
bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev) | |||
void KnobEventHandler::setMouseDeceleration(float accel) noexcept | |||
{ | |||
return pData->mouseEvent(ev); | |||
pData->accel = accel; | |||
} | |||
bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev) | |||
bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor) | |||
{ | |||
return pData->motionEvent(ev); | |||
return pData->mouseEvent(ev, scaleFactor); | |||
} | |||
bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev, const double scaleFactor) | |||
{ | |||
return pData->motionEvent(ev, scaleFactor); | |||
} | |||
bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev) | |||
@@ -395,7 +395,7 @@ bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev) | |||
{ | |||
if (SubWidget::onMouse(ev)) | |||
return true; | |||
return KnobEventHandler::mouseEvent(ev); | |||
return KnobEventHandler::mouseEvent(ev, getTopLevelWidget()->getScaleFactor()); | |||
} | |||
template <class ImageType> | |||
@@ -403,7 +403,7 @@ bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev) | |||
{ | |||
if (SubWidget::onMotion(ev)) | |||
return true; | |||
return KnobEventHandler::motionEvent(ev); | |||
return KnobEventHandler::motionEvent(ev, getTopLevelWidget()->getScaleFactor()); | |||
} | |||
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)); | |||
} | |||
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)); | |||
} | |||
NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, int imageFlags) | |||
NanoImage::Handle NanoVG::createImageFromMemory(const uchar* data, uint dataSize, int imageFlags) | |||
{ | |||
if (fContext == nullptr) return NanoImage::Handle(); | |||
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); | |||
DISTRHO_SAFE_ASSERT_RETURN(dataSize > 0, NanoImage::Handle()); | |||
return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast<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, | |||
@@ -139,12 +139,21 @@ void SubWidget::repaint() noexcept | |||
if (TopLevelWidget* const topw = getTopLevelWidget()) | |||
{ | |||
if (pData->needsFullViewportForDrawing) | |||
topw->repaint(); | |||
// repaint is virtual and we want precisely the top-level specific implementation, not any higher level | |||
topw->TopLevelWidget::repaint(); | |||
else | |||
topw->repaint(getConstrainedAbsoluteArea()); | |||
} | |||
} | |||
void SubWidget::toBottom() | |||
{ | |||
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets); | |||
subwidgets.remove(this); | |||
subwidgets.insert(subwidgets.begin(), this); | |||
} | |||
void SubWidget::toFront() | |||
{ | |||
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets); | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -148,6 +148,11 @@ TopLevelWidget* Widget::getTopLevelWidget() const noexcept | |||
return pData->topLevelWidget; | |||
} | |||
std::list<SubWidget*> Widget::getChildren() const noexcept | |||
{ | |||
return pData->subWidgets; | |||
} | |||
void Widget::repaint() noexcept | |||
{ | |||
} | |||
@@ -157,11 +162,22 @@ uint Widget::getId() const noexcept | |||
return pData->id; | |||
} | |||
const char* Widget::getName() const noexcept | |||
{ | |||
return pData->name != nullptr ? pData->name : ""; | |||
} | |||
void Widget::setId(uint id) noexcept | |||
{ | |||
pData->id = id; | |||
} | |||
void Widget::setName(const char* const name) noexcept | |||
{ | |||
std::free(pData->name); | |||
pData->name = strdup(name); | |||
} | |||
bool Widget::onKeyboard(const KeyboardEvent& ev) | |||
{ | |||
return pData->giveKeyboardEventForSubWidgets(ev); | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -33,6 +33,7 @@ Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw) | |||
topLevelWidget(tlw), | |||
parentWidget(nullptr), | |||
id(0), | |||
name(nullptr), | |||
needsScaling(false), | |||
visible(true), | |||
size(0, 0), | |||
@@ -43,6 +44,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw) | |||
topLevelWidget(findTopLevelWidget(pw)), | |||
parentWidget(pw), | |||
id(0), | |||
name(nullptr), | |||
needsScaling(false), | |||
visible(true), | |||
size(0, 0), | |||
@@ -51,6 +53,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw) | |||
Widget::PrivateData::~PrivateData() | |||
{ | |||
subWidgets.clear(); | |||
std::free(name); | |||
} | |||
void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor) | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -30,6 +30,7 @@ struct Widget::PrivateData { | |||
TopLevelWidget* const topLevelWidget; | |||
Widget* const parentWidget; | |||
uint id; | |||
char* name; | |||
bool needsScaling; | |||
bool visible; | |||
Size<uint> size; | |||
@@ -23,6 +23,11 @@ | |||
#include "nanovg.h" | |||
#define FONTSTASH_IMPLEMENTATION | |||
#define stbtt_fontinfo dpf_nvg_stbtt_fontinfo | |||
#define stbrp_context dpf_nvg_stbrp_context | |||
#define stbrp_rect dpf_nvg_stbrp_rect | |||
#define stbrp_node dpf_nvg_stbrp_node | |||
#define stbrp_coord dpf_nvg_stbrp_coord | |||
#include "fontstash.h" | |||
#ifndef NVG_NO_STB | |||
@@ -869,7 +874,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) | |||
return image; | |||
} | |||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata) | |||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata) | |||
{ | |||
int w, h, n, image; | |||
unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); | |||
@@ -385,7 +385,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); | |||
// Creates image by loading it from the specified chunk of memory. | |||
// Returns handle to the image. | |||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); | |||
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata); | |||
// Creates image from specified image data and texture format. | |||
// Returns handle to the image. | |||
@@ -1,4 +0,0 @@ | |||
Checks: > | |||
*, | |||
FormatStyle: file | |||
WarningsAsErrors: '*' |
@@ -8,7 +8,6 @@ Checks: > | |||
-hicpp-signed-bitwise, | |||
-llvm-header-guard, | |||
-llvmlibc-*, | |||
-performance-no-int-to-ptr, | |||
-readability-function-cognitive-complexity, | |||
FormatStyle: file | |||
HeaderFilterRegex: 'pugl/.*' |
@@ -1378,7 +1378,7 @@ puglProcessEvents(PuglView* view) | |||
double | |||
puglGetTime(const PuglWorld* world) | |||
{ | |||
return (mach_absolute_time() / 1e9) - world->startTime; | |||
return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9) - world->startTime; | |||
} | |||
PuglStatus | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -54,6 +54,9 @@ START_NAMESPACE_DISTRHO | |||
Let's begin with some examples.@n | |||
Here is one of a stereo audio plugin that simply mutes the host output: | |||
@code | |||
/* DPF plugin include */ | |||
#include "DistrhoPlugin.hpp" | |||
/* Make DPF related classes available for us to use without any extra namespace references */ | |||
USE_NAMESPACE_DISTRHO; | |||
@@ -115,7 +118,7 @@ START_NAMESPACE_DISTRHO | |||
/** | |||
Get the plugin unique Id. | |||
This value is used by LADSPA, DSSI and VST plugin formats. | |||
This value is used by LADSPA, DSSI, VST2 and VST3 plugin formats. | |||
*/ | |||
int64_t getUniqueId() const override | |||
{ | |||
@@ -157,7 +160,7 @@ START_NAMESPACE_DISTRHO | |||
A plugin is nothing without parameters.@n | |||
In DPF parameters can be inputs or outputs.@n | |||
They have hints to describe how they behave plus a name and a symbol identifying them.@n | |||
Parameters also have 'ranges' – a minimum, maximum and default value. | |||
Parameters also have 'ranges' - a minimum, maximum and default value. | |||
Input parameters are by default "read-only": the plugin can read them but not change them. | |||
(there are exceptions and possibly a request to the host to change values, more on that below)@n | |||
@@ -349,10 +352,10 @@ START_NAMESPACE_DISTRHO | |||
} | |||
} | |||
/** | |||
Set the name of the program @a index. | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
/** | |||
Set the name of the program @a index. | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
void initProgramName(uint32_t index, String& programName) | |||
{ | |||
// we only have one program so we can skip checking the index | |||
@@ -374,6 +377,8 @@ START_NAMESPACE_DISTRHO | |||
return fGainL; | |||
case 1; | |||
return fGainR; | |||
default: | |||
return 0.f; | |||
} | |||
} | |||
@@ -618,6 +623,24 @@ START_NAMESPACE_DISTRHO | |||
*/ | |||
#define DISTRHO_UI_CUSTOM_WIDGET_TYPE | |||
/** | |||
Default UI width to use when creating initial and temporary windows.@n | |||
Setting this macro allows to skip a temporary UI from being created in certain VST2 and VST3 hosts. | |||
(which would normally be done for knowing the UI size before host creates a window for it) | |||
When this macro is defined, the companion DISTRHO_UI_DEFAULT_HEIGHT macro must be defined as well. | |||
*/ | |||
#define DISTRHO_UI_DEFAULT_WIDTH 300 | |||
/** | |||
Default UI height to use when creating initial and temporary windows.@n | |||
Setting this macro allows to skip a temporary UI from being created in certain VST2 and VST3 hosts. | |||
(which would normally be done for knowing the UI size before host creates a window for it) | |||
When this macro is defined, the companion DISTRHO_UI_DEFAULT_WIDTH macro must be defined as well. | |||
*/ | |||
#define DISTRHO_UI_DEFAULT_HEIGHT 300 | |||
/** | |||
Whether the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n | |||
When enabled your %UI instance will subclass @ref NanoWidget instead of @ref Widget. | |||
@@ -640,9 +663,8 @@ START_NAMESPACE_DISTRHO | |||
/** | |||
Custom LV2 category for the plugin.@n | |||
This can be one of the following values: | |||
This is a single string, and can be one of the following values: | |||
- lv2:Plugin | |||
- lv2:AllpassPlugin | |||
- lv2:AmplifierPlugin | |||
- lv2:AnalyserPlugin | |||
@@ -688,7 +710,7 @@ START_NAMESPACE_DISTRHO | |||
/** | |||
Custom VST3 categories for the plugin.@n | |||
This is a list of categories, separated by a @c |. | |||
This is a single concatenated string of categories, separated by a @c |. | |||
Each effect category can be one of the following values: | |||
@@ -723,9 +745,70 @@ START_NAMESPACE_DISTRHO | |||
- Instrument|Synth | |||
- Instrument|Synth|Sampler | |||
@note DPF will automatically set Mono and Stereo categories when appropriate. | |||
And extra categories possible for any plugin type: | |||
- Mono | |||
- Stereo | |||
*/ | |||
#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx" | |||
#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx|Stereo" | |||
/** | |||
Custom CLAP features for the plugin.@n | |||
This is a list of features defined as a string array body, without the terminating @c , or nullptr. | |||
A top-level category can be set as feature and be one of the following values: | |||
- instrument | |||
- audio-effect | |||
- note-effect | |||
- analyzer | |||
The following sub-categories can also be set: | |||
- synthesizer | |||
- sampler | |||
- drum | |||
- drum-machine | |||
- filter | |||
- phaser | |||
- equalizer | |||
- de-esser | |||
- phase-vocoder | |||
- granular | |||
- frequency-shifter | |||
- pitch-shifter | |||
- distortion | |||
- transient-shaper | |||
- compressor | |||
- limiter | |||
- flanger | |||
- chorus | |||
- delay | |||
- reverb | |||
- tremolo | |||
- glitch | |||
- utility | |||
- pitch-correction | |||
- restoration | |||
- multi-effects | |||
- mixing | |||
- mastering | |||
And finally the following audio capabilities can be set: | |||
- mono | |||
- stereo | |||
- surround | |||
- ambisonic | |||
*/ | |||
#define DISTRHO_PLUGIN_CLAP_FEATURES "audio-effect", "stereo" | |||
/** @} */ | |||
@@ -362,7 +362,7 @@ struct ParameterRanges { | |||
*/ | |||
float getNormalizedValue(const float& value) const noexcept | |||
{ | |||
const float normValue((value - min) / (max - min)); | |||
const float normValue = (value - min) / (max - min); | |||
if (normValue <= 0.0f) | |||
return 0.0f; | |||
@@ -371,6 +371,21 @@ struct ParameterRanges { | |||
return normValue; | |||
} | |||
/** | |||
Get a value normalized to 0.0<->1.0. | |||
Overloaded function using double precision values. | |||
*/ | |||
double getNormalizedValue(const double& value) const noexcept | |||
{ | |||
const double normValue = (value - min) / (max - min); | |||
if (normValue <= 0.0) | |||
return 0.0; | |||
if (normValue >= 1.0) | |||
return 1.0; | |||
return normValue; | |||
} | |||
/** | |||
Get a value normalized to 0.0<->1.0, fixed within range. | |||
*/ | |||
@@ -381,7 +396,7 @@ struct ParameterRanges { | |||
if (value >= max) | |||
return 1.0f; | |||
const float normValue((value - min) / (max - min)); | |||
const float normValue = (value - min) / (max - min); | |||
if (normValue <= 0.0f) | |||
return 0.0f; | |||
@@ -391,6 +406,27 @@ struct ParameterRanges { | |||
return normValue; | |||
} | |||
/** | |||
Get a value normalized to 0.0<->1.0, fixed within range. | |||
Overloaded function using double precision values. | |||
*/ | |||
double getFixedAndNormalizedValue(const double& value) const noexcept | |||
{ | |||
if (value <= min) | |||
return 0.0; | |||
if (value >= max) | |||
return 1.0; | |||
const double normValue = (value - min) / (max - min); | |||
if (normValue <= 0.0) | |||
return 0.0; | |||
if (normValue >= 1.0) | |||
return 1.0; | |||
return normValue; | |||
} | |||
/** | |||
Get a proper value previously normalized to 0.0<->1.0. | |||
*/ | |||
@@ -403,6 +439,20 @@ struct ParameterRanges { | |||
return value * (max - min) + min; | |||
} | |||
/** | |||
Get a proper value previously normalized to 0.0<->1.0. | |||
Overloaded function using double precision values. | |||
*/ | |||
double getUnnormalizedValue(const double& value) const noexcept | |||
{ | |||
if (value <= 0.0) | |||
return min; | |||
if (value >= 1.0) | |||
return max; | |||
return value * (max - min) + min; | |||
} | |||
}; | |||
/** | |||
@@ -706,6 +756,16 @@ struct State { | |||
@note This value is optional and only used for LV2. | |||
*/ | |||
String description; | |||
/** | |||
Default constructor for a null state. | |||
*/ | |||
State() noexcept | |||
: hints(0x0), | |||
key(), | |||
defaultValue(), | |||
label(), | |||
description() {} | |||
}; | |||
/** | |||
@@ -951,6 +1011,16 @@ public: | |||
*/ | |||
bool isDummyInstance() const noexcept; | |||
/** | |||
Check if this plugin instance is a "selftest" one used for automated plugin tests.@n | |||
To enable this mode build with `DPF_RUNTIME_TESTING` macro defined (i.e. set as compiler build flag), | |||
and run the JACK/Standalone executable with "selftest" as its only and single argument. | |||
A few basic DSP and UI tests will run in self-test mode, with once instance having this function returning true.@n | |||
You can use this chance to do a few tests of your own as well. | |||
*/ | |||
bool isSelfTestInstance() const noexcept; | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
/** | |||
Get the current host transport time position.@n | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -18,6 +18,8 @@ | |||
#if defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||
# include "src/DistrhoPluginCarla.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP) | |||
# include "src/DistrhoPluginCLAP.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_JACK) | |||
# include "src/DistrhoPluginJACK.cpp" | |||
#elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI)) | |||
@@ -17,24 +17,29 @@ | |||
#include "src/DistrhoUI.cpp" | |||
#if defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||
// nothing | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | |||
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP) | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | |||
#elif defined(DISTRHO_PLUGIN_TARGET_JACK) | |||
// nothing | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | |||
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 0 | |||
# include "src/DistrhoUIDSSI.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_LV2) | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
# include "src/DistrhoUILV2.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST2) | |||
// nothing | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | |||
#elif defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | |||
# include "src/DistrhoUIVST3.cpp" | |||
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC) | |||
// nothing | |||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | |||
#else | |||
# error unsupported format | |||
#endif | |||
#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && !defined(DISTRHO_PLUGIN_TARGET_CARLA) && !defined(DISTRHO_PLUGIN_TARGET_JACK) && !defined(DISTRHO_PLUGIN_TARGET_VST2) && !defined(DISTRHO_PLUGIN_TARGET_VST3) | |||
#if !DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT | |||
# ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
# define DISTRHO_IS_STANDALONE 1 | |||
# else | |||
@@ -211,7 +211,7 @@ private: \ | |||
#endif | |||
/* Useful macros */ | |||
#define ARRAY_SIZE(ARRAY) sizeof(ARRAY)/sizeof(ARRAY[0]) | |||
#define ARRAY_SIZE(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0])) | |||
/* Useful typedefs */ | |||
typedef unsigned char uchar; | |||
@@ -25,6 +25,7 @@ uint32_t d_nextBufferSize = 0; | |||
double d_nextSampleRate = 0.0; | |||
const char* d_nextBundlePath = nullptr; | |||
bool d_nextPluginIsDummy = false; | |||
bool d_nextPluginIsSelfTest = false; | |||
bool d_nextCanRequestParameterValueChanges = false; | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
@@ -42,45 +43,45 @@ const PortGroupWithId PluginExporter::sFallbackPortGroup; | |||
Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) | |||
: pData(new PrivateData()) | |||
{ | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
pData->audioPorts = new AudioPortWithBusId[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
#endif | |||
#endif | |||
#ifdef DPF_ABORT_ON_ERROR | |||
# define DPF_ABORT abort(); | |||
#else | |||
# define DPF_ABORT | |||
#endif | |||
#if defined(DPF_ABORT_ON_ERROR) || defined(DPF_RUNTIME_TESTING) | |||
#define DPF_ABORT abort(); | |||
#else | |||
#define DPF_ABORT | |||
#endif | |||
if (parameterCount > 0) | |||
{ | |||
pData->parameterCount = parameterCount; | |||
pData->parameters = new Parameter[parameterCount]; | |||
pData->parameters = new Parameter[parameterCount]; | |||
} | |||
if (programCount > 0) | |||
{ | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
pData->programCount = programCount; | |||
pData->programNames = new String[programCount]; | |||
#else | |||
#else | |||
d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); | |||
DPF_ABORT | |||
#endif | |||
#endif | |||
} | |||
if (stateCount > 0) | |||
{ | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
pData->stateCount = stateCount; | |||
pData->states = new State[stateCount]; | |||
#else | |||
pData->states = new State[stateCount]; | |||
#else | |||
d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); | |||
DPF_ABORT | |||
#endif | |||
#endif | |||
} | |||
#undef DPF_ABORT | |||
#undef DPF_ABORT | |||
} | |||
Plugin::~Plugin() | |||
@@ -111,6 +112,11 @@ bool Plugin::isDummyInstance() const noexcept | |||
return pData->isDummy; | |||
} | |||
bool Plugin::isSelfTestInstance() const noexcept | |||
{ | |||
return pData->isSelfTest; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
const TimePosition& Plugin::getTimePosition() const noexcept | |||
{ | |||
@@ -183,6 +183,17 @@ | |||
# define DISTRHO_PLUGIN_HAS_UI 0 | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Make sure both default width and height are provided | |||
#if defined(DISTRHO_UI_DEFAULT_WIDTH) && !defined(DISTRHO_UI_DEFAULT_HEIGHT) | |||
# error DISTRHO_UI_DEFAULT_WIDTH is defined but DISTRHO_UI_DEFAULT_HEIGHT is not | |||
#endif | |||
#if defined(DISTRHO_UI_DEFAULT_HEIGHT) && !defined(DISTRHO_UI_DEFAULT_WIDTH) | |||
# error DISTRHO_UI_DEFAULT_HEIGHT is defined but DISTRHO_UI_DEFAULT_WIDTH is not | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Prevent users from messing about with DPF internals | |||
@@ -39,6 +39,7 @@ extern uint32_t d_nextBufferSize; | |||
extern double d_nextSampleRate; | |||
extern const char* d_nextBundlePath; | |||
extern bool d_nextPluginIsDummy; | |||
extern bool d_nextPluginIsSelfTest; | |||
extern bool d_nextCanRequestParameterValueChanges; | |||
// ----------------------------------------------------------------------- | |||
@@ -67,7 +68,8 @@ struct PortGroupWithId : PortGroup { | |||
groupId(kPortGroupNone) {} | |||
}; | |||
static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup) | |||
static inline | |||
void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup) | |||
{ | |||
switch (groupId) | |||
{ | |||
@@ -86,12 +88,62 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por | |||
} | |||
} | |||
static inline | |||
void strncpy(char* const dst, const char* const src, const size_t length) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(length > 0,); | |||
if (const size_t len = std::min(std::strlen(src), length-1U)) | |||
{ | |||
std::memcpy(dst, src, len); | |||
dst[len] = '\0'; | |||
} | |||
else | |||
{ | |||
dst[0] = '\0'; | |||
} | |||
} | |||
template<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 | |||
struct Plugin::PrivateData { | |||
const bool canRequestParameterValueChanges; | |||
const bool isDummy; | |||
const bool isSelfTest; | |||
bool isProcessing; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
@@ -136,6 +188,7 @@ struct Plugin::PrivateData { | |||
PrivateData() noexcept | |||
: canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges), | |||
isDummy(d_nextPluginIsDummy), | |||
isSelfTest(d_nextPluginIsSelfTest), | |||
isProcessing(false), | |||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
audioPorts(nullptr), | |||
@@ -324,7 +377,8 @@ public: | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
if (fData->stateCount != 0) | |||
{ | |||
if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState) | |||
if ((void*)(fPlugin->*(static_cast<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`"); | |||
abort(); | |||
@@ -593,6 +647,11 @@ public: | |||
return (getParameterHints(index) & kParameterIsOutput) != 0x0; | |||
} | |||
bool isParameterInteger(const uint32_t index) const noexcept | |||
{ | |||
return (getParameterHints(index) & kParameterIsInteger) != 0x0; | |||
} | |||
bool isParameterTrigger(const uint32_t index) const noexcept | |||
{ | |||
return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger; | |||
@@ -38,6 +38,12 @@ | |||
#include "jackbridge/JackBridge.cpp" | |||
#include "lv2/lv2.h" | |||
#ifdef DISTRHO_OS_MAC | |||
# define Point CocoaPoint | |||
# include <CoreFoundation/CoreFoundation.h> | |||
# undef Point | |||
#endif | |||
#ifndef DISTRHO_OS_WINDOWS | |||
# include <signal.h> | |||
# include <unistd.h> | |||
@@ -789,11 +795,11 @@ protected: | |||
while (! shouldThreadExit()) | |||
{ | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
plugin.run(inputs, outputs, 128, nullptr, 0); | |||
#else | |||
#else | |||
plugin.run(inputs, outputs, 128); | |||
#endif | |||
#endif | |||
d_msleep(100); | |||
} | |||
@@ -807,7 +813,7 @@ bool runSelfTests() | |||
{ | |||
d_nextBufferSize = 512; | |||
d_nextSampleRate = 44100.0; | |||
PluginExporter plugin(nullptr, nullptr, nullptr); | |||
PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); | |||
d_nextBufferSize = 0; | |||
d_nextSampleRate = 0.0; | |||
} | |||
@@ -818,7 +824,17 @@ bool runSelfTests() | |||
// simple processing | |||
{ | |||
PluginExporter plugin(nullptr, nullptr, nullptr); | |||
d_nextPluginIsSelfTest = true; | |||
PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); | |||
d_nextPluginIsSelfTest = false; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
UIExporter ui(nullptr, 0, plugin.getSampleRate(), | |||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |||
plugin.getInstancePointer(), 0.0); | |||
ui.showAndFocus(); | |||
#endif | |||
plugin.activate(); | |||
plugin.deactivate(); | |||
plugin.setBufferSize(128); | |||
@@ -833,20 +849,26 @@ bool runSelfTests() | |||
for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
outputs[i] = buffer; | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
plugin.run(inputs, outputs, 128, nullptr, 0); | |||
#else | |||
#else | |||
plugin.run(inputs, outputs, 128); | |||
#endif | |||
#endif | |||
plugin.deactivate(); | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
ui.plugin_idle(); | |||
#endif | |||
} | |||
return true; | |||
// 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 procTestB(pluginB); | |||
PluginProcessTestingThread procTestC(pluginC); | |||
@@ -860,7 +882,7 @@ bool runSelfTests() | |||
// stop the 2nd instance now | |||
procTestB.stopThread(5000); | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
// start UI in the middle of this | |||
{ | |||
UIExporter uiA(nullptr, 0, pluginA.getSampleRate(), | |||
@@ -887,7 +909,7 @@ bool runSelfTests() | |||
d_msleep(100); | |||
} | |||
} | |||
#endif | |||
#endif | |||
procTestA.stopThread(5000); | |||
procTestC.stopThread(5000); | |||
@@ -905,12 +927,47 @@ int main(int argc, char* argv[]) | |||
{ | |||
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) | |||
{ | |||
#ifdef DPF_RUNTIME_TESTING | |||
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 | |||
* 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; | |||
} | |||
#endif | |||
#endif | |||
jack_status_t status = jack_status_t(0x0); | |||
jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); | |||
@@ -982,7 +1039,17 @@ int main(int argc, char* argv[]) | |||
else | |||
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 | |||
if (const HMODULE user32 = LoadLibrary("user32.dll")) | |||
{ | |||
@@ -1007,49 +1074,19 @@ int main(int argc, char* argv[]) | |||
return 1; | |||
} | |||
initSignalHandler(); | |||
d_nextBufferSize = jackbridge_get_buffer_size(client); | |||
d_nextSampleRate = jackbridge_get_sample_rate(client); | |||
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; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
if (argc == 3 && std::strcmp(argv[1], "embed") == 0) | |||
winId = static_cast<uintptr_t>(std::atoll(argv[2])); | |||
#endif | |||
#endif | |||
const PluginJack p(client, winId); | |||
#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI | |||
#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI | |||
/* the code below is based on | |||
* https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/ | |||
*/ | |||
@@ -1075,14 +1112,9 @@ int main(int argc, char* argv[]) | |||
ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release | |||
SendInput(1, &ip, sizeof(INPUT)); | |||
} | |||
#endif | |||
#endif | |||
return 0; | |||
#ifndef DPF_RUNTIME_TESTING | |||
// unused | |||
(void)argc; (void)argv; | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------- |
@@ -620,22 +620,22 @@ public: | |||
// Run plugin | |||
if (sampleCount != 0) | |||
{ | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
fRunCount = mod_license_run_begin(fRunCount, sampleCount); | |||
#endif | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount); | |||
#else | |||
#else | |||
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | |||
#endif | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++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 | |||
if (d_isNotZero(fLastPositionData.speed)) | |||
{ | |||
@@ -691,12 +691,12 @@ public: | |||
fPlugin.setTimePosition(fTimePosition); | |||
} | |||
#endif | |||
#endif | |||
} | |||
updateParameterOutputsAndTriggers(); | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
fEventsOutData.initIfNeeded(fURIDs.atomSequence); | |||
LV2_Atom_Event* aev; | |||
@@ -797,11 +797,11 @@ public: | |||
break; | |||
} | |||
} | |||
#endif | |||
#endif | |||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||
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) | |||
{ | |||
if (index >= fPlugin.getProgramCount()) | |||
@@ -895,30 +895,30 @@ public: | |||
setPortControlValue(i, fLastControlValues[i]); | |||
} | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
#if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
// Update state | |||
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
{ | |||
const String& key = cit->first; | |||
fStateMap[key] = fPlugin.getStateValue(key); | |||
} | |||
# endif | |||
#endif | |||
} | |||
#endif | |||
#endif | |||
// ------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle) | |||
{ | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
#if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
// Update current state | |||
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
{ | |||
const String& key = cit->first; | |||
fStateMap[key] = fPlugin.getStateValue(key); | |||
} | |||
# endif | |||
#endif | |||
String lv2key; | |||
LV2_URID urid; | |||
@@ -1021,11 +1021,11 @@ public: | |||
setState(key, value); | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
// signal msg needed for UI | |||
if ((hints & kStateIsOnlyForDSP) == 0x0) | |||
fNeededUiSends[i] = true; | |||
#endif | |||
#endif | |||
} | |||
return LV2_STATE_SUCCESS; | |||
@@ -1090,16 +1090,16 @@ public: | |||
{ | |||
return LV2_WORKER_SUCCESS; | |||
} | |||
#endif | |||
#endif | |||
// ------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
void* lv2_get_instance_pointer() | |||
{ | |||
return fPlugin.getInstancePointer(); | |||
} | |||
#endif | |||
#endif | |||
// ------------------------------------------------------------------- | |||
@@ -1107,36 +1107,36 @@ private: | |||
PluginExporter fPlugin; | |||
const bool fUsingNominal; // if false use maxBlockLength | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
uint32_t fRunCount; | |||
#endif | |||
#endif | |||
// LV2 ports | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
const float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||
#else | |||
#else | |||
const float** fPortAudioIns; | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
#else | |||
#else | |||
float** fPortAudioOuts; | |||
#endif | |||
#endif | |||
float** fPortControls; | |||
#if DISTRHO_LV2_USE_EVENTS_IN | |||
#if DISTRHO_LV2_USE_EVENTS_IN | |||
LV2_Atom_Sequence* fPortEventsIn; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
float* fPortLatency; | |||
#endif | |||
#endif | |||
// Temporary data | |||
float* fLastControlValues; | |||
double fSampleRate; | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
MidiEvent fMidiEvents[kMaxMidiEvents]; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
TimePosition fTimePosition; | |||
struct Lv2PositionData { | |||
@@ -1160,9 +1160,9 @@ private: | |||
ticksPerBeat(-1.0) {} | |||
} fLastPositionData; | |||
#endif | |||
#endif | |||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||
struct Lv2EventsOutData { | |||
uint32_t capacity, offset; | |||
LV2_Atom_Sequence* port; | |||
@@ -1198,7 +1198,7 @@ private: | |||
} | |||
} fEventsOutData; | |||
#endif | |||
#endif | |||
// LV2 URIDs | |||
struct URIDs { | |||
@@ -1262,13 +1262,13 @@ private: | |||
} fURIDs; | |||
// LV2 features | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
const LV2_ControlInputPort_Change_Request* const fCtrlInPortChangeReq; | |||
#endif | |||
#endif | |||
const LV2_URID_Map* const fUridMap; | |||
const LV2_Worker_Schedule* const fWorker; | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
LV2_Atom_Forge fAtomForge; | |||
StringToStringMap fStateMap; | |||
UridToStringMap fUridStateMap; | |||
@@ -1321,7 +1321,7 @@ private: | |||
d_stderr("Failed to find plugin state with key \"%s\"", key); | |||
return false; | |||
} | |||
#endif | |||
#endif | |||
void updateParameterOutputsAndTriggers() | |||
{ | |||
@@ -1341,13 +1341,13 @@ private: | |||
} | |||
} | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
if (fPortLatency != nullptr) | |||
*fPortLatency = fPlugin.getLatency(); | |||
#endif | |||
#endif | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |||
bool requestParameterValueChange(const uint32_t index, const float value) | |||
{ | |||
if (fCtrlInPortChangeReq == nullptr) | |||
@@ -1359,16 +1359,16 @@ private: | |||
{ | |||
return (((PluginLv2*)ptr)->requestParameterValueChange(index, value) == 0); | |||
} | |||
#endif | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) | |||
{ | |||
return ((PluginLv2*)ptr)->updateState(key, value); | |||
} | |||
#endif | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
bool writeMidi(const MidiEvent& midiEvent) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fEventsOutData.port != nullptr, false); | |||
@@ -1398,7 +1398,7 @@ private: | |||
{ | |||
return ((PluginLv2*)ptr)->writeMidi(midiEvent); | |||
} | |||
#endif | |||
#endif | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -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 { | |||
#if DPF_VST3_USES_SEPARATE_CONTROLLER | |||
#if DPF_VST3_USES_SEPARATE_CONTROLLER | |||
kVst3InternalParameterBufferSize, | |||
kVst3InternalParameterSampleRate, | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
kVst3InternalParameterLatency, | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
kVst3InternalParameterProgram, | |||
#endif | |||
#endif | |||
kVst3InternalParameterBaseCount, | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, | |||
kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, | |||
kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end | |||
#else | |||
#else | |||
kVst3InternalParameterCount = kVst3InternalParameterBaseCount | |||
#endif | |||
#endif | |||
}; | |||
#if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
@@ -136,22 +136,6 @@ size_t strlen_utf16(const int16_t* const str) | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
static inline | |||
void strncpy(char* const dst, const char* const src, const size_t length) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(length > 0,); | |||
if (const size_t len = std::min(std::strlen(src), length-1U)) | |||
{ | |||
std::memcpy(dst, src, len); | |||
dst[len] = '\0'; | |||
} | |||
else | |||
{ | |||
dst[0] = '\0'; | |||
} | |||
} | |||
static inline | |||
void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length) | |||
{ | |||
@@ -201,15 +185,8 @@ void strncpy_utf16(int16_t* const dst, const char* const src, const size_t lengt | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
template<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,); | |||
@@ -224,27 +201,15 @@ static void snprintf_utf16_t(int16_t* const dst, const T value, const char* cons | |||
} | |||
static inline | |||
void snprintf_f32(char* const dst, const float value, const size_t size) | |||
{ | |||
return snprintf_t<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 | |||
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 | |||
@@ -65,7 +65,7 @@ START_NAMESPACE_DISTRHO | |||
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 | |||
static const writeMidiFunc writeMidiCallback = nullptr; | |||
@@ -117,7 +117,7 @@ struct ParameterAndNotesHelper | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0; | |||
virtual void setStateFromUI(const char* key, const char* value) = 0; | |||
#endif | |||
}; | |||
@@ -575,17 +575,25 @@ public: | |||
} | |||
else | |||
{ | |||
double scaleFactor = fLastScaleFactor; | |||
#if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) | |||
fVstRect.right = DISTRHO_UI_DEFAULT_WIDTH; | |||
fVstRect.bottom = DISTRHO_UI_DEFAULT_HEIGHT; | |||
if (d_isZero(scaleFactor)) | |||
scaleFactor = 1.0; | |||
#else | |||
UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), | |||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, | |||
fPlugin.getInstancePointer(), fLastScaleFactor); | |||
fVstRect.right = tmpUI.getWidth(); | |||
fPlugin.getInstancePointer(), scaleFactor); | |||
fVstRect.right = tmpUI.getWidth(); | |||
fVstRect.bottom = tmpUI.getHeight(); | |||
# ifdef DISTRHO_OS_MAC | |||
const double scaleFactor = tmpUI.getScaleFactor(); | |||
scaleFactor = tmpUI.getScaleFactor(); | |||
tmpUI.quit(); | |||
#endif | |||
#ifdef DISTRHO_OS_MAC | |||
fVstRect.right /= scaleFactor; | |||
fVstRect.bottom /= scaleFactor; | |||
# endif | |||
tmpUI.quit(); | |||
#endif | |||
} | |||
*(ERect**)ptr = &fVstRect; | |||
return 1; | |||
@@ -995,7 +1003,7 @@ public: | |||
else | |||
fTimePosition.bbt.beatsPerMinute = 120.0; | |||
if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) | |||
if ((vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) == (kVstPpqPosValid|kVstTimeSigValid)) | |||
{ | |||
const double ppqPos = std::abs(vstTimeInfo->ppqPos); | |||
const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator; | |||
@@ -1131,7 +1139,7 @@ private: | |||
{ | |||
if (fPlugin.isParameterOutput(i)) | |||
{ | |||
// NOTE: no output parameter support in VST, simulate it here | |||
// NOTE: no output parameter support in VST2, simulate it here | |||
curValue = fPlugin.getParameterValue(i); | |||
if (d_isEqual(curValue, parameterValues[i])) | |||
@@ -1230,12 +1238,12 @@ private: | |||
// functions called from the UI side, may block | |||
# if DISTRHO_PLUGIN_HAS_UI | |||
void setStateFromUI(const char* const key, const char* const newValue) override | |||
void setStateFromUI(const char* const key, const char* const value) override | |||
# else | |||
void setStateFromUI(const char* const key, const char* const newValue) | |||
void setStateFromUI(const char* const key, const char* const value) | |||
# endif | |||
{ | |||
fPlugin.setState(key, newValue); | |||
fPlugin.setState(key, value); | |||
// check if we want to save this key | |||
if (! fPlugin.wantStateKey(key)) | |||
@@ -1248,7 +1256,7 @@ private: | |||
if (dkey == key) | |||
{ | |||
it->second = newValue; | |||
it->second = value; | |||
return; | |||
} | |||
} | |||
@@ -202,13 +202,23 @@ UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint hei | |||
* UI */ | |||
UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize) | |||
: UIWidget(UI::PrivateData::createNextWindow(this, width, height)), | |||
: UIWidget(UI::PrivateData::createNextWindow(this, | |||
#ifdef DISTRHO_UI_DEFAULT_WIDTH | |||
width == 0 ? DISTRHO_UI_DEFAULT_WIDTH : | |||
#endif | |||
width, | |||
#ifdef DISTRHO_UI_DEFAULT_WIDTH | |||
height == 0 ? DISTRHO_UI_DEFAULT_WIDTH : | |||
#endif | |||
height)), | |||
uiData(UI::PrivateData::s_nextPrivateData) | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
if (width != 0 && height != 0) | |||
{ | |||
#ifndef DISTRHO_UI_DEFAULT_WIDTH | |||
Widget::setSize(width, height); | |||
#endif | |||
if (automaticallyScaleAndSetAsMinimumSize) | |||
setGeometryConstraints(width, height, true, true, true); | |||
@@ -405,19 +415,19 @@ void UI::onResize(const ResizeEvent& ev) | |||
#endif | |||
} | |||
// NOTE: only used for VST3 | |||
// NOTE: only used for VST3 and CLAP | |||
void UI::requestSizeChange(const uint width, const uint height) | |||
{ | |||
# ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
#if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP) | |||
if (uiData->initializing) | |||
uiData->window->setSizeForVST3(width, height); | |||
uiData->window->setSizeFromHost(width, height); | |||
else | |||
uiData->setSizeCallback(width, height); | |||
# else | |||
#else | |||
// unused | |||
(void)width; | |||
(void)height; | |||
# endif | |||
#endif | |||
} | |||
#endif | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* 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 | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -262,7 +262,7 @@ public: | |||
// ------------------------------------------------------------------- | |||
#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) | |||
#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) | |||
void idleForVST3() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
@@ -271,7 +271,7 @@ public: | |||
ui->uiIdle(); | |||
} | |||
# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs) | |||
{ | |||
uiData->window->addIdleCallback(cb, timerFrequencyInMs); | |||
@@ -281,31 +281,31 @@ public: | |||
{ | |||
uiData->window->removeIdleCallback(cb); | |||
} | |||
# endif | |||
#endif | |||
#endif | |||
#endif | |||
// ------------------------------------------------------------------- | |||
void setWindowOffset(const int x, const int y) | |||
{ | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// TODO | |||
(void)x; (void)y; | |||
#else | |||
#else | |||
uiData->window->setOffset(x, y); | |||
#endif | |||
#endif | |||
} | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
void setWindowSizeForVST3(const uint width, const uint height) | |||
#if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP) | |||
void setWindowSizeFromHost(const uint width, const uint height) | |||
{ | |||
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
ui->setSize(width, height); | |||
# else | |||
uiData->window->setSizeForVST3(width, height); | |||
# endif | |||
#else | |||
uiData->window->setSizeFromHost(width, height); | |||
#endif | |||
} | |||
#endif | |||
#endif | |||
void setWindowTitle(const char* const uiTitle) | |||
{ | |||
@@ -42,8 +42,11 @@ typedef struct _LV2_Atom_MidiEvent { | |||
uint8_t data[3]; /**< MIDI data (body). */ | |||
} LV2_Atom_MidiEvent; | |||
#if ! DISTRHO_PLUGIN_WANT_STATE | |||
static constexpr const setStateFunc setStateCallback = nullptr; | |||
#endif | |||
#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
static const sendNoteFunc sendNoteCallback = nullptr; | |||
static constexpr const sendNoteFunc sendNoteCallback = nullptr; | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
@@ -76,19 +79,8 @@ public: | |||
const float scaleFactor, | |||
const uint32_t bgColor, | |||
const uint32_t fgColor) | |||
: fUI(this, winId, sampleRate, | |||
editParameterCallback, | |||
setParameterCallback, | |||
setStateCallback, | |||
sendNoteCallback, | |||
nullptr, // resize is very messy, hosts can do it without extensions | |||
fileRequestCallback, | |||
bundlePath, | |||
dspPtr, | |||
scaleFactor, | |||
bgColor, | |||
fgColor), | |||
fUridMap(uridMap), | |||
: fUridMap(uridMap), | |||
fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)), | |||
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)), | |||
fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)), | |||
fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)), | |||
@@ -97,15 +89,23 @@ public: | |||
fURIDs(uridMap), | |||
fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled") | |||
: LV2UI_INVALID_PORT_INDEX), | |||
fWinIdWasNull(winId == 0) | |||
fWinIdWasNull(winId == 0), | |||
fUI(this, winId, sampleRate, | |||
editParameterCallback, | |||
setParameterCallback, | |||
setStateCallback, | |||
sendNoteCallback, | |||
nullptr, // resize is very messy, hosts can do it without extensions | |||
fileRequestCallback, | |||
bundlePath, dspPtr, scaleFactor, bgColor, fgColor) | |||
{ | |||
if (widget != nullptr) | |||
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
// tell the DSP we're ready to receive msgs | |||
setState("__dpf_ui_data__", ""); | |||
#endif | |||
#endif | |||
if (winId != 0) | |||
return; | |||
@@ -169,7 +169,7 @@ public: | |||
fUI.parameterChanged(rindex-parameterOffset, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
else if (format == fURIDs.atomEventTransfer) | |||
{ | |||
const LV2_Atom* const atom = (const LV2_Atom*)buffer; | |||
@@ -181,39 +181,44 @@ public: | |||
fUI.stateChanged(key, value); | |||
} | |||
else if (atom->type == fURIDs.atomObject) | |||
else if (atom->type == fURIDs.atomObject && fUridUnmap != nullptr) | |||
{ | |||
/* TODO | |||
const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)LV2_ATOM_BODY_CONST(atom); | |||
const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)atom; | |||
const LV2_Atom* property = nullptr; | |||
const LV2_Atom* avalue = nullptr; | |||
lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &avalue, 0); | |||
const LV2_Atom* atomvalue = nullptr; | |||
lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &atomvalue, 0); | |||
DISTRHO_SAFE_ASSERT_RETURN(property != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(avalue != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(atomvalue != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID,); | |||
DISTRHO_SAFE_ASSERT_RETURN(avalue->type == fURIDs.atomPath || avalue->type == fURIDs.atomString,); | |||
DISTRHO_SAFE_ASSERT_RETURN(atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString,); | |||
if (property != nullptr && property->type == fURIDs.atomURID && | |||
avalue != nullptr && (avalue->type == fURIDs.atomPath || avalue->type == fURIDs.atomString)) | |||
atomvalue != nullptr && (atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString)) | |||
{ | |||
const char* const key = (const char*)LV2_ATOM_BODY_CONST(property); | |||
const char* const value = (const char*)LV2_ATOM_BODY_CONST(avalue); | |||
const LV2_URID dpf_lv2_urid = ((const LV2_Atom_URID*)property)->body; | |||
DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_urid != 0,); | |||
const char* const dpf_lv2_key = fUridUnmap->unmap(fUridUnmap->handle, dpf_lv2_urid); | |||
DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_key != nullptr,); | |||
/*constexpr*/ const size_t reqLen = std::strlen(DISTRHO_PLUGIN_URI "#"); | |||
DISTRHO_SAFE_ASSERT_RETURN(std::strlen(dpf_lv2_key) > reqLen,); | |||
d_stdout("received atom object '%s' '%s'", key, value); | |||
const char* const key = dpf_lv2_key + reqLen; | |||
const char* const value = (const char*)LV2_ATOM_BODY_CONST(atomvalue); | |||
fUI.stateChanged(key, value); | |||
} | |||
*/ | |||
} | |||
else | |||
{ | |||
d_stdout("received atom not dpfKeyValue"); | |||
d_stdout("DPF :: received atom not handled"); | |||
} | |||
} | |||
#endif | |||
#endif | |||
} | |||
// ------------------------------------------------------------------- | |||
@@ -269,24 +274,91 @@ public: | |||
// ------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
void lv2ui_select_program(const uint32_t bank, const uint32_t program) | |||
{ | |||
const uint32_t realProgram = bank * 128 + program; | |||
fUI.programLoaded(realProgram); | |||
} | |||
#endif | |||
#endif | |||
// ------------------------------------------------------------------- | |||
protected: | |||
private: | |||
// LV2 features | |||
const LV2_URID_Map* const fUridMap; | |||
const LV2_URID_Unmap* const fUridUnmap; | |||
const LV2UI_Port_Map* const fUiPortMap; | |||
const LV2UI_Request_Value* const fUiRequestValue; | |||
const LV2UI_Touch* const fUiTouch; | |||
// LV2 UI stuff | |||
const LV2UI_Controller fController; | |||
const LV2UI_Write_Function fWriteFunction; | |||
// LV2 URIDs | |||
const struct URIDs { | |||
const LV2_URID_Map* _uridMap; | |||
const LV2_URID dpfKeyValue; | |||
const LV2_URID atomEventTransfer; | |||
const LV2_URID atomFloat; | |||
const LV2_URID atomLong; | |||
const LV2_URID atomObject; | |||
const LV2_URID atomPath; | |||
const LV2_URID atomString; | |||
const LV2_URID atomURID; | |||
const LV2_URID midiEvent; | |||
const LV2_URID paramSampleRate; | |||
const LV2_URID patchProperty; | |||
const LV2_URID patchSet; | |||
const LV2_URID patchValue; | |||
URIDs(const LV2_URID_Map* const uridMap) | |||
: _uridMap(uridMap), | |||
dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
atomEventTransfer(map(LV2_ATOM__eventTransfer)), | |||
atomFloat(map(LV2_ATOM__Float)), | |||
atomLong(map(LV2_ATOM__Long)), | |||
atomObject(map(LV2_ATOM__Object)), | |||
atomPath(map(LV2_ATOM__Path)), | |||
atomString(map(LV2_ATOM__String)), | |||
atomURID(map(LV2_ATOM__URID)), | |||
midiEvent(map(LV2_MIDI__MidiEvent)), | |||
paramSampleRate(map(LV2_PARAMETERS__sampleRate)), | |||
patchProperty(map(LV2_PATCH__property)), | |||
patchSet(map(LV2_PATCH__Set)), | |||
patchValue(map(LV2_PATCH__value)) {} | |||
inline LV2_URID map(const char* const uri) const | |||
{ | |||
return _uridMap->map(_uridMap->handle, uri); | |||
} | |||
} fURIDs; | |||
// index of bypass parameter, if present | |||
const uint32_t fBypassParameterIndex; | |||
// using ui:showInterface if true | |||
const bool fWinIdWasNull; | |||
// Plugin UI (after LV2 stuff so the UI can call into us during its constructor) | |||
UIExporter fUI; | |||
// ---------------------------------------------------------------------------------------------------------------- | |||
// DPF callbacks | |||
void editParameterValue(const uint32_t rindex, const bool started) | |||
{ | |||
if (fUiTouch != nullptr && fUiTouch->touch != nullptr) | |||
fUiTouch->touch(fUiTouch->handle, rindex, started); | |||
} | |||
static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) | |||
{ | |||
static_cast<UiLv2*>(ptr)->editParameterValue(rindex, started); | |||
} | |||
void setParameterValue(const uint32_t rindex, float value) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
@@ -297,6 +369,12 @@ protected: | |||
fWriteFunction(fController, rindex, sizeof(float), 0, &value); | |||
} | |||
static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) | |||
{ | |||
static_cast<UiLv2*>(ptr)->setParameterValue(rindex, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
void setState(const char* const key, const char* const value) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
@@ -336,7 +414,13 @@ protected: | |||
free(atomBuf); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
static void setStateCallback(void* const ptr, const char* const key, const char* const value) | |||
{ | |||
static_cast<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) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
@@ -358,7 +442,12 @@ protected: | |||
fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), | |||
fURIDs.atomEventTransfer, &atomMidiEvent); | |||
} | |||
#endif | |||
static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
{ | |||
static_cast<UiLv2*>(ptr)->sendNote(channel, note, velocity); | |||
} | |||
#endif | |||
bool fileRequest(const char* const key) | |||
{ | |||
@@ -379,97 +468,10 @@ protected: | |||
return r == LV2UI_REQUEST_VALUE_SUCCESS; | |||
} | |||
private: | |||
UIExporter fUI; | |||
// LV2 features | |||
const LV2_URID_Map* const fUridMap; | |||
const LV2UI_Port_Map* const fUiPortMap; | |||
const LV2UI_Request_Value* const fUiRequestValue; | |||
const LV2UI_Touch* const fUiTouch; | |||
// LV2 UI stuff | |||
const LV2UI_Controller fController; | |||
const LV2UI_Write_Function fWriteFunction; | |||
// LV2 URIDs | |||
const struct URIDs { | |||
const LV2_URID_Map* _uridMap; | |||
const LV2_URID dpfKeyValue; | |||
const LV2_URID atomEventTransfer; | |||
const LV2_URID atomFloat; | |||
const LV2_URID atomLong; | |||
const LV2_URID atomObject; | |||
const LV2_URID atomPath; | |||
const LV2_URID atomString; | |||
const LV2_URID atomURID; | |||
const LV2_URID midiEvent; | |||
const LV2_URID paramSampleRate; | |||
const LV2_URID patchProperty; | |||
const LV2_URID patchSet; | |||
const LV2_URID patchValue; | |||
URIDs(const LV2_URID_Map* const uridMap) | |||
: _uridMap(uridMap), | |||
dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
atomEventTransfer(map(LV2_ATOM__eventTransfer)), | |||
atomFloat(map(LV2_ATOM__Float)), | |||
atomLong(map(LV2_ATOM__Long)), | |||
atomObject(map(LV2_ATOM__Object)), | |||
atomPath(map(LV2_ATOM__Path)), | |||
atomString(map(LV2_ATOM__String)), | |||
atomURID(map(LV2_ATOM__URID)), | |||
midiEvent(map(LV2_MIDI__MidiEvent)), | |||
paramSampleRate(map(LV2_PARAMETERS__sampleRate)), | |||
patchProperty(map(LV2_PATCH__property)), | |||
patchSet(map(LV2_PATCH__Set)), | |||
patchValue(map(LV2_PATCH__value)) {} | |||
inline LV2_URID map(const char* const uri) const | |||
{ | |||
return _uridMap->map(_uridMap->handle, uri); | |||
} | |||
} fURIDs; | |||
// index of bypass parameter, if present | |||
const uint32_t fBypassParameterIndex; | |||
// using ui:showInterface if true | |||
const bool fWinIdWasNull; | |||
// ------------------------------------------------------------------- | |||
// Callbacks | |||
#define uiPtr ((UiLv2*)ptr) | |||
static void editParameterCallback(void* ptr, uint32_t rindex, bool started) | |||
{ | |||
uiPtr->editParameterValue(rindex, started); | |||
} | |||
static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||
{ | |||
uiPtr->setParameterValue(rindex, value); | |||
} | |||
static void setStateCallback(void* ptr, const char* key, const char* value) | |||
{ | |||
uiPtr->setState(key, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||
{ | |||
uiPtr->sendNote(channel, note, velocity); | |||
} | |||
#endif | |||
static bool fileRequestCallback(void* ptr, const char* key) | |||
{ | |||
return uiPtr->fileRequest(key); | |||
return static_cast<UiLv2*>(ptr)->fileRequest(key); | |||
} | |||
#undef uiPtr | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -215,7 +215,7 @@ public: | |||
puglBackendLeave(pData->view); | |||
} | |||
// used for temporary windows (VST2/3 get size without active/visible view) | |||
// used for temporary windows (VST/CLAP get size without active/visible view) | |||
void setIgnoreIdleCallbacks(const bool ignore = true) | |||
{ | |||
pData->ignoreIdleCallbacks = ignore; | |||
@@ -228,8 +228,8 @@ public: | |||
puglBackendEnter(pData->view); | |||
} | |||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||
void setSizeForVST3(const uint width, const uint height) | |||
#if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP) | |||
void setSizeFromHost(const uint width, const uint height) | |||
{ | |||
puglSetSizeAndDefault(pData->view, width, height); | |||
} | |||
@@ -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 | |||
static constexpr const setStateFunc setStateCallback = nullptr; | |||
#endif | |||
#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
static constexpr const sendNoteFunc sendNoteCallback = nullptr; | |||
#endif | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Static data, see DistrhoPlugin.cpp | |||
@@ -73,13 +73,18 @@ static void applyGeometryConstraints(const uint minimumWidth, | |||
const bool keepAspectRatio, | |||
v3_view_rect* const rect) | |||
{ | |||
d_stdout("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | BEFORE", | |||
minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); | |||
d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | BEFORE", | |||
minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); | |||
const int32_t minWidth = static_cast<int32_t>(minimumWidth); | |||
const int32_t minHeight = static_cast<int32_t>(minimumHeight); | |||
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 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) | |||
rect->bottom = minHeight; | |||
d_stdout("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | AFTER", | |||
minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); | |||
d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | AFTER", | |||
minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -133,35 +138,27 @@ static uint translateVST3Modifiers(const int64_t modifiers) noexcept | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !DPF_VST3_USING_HOST_RUN_LOOP | |||
/** | |||
* Helper class for getting a native idle timer, either through pugl or via native APIs. | |||
* Helper class for getting a native idle timer via native APIs. | |||
*/ | |||
#if !DPF_VST3_USING_HOST_RUN_LOOP | |||
class NativeIdleCallback : public IdleCallback | |||
class NativeIdleHelper | |||
{ | |||
public: | |||
NativeIdleCallback(UIExporter& ui) | |||
: fUI(ui), | |||
fCallbackRegistered(false) | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#if defined(DISTRHO_OS_MAC) | |||
, fTimerRef(nullptr) | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
, fTimerWindow(nullptr) | |||
, fTimerWindowClassName() | |||
#endif | |||
#endif | |||
NativeIdleHelper(IdleCallback* const callback) | |||
: fCallback(callback), | |||
#ifdef DISTRHO_OS_MAC | |||
fTimerRef(nullptr) | |||
#else | |||
fTimerWindow(nullptr), | |||
fTimerWindowClassName() | |||
#endif | |||
{ | |||
} | |||
void registerNativeIdleCallback() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(!fCallbackRegistered,); | |||
fCallbackRegistered = true; | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); | |||
#elif defined(DISTRHO_OS_MAC) | |||
#ifdef DISTRHO_OS_MAC | |||
constexpr const CFTimeInterval interval = DPF_VST3_TIMER_INTERVAL * 0.0001; | |||
CFRunLoopTimerContext context = {}; | |||
@@ -171,12 +168,9 @@ public: | |||
DISTRHO_SAFE_ASSERT_RETURN(fTimerRef != nullptr,); | |||
CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
/* We cannot assume anything about the native parent window passed as a parameter (winId) to the | |||
* UIVst3 constructor because we do not own it. | |||
* These parent windows have class names like 'reaperPluginHostWrapProc' and 'JUCE_nnnnnn'. | |||
* | |||
* Create invisible window to handle a timer instead. | |||
#else | |||
/* | |||
* Create an invisible window to handle a timer. | |||
* There is no need for implementing a window proc because DefWindowProc already calls the | |||
* callback function when processing WM_TIMER messages. | |||
*/ | |||
@@ -214,12 +208,10 @@ public: | |||
void unregisterNativeIdleCallback() | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
fUI.removeIdleCallbackForVST3(this); | |||
#elif defined(DISTRHO_OS_MAC) | |||
#ifdef DISTRHO_OS_MAC | |||
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); | |||
CFRelease(fTimerRef); | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,); | |||
KillTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID); | |||
DestroyWindow(fTimerWindow); | |||
@@ -228,27 +220,79 @@ public: | |||
} | |||
private: | |||
UIExporter& fUI; | |||
bool fCallbackRegistered; | |||
IdleCallback* const fCallback; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
#if defined(DISTRHO_OS_MAC) | |||
#ifdef DISTRHO_OS_MAC | |||
CFRunLoopTimerRef fTimerRef; | |||
static void platformIdleTimerCallback(CFRunLoopTimerRef, void* const info) | |||
{ | |||
static_cast<NativeIdleCallback*>(info)->idleCallback(); | |||
static_cast<NativeIdleHelper*>(info)->fCallback->idleCallback(); | |||
} | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
#else | |||
HWND fTimerWindow; | |||
String fTimerWindowClassName; | |||
WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD) | |||
{ | |||
reinterpret_cast<NativeIdleCallback*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->idleCallback(); | |||
reinterpret_cast<NativeIdleHelper*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback(); | |||
} | |||
#endif | |||
#endif | |||
}; | |||
#endif | |||
/** | |||
* Helper class for getting a native idle timer, either through pugl or via native APIs. | |||
*/ | |||
#if !DPF_VST3_USING_HOST_RUN_LOOP | |||
class NativeIdleCallback : public IdleCallback | |||
{ | |||
public: | |||
NativeIdleCallback(UIExporter& ui) | |||
: fCallbackRegistered(false), | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
fIdleHelper(this) | |||
#else | |||
fUI(ui) | |||
#endif | |||
{ | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// unused | |||
(void)ui; | |||
#endif | |||
} | |||
void registerNativeIdleCallback() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(!fCallbackRegistered,); | |||
fCallbackRegistered = true; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
fIdleHelper.registerNativeIdleCallback(); | |||
#else | |||
fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); | |||
#endif | |||
} | |||
void unregisterNativeIdleCallback() | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fCallbackRegistered,); | |||
fCallbackRegistered = false; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
fIdleHelper.unregisterNativeIdleCallback(); | |||
#else | |||
fUI.removeIdleCallbackForVST3(this); | |||
#endif | |||
} | |||
private: | |||
bool fCallbackRegistered; | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
NativeIdleHelper fIdleHelper; | |||
#else | |||
UIExporter& fUI; | |||
#endif | |||
}; | |||
#endif | |||
@@ -327,13 +371,13 @@ public: | |||
if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight) | |||
{ | |||
d_stdout("postInit sets new size as %u %u", nextWidth, nextHeight); | |||
fUI.setWindowSizeForVST3(nextWidth, nextHeight); | |||
d_debug("postInit sets new size as %u %u", nextWidth, nextHeight); | |||
fUI.setWindowSizeFromHost(nextWidth, nextHeight); | |||
} | |||
} | |||
else if (fNeedsResizeFromPlugin) | |||
{ | |||
d_stdout("postInit forcely sets size from plugin as %u %u", fUI.getWidth(), fUI.getHeight()); | |||
d_debug("postInit forcely sets size from plugin as %u %u", fUI.getWidth(), fUI.getHeight()); | |||
setSize(fUI.getWidth(), fUI.getHeight()); | |||
} | |||
@@ -408,7 +452,7 @@ public: | |||
#endif | |||
} | |||
d_stdout("getSize request returning %i %i", rect->right, rect->bottom); | |||
d_debug("getSize request returning %i %i", rect->right, rect->bottom); | |||
return V3_OK; | |||
} | |||
@@ -426,17 +470,17 @@ public: | |||
if (fIsResizingFromPlugin) | |||
{ | |||
d_stdout("host->plugin onSize request %i %i (plugin resize was active, unsetting now)", | |||
rect.right - rect.left, rect.bottom - rect.top); | |||
d_debug("host->plugin onSize request %i %i (plugin resize was active, unsetting now)", | |||
rect.right - rect.left, rect.bottom - rect.top); | |||
fIsResizingFromPlugin = false; | |||
} | |||
else | |||
{ | |||
d_stdout("host->plugin onSize request %i %i (OK)", rect.right - rect.left, rect.bottom - rect.top); | |||
d_debug("host->plugin onSize request %i %i (OK)", rect.right - rect.left, rect.bottom - rect.top); | |||
} | |||
fIsResizingFromHost = true; | |||
fUI.setWindowSizeForVST3(rect.right - rect.left, rect.bottom - rect.top); | |||
fUI.setWindowSizeFromHost(rect.right - rect.left, rect.bottom - rect.top); | |||
return V3_OK; | |||
} | |||
@@ -476,7 +520,7 @@ public: | |||
fConnection = point; | |||
d_stdout("requesting current plugin state"); | |||
d_debug("requesting current plugin state"); | |||
v3_message** const message = createMessage("init"); | |||
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |||
@@ -494,7 +538,7 @@ public: | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |||
d_stdout("reporting UI closed"); | |||
d_debug("reporting UI closed"); | |||
fReadyForPluginData = false; | |||
v3_message** const message = createMessage("close"); | |||
@@ -556,10 +600,13 @@ public: | |||
#endif | |||
} | |||
// others like latency and buffer-size do not matter on UI side | |||
return V3_OK; | |||
} | |||
const uint32_t index = static_cast<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); | |||
return V3_OK; | |||
} | |||
@@ -614,7 +661,7 @@ public: | |||
} | |||
#endif | |||
d_stdout("UIVst3 received unknown msg '%s'", msgid); | |||
d_stderr("UIVst3 received unknown msg '%s'", msgid); | |||
return V3_NOT_IMPLEMENTED; | |||
} | |||
@@ -663,19 +710,19 @@ public: | |||
if (fNeedsResizeFromPlugin) | |||
{ | |||
fNeedsResizeFromPlugin = false; | |||
d_stdout("first resize forced behaviour is now stopped"); | |||
d_debug("first resize forced behaviour is now stopped"); | |||
} | |||
if (fIsResizingFromHost) | |||
{ | |||
fIsResizingFromHost = false; | |||
d_stdout("was resizing from host, now stopped"); | |||
d_debug("was resizing from host, now stopped"); | |||
} | |||
if (fIsResizingFromPlugin) | |||
{ | |||
fIsResizingFromPlugin = false; | |||
d_stdout("was resizing from plugin, now stopped"); | |||
d_debug("was resizing from plugin, now stopped"); | |||
} | |||
} | |||
@@ -754,9 +801,9 @@ private: | |||
v3_cpp_obj_unref(message); | |||
} | |||
static void editParameterCallback(void* ptr, uint32_t rindex, bool started) | |||
static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) | |||
{ | |||
((UIVst3*)ptr)->editParameter(rindex, started); | |||
static_cast<UIVst3*>(ptr)->editParameter(rindex, started); | |||
} | |||
void setParameterValue(const uint32_t rindex, const float realValue) | |||
@@ -777,53 +824,37 @@ private: | |||
v3_cpp_obj_unref(message); | |||
} | |||
static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||
static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) | |||
{ | |||
((UIVst3*)ptr)->setParameterValue(rindex, value); | |||
static_cast<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 | |||
void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
@@ -848,38 +879,54 @@ private: | |||
v3_cpp_obj_unref(message); | |||
} | |||
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||
static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
{ | |||
((UIVst3*)ptr)->sendNote(channel, note, velocity); | |||
static_cast<UIVst3*>(ptr)->sendNote(channel, note, velocity); | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
void setState(const char* const key, const char* const value) | |||
void setSize(uint width, uint height) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); | |||
v3_message** const message = createMessage("state-set"); | |||
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |||
#ifdef DISTRHO_OS_MAC | |||
const double scaleFactor = fUI.getScaleFactor(); | |||
width /= scaleFactor; | |||
height /= scaleFactor; | |||
#endif | |||
v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |||
DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |||
if (fIsResizingFromHost) | |||
{ | |||
if (fNeedsResizeFromPlugin) | |||
{ | |||
d_debug("plugin->host setSize %u %u (FORCED, exception for first resize)", width, height); | |||
} | |||
else | |||
{ | |||
d_debug("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); | |||
return; | |||
} | |||
} | |||
else | |||
{ | |||
d_debug("plugin->host setSize %u %u (OK)", width, height); | |||
} | |||
v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |||
v3_cpp_obj(attrlist)->set_int(attrlist, "key:length", std::strlen(key)); | |||
v3_cpp_obj(attrlist)->set_int(attrlist, "value:length", std::strlen(value)); | |||
v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key)); | |||
v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value)); | |||
v3_cpp_obj(fConnection)->notify(fConnection, message); | |||
fIsResizingFromPlugin = true; | |||
v3_cpp_obj_unref(message); | |||
v3_view_rect rect; | |||
rect.left = rect.top = 0; | |||
rect.right = width; | |||
rect.bottom = height; | |||
fNextPluginRect = rect; | |||
v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); | |||
} | |||
static void setStateCallback(void* ptr, const char* key, const char* value) | |||
static void setSizeCallback(void* const ptr, const uint width, const uint height) | |||
{ | |||
((UIVst3*)ptr)->setState(key, value); | |||
static_cast<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) || | |||
v3_tuid_match(iid, v3_connection_point_iid)) | |||
{ | |||
d_stdout("UI|query_interface_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); | |||
d_debug("UI|query_interface_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); | |||
++point->refcounter; | |||
*iface = self; | |||
return V3_OK; | |||
} | |||
d_stdout("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
d_debug("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
*iface = NULL; | |||
return V3_NO_INTERFACE; | |||
@@ -955,7 +1002,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { | |||
static v3_result V3_API connect(void* const self, v3_connection_point** const other) | |||
{ | |||
dpf_ui_connection_point* const point = *static_cast<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); | |||
@@ -969,10 +1016,11 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { | |||
static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) | |||
{ | |||
d_stdout("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); | |||
d_debug("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); | |||
dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); | |||
DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); | |||
DISTRHO_SAFE_ASSERT(point->other == other); | |||
point->other = nullptr; | |||
@@ -1026,13 +1074,13 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { | |||
if (v3_tuid_match(iid, v3_funknown_iid) || | |||
v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) | |||
{ | |||
d_stdout("query_interface_view_content_scale => %p %s %p | OK", self, tuid2str(iid), iface); | |||
d_debug("query_interface_view_content_scale => %p %s %p | OK", self, tuid2str(iid), iface); | |||
++scale->refcounter; | |||
*iface = self; | |||
return V3_OK; | |||
} | |||
d_stdout("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
d_debug("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
*iface = NULL; | |||
return V3_NO_INTERFACE; | |||
@@ -1044,7 +1092,7 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { | |||
static v3_result V3_API set_content_scale_factor(void* const self, const float factor) | |||
{ | |||
dpf_plugin_view_content_scale* const scale = *static_cast<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; | |||
@@ -1088,13 +1136,13 @@ struct dpf_timer_handler : v3_timer_handler_cpp { | |||
if (v3_tuid_match(iid, v3_funknown_iid) || | |||
v3_tuid_match(iid, v3_timer_handler_iid)) | |||
{ | |||
d_stdout("query_interface_timer_handler => %p %s %p | OK", self, tuid2str(iid), iface); | |||
d_debug("query_interface_timer_handler => %p %s %p | OK", self, tuid2str(iid), iface); | |||
++timer->refcounter; | |||
*iface = self; | |||
return V3_OK; | |||
} | |||
d_stdout("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
d_debug("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
*iface = NULL; | |||
return V3_NO_INTERFACE; | |||
@@ -1155,7 +1203,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
nextHeight(0), | |||
sizeRequestedBeforeBeingAttached(false) | |||
{ | |||
d_stdout("dpf_plugin_view() with hostApplication %p", hostApplication); | |||
d_debug("dpf_plugin_view() with hostApplication %p", hostApplication); | |||
// make sure host application is valid through out this view lifetime | |||
if (hostApplication != nullptr) | |||
@@ -1183,7 +1231,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
~dpf_plugin_view() | |||
{ | |||
d_stdout("~dpf_plugin_view()"); | |||
d_debug("~dpf_plugin_view()"); | |||
connection = nullptr; | |||
scale = nullptr; | |||
@@ -1206,7 +1254,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
if (v3_tuid_match(iid, v3_funknown_iid) || | |||
v3_tuid_match(iid, v3_plugin_view_iid)) | |||
{ | |||
d_stdout("query_interface_view => %p %s %p | OK", self, tuid2str(iid), iface); | |||
d_debug("query_interface_view => %p %s %p | OK", self, tuid2str(iid), iface); | |||
++view->refcounter; | |||
*iface = self; | |||
return V3_OK; | |||
@@ -1214,8 +1262,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
if (v3_tuid_match(v3_connection_point_iid, iid)) | |||
{ | |||
d_stdout("query_interface_view => %p %s %p | OK convert %p", | |||
self, tuid2str(iid), iface, view->connection.get()); | |||
d_debug("query_interface_view => %p %s %p | OK convert %p", | |||
self, tuid2str(iid), iface, view->connection.get()); | |||
if (view->connection == nullptr) | |||
view->connection = new dpf_ui_connection_point(view->uivst3); | |||
@@ -1228,8 +1276,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
#ifndef DISTRHO_OS_MAC | |||
if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) | |||
{ | |||
d_stdout("query_interface_view => %p %s %p | OK convert %p", | |||
self, tuid2str(iid), iface, view->scale.get()); | |||
d_debug("query_interface_view => %p %s %p | OK convert %p", | |||
self, tuid2str(iid), iface, view->scale.get()); | |||
if (view->scale == nullptr) | |||
view->scale = new dpf_plugin_view_content_scale(view->uivst3); | |||
@@ -1240,7 +1288,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
} | |||
#endif | |||
d_stdout("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
d_debug("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |||
*iface = nullptr; | |||
return V3_NO_INTERFACE; | |||
@@ -1250,7 +1298,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
{ | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
const int refcount = ++view->refcounter; | |||
d_stdout("dpf_plugin_view::ref => %p | refcount %i", self, refcount); | |||
d_debug("dpf_plugin_view::ref => %p | refcount %i", self, refcount); | |||
return refcount; | |||
} | |||
@@ -1261,7 +1309,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
if (const int refcount = --view->refcounter) | |||
{ | |||
d_stdout("dpf_plugin_view::unref => %p | refcount %i", self, refcount); | |||
d_debug("dpf_plugin_view::unref => %p | refcount %i", self, refcount); | |||
return refcount; | |||
} | |||
@@ -1300,7 +1348,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
if (unclean) | |||
return 0; | |||
d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); | |||
d_debug("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); | |||
delete view; | |||
delete viewptr; | |||
@@ -1312,7 +1360,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API is_platform_type_supported(void* const self, const char* const platform_type) | |||
{ | |||
d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); | |||
d_debug("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); | |||
for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i) | |||
{ | |||
@@ -1321,11 +1369,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
} | |||
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) | |||
{ | |||
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); | |||
DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); | |||
@@ -1378,7 +1429,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API removed(void* const self) | |||
{ | |||
d_stdout("dpf_plugin_view::removed => %p", self); | |||
d_debug("dpf_plugin_view::removed => %p", self); | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); | |||
@@ -1413,7 +1464,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API on_wheel(void* const self, const float distance) | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); | |||
d_debug("dpf_plugin_view::on_wheel => %p %f", self, distance); | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
UIVst3* const uivst3 = view->uivst3; | |||
@@ -1430,7 +1481,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API on_key_down(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); | |||
d_debug("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
UIVst3* const uivst3 = view->uivst3; | |||
@@ -1447,7 +1498,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API on_key_up(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); | |||
d_debug("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
UIVst3* const uivst3 = view->uivst3; | |||
@@ -1463,21 +1514,43 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API get_size(void* const self, v3_view_rect* const rect) | |||
{ | |||
d_stdout("dpf_plugin_view::get_size => %p", self); | |||
d_debug("dpf_plugin_view::get_size => %p", self); | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
if (UIVst3* const uivst3 = view->uivst3) | |||
return uivst3->getSize(rect); | |||
d_stdout("dpf_plugin_view::get_size => %p | V3_NOT_INITIALIZED", self); | |||
std::memset(rect, 0, sizeof(v3_view_rect)); | |||
d_debug("dpf_plugin_view::get_size => %p | NOTE: size request before attach", self); | |||
view->sizeRequestedBeforeBeingAttached = true; | |||
return V3_NOT_INITIALIZED; | |||
double scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0; | |||
#if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) | |||
rect->right = DISTRHO_UI_DEFAULT_WIDTH; | |||
rect->bottom = DISTRHO_UI_DEFAULT_HEIGHT; | |||
if (d_isZero(scaleFactor)) | |||
scaleFactor = 1.0; | |||
#else | |||
UIExporter tmpUI(nullptr, 0, view->sampleRate, | |||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, | |||
view->instancePointer, scaleFactor); | |||
rect->right = tmpUI.getWidth(); | |||
rect->bottom = tmpUI.getHeight(); | |||
scaleFactor = tmpUI.getScaleFactor(); | |||
tmpUI.quit(); | |||
#endif | |||
rect->left = rect->top = 0; | |||
#ifdef DISTRHO_OS_MAC | |||
rect->right /= scaleFactor; | |||
rect->bottom /= scaleFactor; | |||
#endif | |||
return V3_OK; | |||
} | |||
static v3_result V3_API on_size(void* const self, v3_view_rect* const rect) | |||
{ | |||
d_stdout("dpf_plugin_view::on_size => %p {%d,%d,%d,%d}", | |||
d_debug("dpf_plugin_view::on_size => %p {%d,%d,%d,%d}", | |||
self, rect->top, rect->left, rect->right, rect->bottom); | |||
DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->right > rect->left, rect->right, rect->left, V3_INVALID_ARG); | |||
DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->bottom > rect->top, rect->bottom, rect->top, V3_INVALID_ARG); | |||
@@ -1495,7 +1568,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API on_focus(void* const self, const v3_bool state) | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); | |||
d_debug("dpf_plugin_view::on_focus => %p %u", self, state); | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
UIVst3* const uivst3 = view->uivst3; | |||
@@ -1511,7 +1584,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API set_frame(void* const self, v3_plugin_frame** const frame) | |||
{ | |||
d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame); | |||
d_debug("dpf_plugin_view::set_frame => %p %p", self, frame); | |||
dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |||
view->frame = frame; | |||
@@ -1541,7 +1614,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { | |||
static v3_result V3_API check_size_constraint(void* const self, v3_view_rect* const rect) | |||
{ | |||
d_stdout("dpf_plugin_view::check_size_constraint => %p {%d,%d,%d,%d}", | |||
d_debug("dpf_plugin_view::check_size_constraint => %p {%d,%d,%d,%d}", | |||
self, rect->top, rect->left, rect->right, rect->bottom); | |||
dpf_plugin_view* const view = *static_cast<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 { | |||
V3_SPEAKER_L = 1 << 0, | |||
V3_SPEAKER_R = 1 << 1, | |||
V3_SPEAKER_C = 1 << 2, | |||
V3_SPEAKER_LFE = 1 << 3, | |||
V3_SPEAKER_LS = 1 << 4, | |||
V3_SPEAKER_RS = 1 << 5, | |||
V3_SPEAKER_LC = 1 << 6, | |||
V3_SPEAKER_RC = 1 << 7, | |||
V3_SPEAKER_S = 1 << 8, | |||
V3_SPEAKER_SL = 1 << 9, | |||
V3_SPEAKER_SR = 1 << 10, | |||
V3_SPEAKER_TC = 1 << 11, | |||
V3_SPEAKER_TFL = 1 << 12, | |||
V3_SPEAKER_TFC = 1 << 13, | |||
V3_SPEAKER_TFR = 1 << 14, | |||
V3_SPEAKER_TRL = 1 << 15, | |||
V3_SPEAKER_TRC = 1 << 16, | |||
V3_SPEAKER_TRR = 1 << 17, | |||
V3_SPEAKER_LFE2 = 1 << 18, | |||
V3_SPEAKER_M = 1 << 19 | |||
}; | |||
@@ -44,7 +44,10 @@ cd .. | |||
DPF_UTILS_DIR=$(dirname ${0}) | |||
sed -e "s|@name@|${NAME}|" ${DPF_UTILS_DIR}/plugin.pkg/welcome.txt.in > build/welcome.txt | |||
# can be overridden by environment variables | |||
WELCOME_TXT=${WELCOME_TXT:=${DPF_UTILS_DIR}/plugin.pkg/welcome.txt.in} | |||
sed -e "s|@name@|${NAME}|" "${WELCOME_TXT}" > build/welcome.txt | |||
sed -e "s|@builddir@|${PWD}/build|" \ | |||
-e "s|@lv2bundleref@|dpf-${SNAME}-lv2bundles.pkg|" \ | |||
-e "s|@vst2bundleref@|dpf-${SNAME}-vst2bundles.pkg|" \ | |||
@@ -40,13 +40,13 @@ def res2c(namespace, filenames): | |||
for filename in filenames: | |||
shortFilename = filename.rsplit(os.sep, 1)[-1].split(".", 1)[0] | |||
shortFilename = shortFilename.replace("-", "_") | |||
shortFilename = shortFilename.replace("-", "_").replace("@","_") | |||
resData = open(filename, 'rb').read() | |||
print("Generating data for \"%s\"" % (filename)) | |||
fdH.write(" extern const char* %sData;\n" % shortFilename) | |||
fdH.write(" extern const unsigned char* %sData;\n" % shortFilename) | |||
fdH.write(" const unsigned int %sDataSize = %i;\n" % (shortFilename, len(resData))) | |||
if tempIndex != len(filenames): | |||
@@ -70,7 +70,7 @@ def res2c(namespace, filenames): | |||
curColumn += 1 | |||
fdC.write("};\n") | |||
fdC.write("const char* %s::%sData = (const char*)temp_%s_%i;\n" % (namespace, shortFilename, shortFilename, tempIndex)) | |||
fdC.write("const unsigned char* %s::%sData = (const unsigned char*)temp_%s_%i;\n" % (namespace, shortFilename, shortFilename, tempIndex)) | |||
if tempIndex != len(filenames): | |||
fdC.write("\n") | |||
@@ -89,25 +89,33 @@ def res2c(namespace, filenames): | |||
# ----------------------------------------------------- | |||
if __name__ == '__main__': | |||
if len(sys.argv) != 3: | |||
print("Usage: %s <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() | |||
namespace = sys.argv[1].replace("-","_") | |||
resFolder = sys.argv[2] | |||
outFolder = sys.argv[3] if len(sys.argv) == 4 else None | |||
if not os.path.exists(resFolder): | |||
print("Folder '%s' does not exist" % resFolder) | |||
quit() | |||
if outFolder is not None and not os.path.exists(outFolder): | |||
print("Output folder '%s' does not exist" % outFolder) | |||
quit() | |||
# find resource files | |||
resFiles = [] | |||
for root, dirs, files in os.walk(resFolder): | |||
for name in files: | |||
resFiles.append(os.path.join(root, name)) | |||
resFiles.append(os.path.abspath(os.path.join(root, name))) | |||
resFiles.sort() | |||
if outFolder is not None: | |||
os.chdir(outFolder) | |||
# create code now | |||
res2c(namespace, resFiles) |
@@ -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 | |||
Memcheck:Leak | |||
fun:calloc | |||
fun:allocate_dtv | |||
... | |||
fun:_dl_allocate_tls | |||
... | |||
} | |||
@@ -32,6 +31,12 @@ | |||
... | |||
fun:call_init.part.0 | |||
} | |||
{ | |||
libdl is really something else | |||
Memcheck:Addr8 | |||
... | |||
fun:dl_open_worker | |||
} | |||
{ | |||
ignore XInitThreads | |||
Memcheck:Leak | |||
@@ -46,3 +51,19 @@ | |||
fun:XrmGetStringDatabase | |||
... | |||
} | |||
{ | |||
ignore XPutImage | |||
Memcheck:Param | |||
writev(vector[...]) | |||
... | |||
fun:XPutImage | |||
... | |||
} | |||
{ | |||
ignore mesa stuff | |||
Memcheck:Value8 | |||
... | |||
obj:/usr/lib/x86_64-linux-gnu/dri/swrast_dri.so | |||
fun:start_thread | |||
fun:clone | |||
} |