diff --git a/dpf/Makefile.base.mk b/dpf/Makefile.base.mk index dfa64dd..5c30c32 100644 --- a/dpf/Makefile.base.mk +++ b/dpf/Makefile.base.mk @@ -29,22 +29,17 @@ ifneq ($(WINDOWS),true) ifneq (,$(findstring bsd,$(TARGET_MACHINE))) BSD=true -endif -ifneq (,$(findstring haiku,$(TARGET_MACHINE))) +else ifneq (,$(findstring haiku,$(TARGET_MACHINE))) HAIKU=true -endif -ifneq (,$(findstring linux,$(TARGET_MACHINE))) +else ifneq (,$(findstring linux,$(TARGET_MACHINE))) LINUX=true else ifneq (,$(findstring gnu,$(TARGET_MACHINE))) HURD=true -endif -ifneq (,$(findstring apple,$(TARGET_MACHINE))) +else ifneq (,$(findstring apple,$(TARGET_MACHINE))) MACOS=true -endif -ifneq (,$(findstring mingw,$(TARGET_MACHINE))) +else ifneq (,$(findstring mingw,$(TARGET_MACHINE))) WINDOWS=true -endif -ifneq (,$(findstring windows,$(TARGET_MACHINE))) +else ifneq (,$(findstring windows,$(TARGET_MACHINE))) WINDOWS=true endif diff --git a/dpf/Makefile.plugins.mk b/dpf/Makefile.plugins.mk index d057e28..a3e3d96 100644 --- a/dpf/Makefile.plugins.mk +++ b/dpf/Makefile.plugins.mk @@ -112,12 +112,29 @@ endif # Set plugin symbols to export ifeq ($(MACOS),true) -SYMBOLS_LADSPA = -Wl,-exported_symbol,_ladspa_descriptor -SYMBOLS_DSSI = -Wl,-exported_symbol,_ladspa_descriptor -Wl,-exported_symbol,_dssi_descriptor -SYMBOLS_LV2 = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl -SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor -SYMBOLS_VST2 = -Wl,-exported_symbol,_VSTPluginMain -SYMBOLS_VST3 = -Wl,-exported_symbol,_GetPluginFactory -Wl,-exported_symbol,_bundleEntry -Wl,-exported_symbol,_bundleExit +SYMBOLS_LADSPA = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/ladspa.exp +SYMBOLS_DSSI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/dssi.exp +SYMBOLS_LV2DSP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2-dsp.exp +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 +else ifeq ($(WINDOWS),true) +SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def +SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def +SYMBOLS_LV2DSP = $(DPF_PATH)/utils/symbols/lv2-dsp.def +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 +else +SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version +SYMBOLS_DSSI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/dssi.version +SYMBOLS_LV2DSP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2-dsp.version +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 endif # --------------------------------------------------------------------------------------------------------------------- @@ -337,12 +354,12 @@ $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) $(SYMBOLS_LV2UI) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) -o $@ $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin library for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LV2) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) diff --git a/dpf/cmake/DPF-plugin.cmake b/dpf/cmake/DPF-plugin.cmake index 0253dbc..18860e7 100644 --- a/dpf/cmake/DPF-plugin.cmake +++ b/dpf/cmake/DPF-plugin.cmake @@ -206,6 +206,7 @@ function(dpf__build_ladspa NAME) dpf__add_module("${NAME}-ladspa" ${_no_srcs}) dpf__add_plugin_main("${NAME}-ladspa" "ladspa") + dpf__set_module_export_list("${NAME}-ladspa" "ladspa") target_link_libraries("${NAME}-ladspa" PRIVATE "${NAME}-dsp") set_target_properties("${NAME}-ladspa" PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" @@ -234,6 +235,7 @@ function(dpf__build_dssi NAME DGL_LIBRARY) dpf__add_module("${NAME}-dssi" ${_no_srcs}) dpf__add_plugin_main("${NAME}-dssi" "dssi") + dpf__set_module_export_list("${NAME}-dssi" "dssi") target_link_libraries("${NAME}-dssi" PRIVATE "${NAME}-dsp") set_target_properties("${NAME}-dssi" PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" @@ -264,6 +266,11 @@ function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC) dpf__add_module("${NAME}-lv2" ${_no_srcs}) dpf__add_plugin_main("${NAME}-lv2" "lv2") + if(DGL_LIBRARY AND MONOLITHIC) + dpf__set_module_export_list("${NAME}-lv2" "lv2") + else() + dpf__set_module_export_list("${NAME}-lv2" "lv2-dsp") + endif() target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-dsp") set_target_properties("${NAME}-lv2" PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2/$<0:>" @@ -280,6 +287,7 @@ function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC) else() dpf__add_module("${NAME}-lv2-ui" ${_no_srcs}) dpf__add_ui_main("${NAME}-lv2-ui" "lv2" "${DGL_LIBRARY}") + dpf__set_module_export_list("${NAME}-lv2-ui" "lv2-ui") target_link_libraries("${NAME}-lv2-ui" PRIVATE "${NAME}-ui") set_target_properties("${NAME}-lv2-ui" PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2/$<0:>" @@ -311,6 +319,7 @@ function(dpf__build_vst2 NAME DGL_LIBRARY) dpf__add_module("${NAME}-vst2" ${_no_srcs}) dpf__add_plugin_main("${NAME}-vst2" "vst2") dpf__add_ui_main("${NAME}-vst2" "vst2" "${DGL_LIBRARY}") + dpf__set_module_export_list("${NAME}-vst2" "vst2") target_link_libraries("${NAME}-vst2" PRIVATE "${NAME}-dsp" "${NAME}-ui") set_target_properties("${NAME}-vst2" PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" @@ -566,6 +575,24 @@ function(dpf__add_static_library NAME) dpf__set_target_defaults("${NAME}") endfunction() +# dpf__set_module_export_list +# ------------------------------------------------------------------------------ +# +# Applies a list of exported symbols to the module target. +# +function(dpf__set_module_export_list NAME EXPORTS) + if(WIN32) + target_sources("${NAME}" PRIVATE "${DPF_ROOT_DIR}/utils/symbols/${EXPORTS}.def") + elseif(APPLE) + set_property(TARGET "${NAME}" APPEND PROPERTY LINK_OPTIONS + "-Xlinker" "-exported_symbols_list" + "-Xlinker" "${DPF_ROOT_DIR}/utils/symbols/${EXPORTS}.exp") + else() + set_property(TARGET "${NAME}" APPEND PROPERTY LINK_OPTIONS + "-Xlinker" "--version-script=${DPF_ROOT_DIR}/utils/symbols/${EXPORTS}.version") + endif() +endfunction() + # dpf__set_target_defaults # ------------------------------------------------------------------------------ # diff --git a/dpf/dgl/NanoVG.hpp b/dpf/dgl/NanoVG.hpp index e1462a5..08b8f50 100644 --- a/dpf/dgl/NanoVG.hpp +++ b/dpf/dgl/NanoVG.hpp @@ -582,12 +582,26 @@ public: NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, int imageFlags); /** - Creates image from specified image data. + Creates image from specified raw format image data. + */ + NanoImage::Handle createImageFromRawMemory(uint w, uint h, const uchar* data, + ImageFlags imageFlags, ImageFormat format); + + /** + Creates image from specified raw format image data. + Overloaded function for convenience. + @see ImageFlags + */ + NanoImage::Handle createImageFromRawMemory(uint w, uint h, const uchar* data, + int imageFlags, ImageFormat format); + + /** + Creates image from specified RGBA image data. */ NanoImage::Handle createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags); /** - Creates image from specified image data. + Creates image from specified RGBA image data. Overloaded function for convenience. @see ImageFlags */ diff --git a/dpf/dgl/Window.hpp b/dpf/dgl/Window.hpp index 744f7f2..9effa69 100644 --- a/dpf/dgl/Window.hpp +++ b/dpf/dgl/Window.hpp @@ -382,6 +382,13 @@ public: */ void repaint(const Rectangle& rect) noexcept; + /** + Render this window's content into a picture file, specified by @a filename. + Window must be visible and on screen. + Written picture format is PPM. + */ + void renderToPicture(const char* filename); + /** Run this window as a modal, blocking input events from the parent. Only valid for windows that have been created with another window as parent (as passed in the constructor). diff --git a/dpf/dgl/src/Cairo.cpp b/dpf/dgl/src/Cairo.cpp index ebefaa5..6cd7f20 100644 --- a/dpf/dgl/src/Cairo.cpp +++ b/dpf/dgl/src/Cairo.cpp @@ -808,6 +808,13 @@ void TopLevelWidget::PrivateData::display() // ----------------------------------------------------------------------- +void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) +{ + notImplemented("Window::PrivateData::renderToPicture"); +} + +// ----------------------------------------------------------------------- + const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { GraphicsContext& context((GraphicsContext&)graphicsContext); diff --git a/dpf/dgl/src/NanoVG.cpp b/dpf/dgl/src/NanoVG.cpp index 7b0cf0c..3ad9032 100644 --- a/dpf/dgl/src/NanoVG.cpp +++ b/dpf/dgl/src/NanoVG.cpp @@ -215,6 +215,7 @@ NanoImage& NanoImage::operator=(const Handle& handle) fHandle.context = handle.context; fHandle.imageId = handle.imageId; + _updateSize(); return *this; } @@ -631,6 +632,45 @@ NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, int return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast(dataSize))); } +NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, + ImageFlags imageFlags, ImageFormat format) +{ + return createImageFromRawMemory(w, h, data, static_cast(imageFlags), format); +} + +NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data, + int imageFlags, ImageFormat format) +{ + if (fContext == nullptr) return NanoImage::Handle(); + DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle()); + + NVGtexture nvgformat; + switch (format) + { + case kImageFormatGrayscale: + nvgformat = NVG_TEXTURE_ALPHA; + break; + case kImageFormatBGR: + nvgformat = NVG_TEXTURE_BGR; + break; + case kImageFormatBGRA: + nvgformat = NVG_TEXTURE_BGRA; + break; + case kImageFormatRGB: + nvgformat = NVG_TEXTURE_RGB; + break; + case kImageFormatRGBA: + nvgformat = NVG_TEXTURE_RGBA; + break; + default: + return NanoImage::Handle(); + } + + return NanoImage::Handle(fContext, nvgCreateImageRaw(fContext, + static_cast(w), + static_cast(h), imageFlags, nvgformat, data)); +} + NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, ImageFlags imageFlags) { return createImageFromRGBA(w, h, data, static_cast(imageFlags)); @@ -646,12 +686,14 @@ NanoImage::Handle NanoVG::createImageFromRGBA(uint w, uint h, const uchar* data, static_cast(h), imageFlags, data)); } -NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, ImageFlags imageFlags, bool deleteTexture) +NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, + ImageFlags imageFlags, bool deleteTexture) { return createImageFromTextureHandle(textureId, w, h, static_cast(imageFlags), deleteTexture); } -NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, int imageFlags, bool deleteTexture) +NanoImage::Handle NanoVG::createImageFromTextureHandle(GLuint textureId, uint w, uint h, + int imageFlags, bool deleteTexture) { if (fContext == nullptr) return NanoImage::Handle(); DISTRHO_SAFE_ASSERT_RETURN(textureId != 0, NanoImage::Handle()); diff --git a/dpf/dgl/src/OpenGL.cpp b/dpf/dgl/src/OpenGL.cpp index 098c33e..aa2c396 100644 --- a/dpf/dgl/src/OpenGL.cpp +++ b/dpf/dgl/src/OpenGL.cpp @@ -667,6 +667,36 @@ void TopLevelWidget::PrivateData::display() // ----------------------------------------------------------------------- +void Window::PrivateData::renderToPicture(const char* const filename, + const GraphicsContext&, + const uint width, + const uint height) +{ + FILE* const f = fopen(filename, "w"); + DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,); + + GLubyte* const pixels = new GLubyte[width * height * 3 * sizeof(GLubyte)]; + + glFlush(); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + fprintf(f, "P3\n%d %d\n255\n", width, height); + for (uint y = 0; y < height; y++) + { + for (uint i, x = 0; x < width; x++) + { + i = 3 * ((height - y - 1) * width + x); + fprintf(f, "%3d %3d %3d ", pixels[i], pixels[i+1], pixels[i+2]); + } + fprintf(f, "\n"); + } + + delete[] pixels; + fclose(f); +} + +// ----------------------------------------------------------------------- + const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { return (const GraphicsContext&)graphicsContext; diff --git a/dpf/dgl/src/Vulkan.cpp b/dpf/dgl/src/Vulkan.cpp index d18b3c3..2fc9758 100644 --- a/dpf/dgl/src/Vulkan.cpp +++ b/dpf/dgl/src/Vulkan.cpp @@ -231,6 +231,13 @@ void TopLevelWidget::PrivateData::display() // ----------------------------------------------------------------------- +void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) +{ + notImplemented("Window::PrivateData::renderToPicture"); +} + +// ----------------------------------------------------------------------- + const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { return (const GraphicsContext&)graphicsContext; diff --git a/dpf/dgl/src/Window.cpp b/dpf/dgl/src/Window.cpp index b418fda..7d27613 100644 --- a/dpf/dgl/src/Window.cpp +++ b/dpf/dgl/src/Window.cpp @@ -336,6 +336,11 @@ void Window::repaint(const Rectangle& rect) noexcept puglPostRedisplayRect(pData->view, prect); } +void Window::renderToPicture(const char* const filename) +{ + pData->filenameToRenderInto = strdup(filename); +} + void Window::runAsModal(bool blockWait) { pData->runAsModal(blockWait); diff --git a/dpf/dgl/src/WindowPrivateData.cpp b/dpf/dgl/src/WindowPrivateData.cpp index deaa559..ce0dce3 100644 --- a/dpf/dgl/src/WindowPrivateData.cpp +++ b/dpf/dgl/src/WindowPrivateData.cpp @@ -95,6 +95,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -120,6 +121,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -149,6 +151,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -180,6 +183,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -195,6 +199,7 @@ Window::PrivateData::~PrivateData() { appData->idleCallbacks.remove(this); appData->windows.remove(self); + std::free(filenameToRenderInto); if (view == nullptr) return; @@ -743,6 +748,14 @@ void Window::PrivateData::onPuglExpose() if (widget->isVisible()) widget->pData->display(); } + + if (char* const filename = filenameToRenderInto) + { + const PuglRect rect = puglGetFrame(view); + filenameToRenderInto = nullptr; + renderToPicture(filename, getGraphicsContext(), static_cast(rect.width), static_cast(rect.height)); + std::free(filename); + } #endif } diff --git a/dpf/dgl/src/WindowPrivateData.hpp b/dpf/dgl/src/WindowPrivateData.hpp index 6687afc..5f9f898 100644 --- a/dpf/dgl/src/WindowPrivateData.hpp +++ b/dpf/dgl/src/WindowPrivateData.hpp @@ -77,6 +77,9 @@ struct Window::PrivateData : IdleCallback { /** Whether to ignore idle callback requests, useful for temporary windows. */ bool ignoreIdleCallbacks; + /** Render to a picture file when non-null, automatically free+unset after saving. */ + char* filenameToRenderInto; + #ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ const char* win32SelectedFile; @@ -162,6 +165,8 @@ struct Window::PrivateData : IdleCallback { # endif #endif + static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); + // modal handling void startModal(); void stopModal(); diff --git a/dpf/dgl/src/nanovg/nanovg.c b/dpf/dgl/src/nanovg/nanovg.c index aa86354..209c34f 100644 --- a/dpf/dgl/src/nanovg/nanovg.c +++ b/dpf/dgl/src/nanovg/nanovg.c @@ -817,9 +817,14 @@ int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int return image; } +int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, NVGtexture format, const unsigned char* data) +{ + return ctx->params.renderCreateTexture(ctx->params.userPtr, format, w, h, imageFlags, data); +} + int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data) { - return ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_RGBA, w, h, imageFlags, data); + return nvgCreateImageRaw(ctx, w, h, imageFlags, NVG_TEXTURE_RGBA, data); } void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data) diff --git a/dpf/dgl/src/nanovg/nanovg.h b/dpf/dgl/src/nanovg/nanovg.h index 032575f..a2abfe9 100644 --- a/dpf/dgl/src/nanovg/nanovg.h +++ b/dpf/dgl/src/nanovg/nanovg.h @@ -144,6 +144,14 @@ enum NVGimageFlags { NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear }; +enum NVGtexture { + NVG_TEXTURE_ALPHA, + NVG_TEXTURE_BGR, + NVG_TEXTURE_BGRA, + NVG_TEXTURE_RGB, + NVG_TEXTURE_RGBA, +}; + // Begin drawing a new frame // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() // nvgBeginFrame() defines the size of the window to render to in relation currently @@ -375,6 +383,10 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); // Returns handle to the image. int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); +// Creates image from specified image data and texture format. +// Returns handle to the image. +int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, NVGtexture format, const unsigned char* data); + // Creates image from specified image data. // Returns handle to the image. int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); @@ -627,11 +639,6 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa // // Internal Render API // -enum NVGtexture { - NVG_TEXTURE_ALPHA = 0x01, - NVG_TEXTURE_RGBA = 0x02, -}; - struct NVGscissor { float xform[6]; float extent[2]; diff --git a/dpf/dgl/src/nanovg/nanovg_gl.h b/dpf/dgl/src/nanovg/nanovg_gl.h index b69fc30..fd4b416 100644 --- a/dpf/dgl/src/nanovg/nanovg_gl.h +++ b/dpf/dgl/src/nanovg/nanovg_gl.h @@ -761,9 +761,21 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im } #endif - if (type == NVG_TEXTURE_RGBA) + switch (type) + { + case NVG_TEXTURE_BGR: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data); + break; + case NVG_TEXTURE_BGRA: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); + break; + case NVG_TEXTURE_RGB: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + break; + case NVG_TEXTURE_RGBA: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - else + break; + default: #if defined(NANOVG_GLES2) || defined (NANOVG_GL2) glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); #elif defined(NANOVG_GLES3) @@ -771,6 +783,8 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); #endif + break; + } if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { if (imageFlags & NVG_IMAGE_NEAREST) { @@ -845,22 +859,50 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w glPixelStorei(GL_UNPACK_SKIP_ROWS, y); #else // No support for all of skip, need to update a whole row at a time. - if (tex->type == NVG_TEXTURE_RGBA) + switch (tex->type) + { + case NVG_TEXTURE_BGR: + data += y*tex->width*3; + break; + case NVG_TEXTURE_BGRA: data += y*tex->width*4; - else + break; + case NVG_TEXTURE_RGB: + data += y*tex->width*3; + break; + case NVG_TEXTURE_RGBA: + data += y*tex->width*4; + break; + default: data += y*tex->width; + break; + } x = 0; w = tex->width; #endif - if (tex->type == NVG_TEXTURE_RGBA) + switch (tex->type) + { + case NVG_TEXTURE_BGR: + glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data); + break; + case NVG_TEXTURE_BGRA: + glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data); + break; + case NVG_TEXTURE_RGB: + glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data); + break; + case NVG_TEXTURE_RGBA: glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); - else + break; + default: #if defined(NANOVG_GLES2) || defined(NANOVG_GL2) glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); #else glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); #endif + break; + } glPixelStorei(GL_UNPACK_ALIGNMENT, 4); #ifndef NANOVG_GLES2 @@ -956,15 +998,31 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai frag->type = NSVG_SHADER_FILLIMG; #if NANOVG_GL_USE_UNIFORMBUFFER - if (tex->type == NVG_TEXTURE_RGBA) + switch (tex->type) + { + case NVG_TEXTURE_BGR: + case NVG_TEXTURE_BGRA: + case NVG_TEXTURE_RGB: + case NVG_TEXTURE_RGBA: frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; - else + break; + default: frag->texType = 2; + break; + } #else - if (tex->type == NVG_TEXTURE_RGBA) + switch (tex->type) + { + case NVG_TEXTURE_BGR: + case NVG_TEXTURE_BGRA: + case NVG_TEXTURE_RGB: + case NVG_TEXTURE_RGBA: frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; - else + break; + default: frag->texType = 2.0f; + break; + } #endif // printf("frag->texType = %d\n", frag->texType); } else { diff --git a/dpf/distrho/DistrhoInfo.hpp b/dpf/distrho/DistrhoInfo.hpp index 0ce83ec..c70752e 100644 --- a/dpf/distrho/DistrhoInfo.hpp +++ b/dpf/distrho/DistrhoInfo.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this diff --git a/dpf/distrho/DistrhoPluginMain.cpp b/dpf/distrho/DistrhoPluginMain.cpp index 10e54e3..ab6f0ef 100644 --- a/dpf/distrho/DistrhoPluginMain.cpp +++ b/dpf/distrho/DistrhoPluginMain.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this diff --git a/dpf/distrho/DistrhoPluginUtils.hpp b/dpf/distrho/DistrhoPluginUtils.hpp index d381ca6..47eb0e6 100644 --- a/dpf/distrho/DistrhoPluginUtils.hpp +++ b/dpf/distrho/DistrhoPluginUtils.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -44,11 +44,11 @@ START_NAMESPACE_DISTRHO Some important notes when using this class: 1. MidiEvent::frame retains its original value, but it is useless, do not use it. - 2. The class variables names are be the same as the default ones in the run function. + 2. The class variable names are the same as the default ones in the run function. Keep that in mind and try to avoid typos. :) */ -class AudioMidiSyncHelper { -public: +struct AudioMidiSyncHelper +{ /** Parameters from the run function, adjusted for event sync */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; uint32_t frames; diff --git a/dpf/distrho/extra/String.hpp b/dpf/distrho/extra/String.hpp index fe3d993..04a959b 100644 --- a/dpf/distrho/extra/String.hpp +++ b/dpf/distrho/extra/String.hpp @@ -618,6 +618,36 @@ public: return *this; } + /* + * Create a new string where all non-basic characters are converted to '_'. + * @see toBasic() + */ + String asBasic() const noexcept + { + String s(*this); + return s.toBasic(); + } + + /* + * Create a new string where all ascii characters are converted lowercase. + * @see toLower() + */ + String asLower() const noexcept + { + String s(*this); + return s.toLower(); + } + + /* + * Create a new string where all ascii characters are converted to uppercase. + * @see toUpper() + */ + String asUpper() const noexcept + { + String s(*this); + return s.toUpper(); + } + /* * Direct access to the string buffer (read-only). */ diff --git a/dpf/distrho/src/DistrhoPluginChecks.h b/dpf/distrho/src/DistrhoPluginChecks.h index 7d7883f..910089d 100644 --- a/dpf/distrho/src/DistrhoPluginChecks.h +++ b/dpf/distrho/src/DistrhoPluginChecks.h @@ -146,12 +146,11 @@ // ----------------------------------------------------------------------- // Enable full state if plugin exports presets -// FIXME -// #if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE -// # warning Plugins with programs and state need to implement full state API -// # undef DISTRHO_PLUGIN_WANT_FULL_STATE -// # define DISTRHO_PLUGIN_WANT_FULL_STATE 1 -// #endif +#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE +# warning Plugins with programs and state need to implement full state API too +# undef DISTRHO_PLUGIN_WANT_FULL_STATE +# define DISTRHO_PLUGIN_WANT_FULL_STATE 1 +#endif // ----------------------------------------------------------------------- // Disable UI if DGL or External UI is not available diff --git a/dpf/distrho/src/DistrhoPluginLV2export.cpp b/dpf/distrho/src/DistrhoPluginLV2export.cpp index b529627..1456696 100644 --- a/dpf/distrho/src/DistrhoPluginLV2export.cpp +++ b/dpf/distrho/src/DistrhoPluginLV2export.cpp @@ -332,6 +332,7 @@ void lv2_generate_ttl(const char* const basename) pluginString += "@prefix doap: .\n"; pluginString += "@prefix foaf: .\n"; pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; + pluginString += "@prefix midi: <" LV2_MIDI_PREFIX "> .\n"; pluginString += "@prefix mod: .\n"; pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; pluginString += "@prefix pg: <" LV2_PORT_GROUPS_PREFIX "> .\n"; @@ -341,6 +342,7 @@ void lv2_generate_ttl(const char* const basename) #if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; #endif + pluginString += "@prefix spdx: .\n"; #if DISTRHO_PLUGIN_HAS_UI pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; #endif @@ -368,11 +370,11 @@ void lv2_generate_ttl(const char* const basename) // plugin pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; #ifdef DISTRHO_PLUGIN_LV2_CATEGORY - pluginString += " a " DISTRHO_PLUGIN_LV2_CATEGORY ", lv2:Plugin ;\n"; + pluginString += " a " DISTRHO_PLUGIN_LV2_CATEGORY ", lv2:Plugin, doap:Project ;\n"; #elif DISTRHO_PLUGIN_IS_SYNTH - pluginString += " a lv2:InstrumentPlugin, lv2:Plugin ;\n"; + pluginString += " a lv2:InstrumentPlugin, lv2:Plugin, doap:Project ;\n"; #else - pluginString += " a lv2:Plugin ;\n"; + pluginString += " a lv2:Plugin, doap:Project ;\n"; #endif pluginString += "\n"; @@ -594,10 +596,13 @@ void lv2_generate_ttl(const char* const basename) pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; pluginString += " atom:bufferType atom:Sequence ;\n"; # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) - pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; + pluginString += " atom:supports atom:String ;\n"; # endif # if DISTRHO_PLUGIN_WANT_MIDI_INPUT - pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; + pluginString += " atom:supports midi:MidiEvent ;\n"; +# endif +# if DISTRHO_PLUGIN_WANT_STATEFILES + pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; # endif # if DISTRHO_PLUGIN_WANT_TIMEPOS pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; @@ -615,10 +620,13 @@ void lv2_generate_ttl(const char* const basename) pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; pluginString += " atom:bufferType atom:Sequence ;\n"; # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) - pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; + pluginString += " atom:supports atom:String ;\n"; # endif # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; + pluginString += " atom:supports midi:MidiEvent ;\n"; +# endif +# if DISTRHO_PLUGIN_WANT_STATEFILES + pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; # endif pluginString += " ] ;\n\n"; ++portIndex; @@ -746,12 +754,22 @@ void lv2_generate_ttl(const char* const basename) } if (j+1 == enumValues.count) - pluginString += " ] ;\n\n"; + pluginString += " ] ;\n"; else pluginString += " ] ,\n"; } } + // MIDI CC binding + if (const uint8_t midiCC = plugin.getParameterMidiCC(i)) + { + char midiCCBuf[7]; + snprintf(midiCCBuf, sizeof(midiCCBuf), "B0%02x00", midiCC); + pluginString += " midi:binding \""; + pluginString += midiCCBuf; + pluginString += "\"^^midi:MidiEvent ;\n"; + } + // unit const String& unit(plugin.getParameterUnit(i)); @@ -814,7 +832,7 @@ void lv2_generate_ttl(const char* const basename) } // hints - const uint32_t hints(plugin.getParameterHints(i)); + const uint32_t hints = plugin.getParameterHints(i); if (hints & kParameterIsBoolean) { @@ -832,8 +850,6 @@ void lv2_generate_ttl(const char* const basename) pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; } - // TODO midiCC - // group const uint32_t groupId = plugin.getParameterGroupId(i); @@ -895,13 +911,154 @@ void lv2_generate_ttl(const char* const basename) { const String license(plugin.getLicense()); - // TODO always convert to URL, do best-guess based on known licenses + // Using URL as license if (license.contains("://")) + { pluginString += " doap:license <" + license + "> ;\n\n"; + } + // String contaning quotes, use as-is else if (license.contains('"')) + { pluginString += " doap:license \"\"\"" + license + "\"\"\" ;\n\n"; + } + // Regular license string, convert to URL as much as we can else - pluginString += " doap:license \"" + license + "\" ;\n\n"; + { + const String uplicense(license.asUpper()); + + // for reference, see https://spdx.org/licenses/ + + // common licenses + /**/ if (uplicense == "AGPL-1.0-ONLY" || + uplicense == "AGPL1" || + uplicense == "AGPLV1") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "AGPL-1.0-OR-LATER" || + uplicense == "AGPL1+" || + uplicense == "AGPLV1+") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "AGPL-3.0-ONLY" || + uplicense == "AGPL3" || + uplicense == "AGPLV3") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "AGPL-3.0-OR-LATER" || + uplicense == "AGPL3+" || + uplicense == "AGPLV3+") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "APACHE-2.0" || + uplicense == "APACHE2" || + uplicense == "APACHE-2") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "BSD-2-CLAUSE" || + uplicense == "BSD2" || + uplicense == "BSD-2") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "BSD-3-CLAUSE" || + uplicense == "BSD3" || + uplicense == "BSD-3") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "GPL-2.0-ONLY" || + uplicense == "GPL2" || + uplicense == "GPLV2") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "GPL-2.0-OR-LATER" || + uplicense == "GPL2+" || + uplicense == "GPLV2+" || + uplicense == "GPL V2+") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "GPL-3.0-ONLY" || + uplicense == "GPL3" || + uplicense == "GPLV3") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "GPL-3.0-OR-LATER" || + uplicense == "GPL3+" || + uplicense == "GPLV3+" || + uplicense == "GPL V3+") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "ISC") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "LGPL-2.0-ONLY" || + uplicense == "LGPL2" || + uplicense == "LGPLV2") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "LGPL-2.0-OR-LATER" || + uplicense == "LGPL2+" || + uplicense == "LGPLV2+") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "LGPL-2.1-ONLY" || + uplicense == "LGPL2.1" || + uplicense == "LGPLV2.1") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "LGPL-2.1-OR-LATER" || + uplicense == "LGPL2.1+" || + uplicense == "LGPLV2.1+") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "LGPL-3.0-ONLY" || + uplicense == "LGPL3" || + uplicense == "LGPLV3") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "LGPL-3.0-OR-LATER" || + uplicense == "LGPL3+" || + uplicense == "LGPLV3+") + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense == "MIT") + { + pluginString += " doap:license ;\n\n"; + } + + // generic fallbacks + else if (uplicense.startsWith("GPL")) + { + pluginString += " doap:license ;\n\n"; + } + else if (uplicense.startsWith("LGPL")) + { + pluginString += " doap:license ;\n\n"; + } + + // unknown or not handled yet, log a warning + else + { + d_stderr("Unknown license string '%s'", license.buffer()); + pluginString += " doap:license \"" + license + "\" ;\n\n"; + } + } } // developer @@ -1037,7 +1194,10 @@ void lv2_generate_ttl(const char* const basename) presetsString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; presetsString += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n"; # if DISTRHO_PLUGIN_WANT_STATE + presetsString += "@prefix owl: .\n"; + presetsString += "@prefix rdfs: .\n"; presetsString += "@prefix state: <" LV2_STATE_PREFIX "> .\n"; + presetsString += "@prefix xsd: .\n"; # endif presetsString += "\n"; @@ -1045,8 +1205,13 @@ void lv2_generate_ttl(const char* const basename) const uint32_t numPrograms = plugin.getProgramCount(); # if DISTRHO_PLUGIN_WANT_FULL_STATE const uint32_t numStates = plugin.getStateCount(); + const bool valid = numParameters != 0 || numStates != 0; +# else + const bool valid = numParameters != 0; # endif + DISTRHO_CUSTOM_SAFE_ASSERT_RETURN("Programs require parameters or full state", valid, presetsFile.close()); + const String presetSeparator(std::strstr(DISTRHO_PLUGIN_URI, "#") != nullptr ? ":" : "#"); char strBuf[0xff+1]; @@ -1054,6 +1219,22 @@ void lv2_generate_ttl(const char* const basename) String presetString; +# if DISTRHO_PLUGIN_WANT_FULL_STATE + for (uint32_t i=0; i\n"; + presetString += " a owl:DatatypeProperty ;\n"; + presetString += " rdfs:label \"Plugin state key-value string pair\" ;\n"; + presetString += " rdfs:domain state:State ;\n"; + presetString += " rdfs:range xsd:string .\n\n"; + presetsString += presetString; + } +# endif + for (uint32_t i=0; i\n"; -# if DISTRHO_PLUGIN_WANT_FULL_STATE - if (numParameters == 0 && numStates == 0) -#else - if (numParameters == 0) -#endif - { - presetString += " ."; - presetsString += presetString; - continue; - } - # if DISTRHO_PLUGIN_WANT_FULL_STATE presetString += " state:state [\n"; for (uint32_t j=0; j #include #include +#include #if VESTIGE_HEADER # include "vestige/vestige.h" @@ -1635,6 +1636,20 @@ static void vst_processReplacingCallback(AEffect* effect, float** inputs, float* #undef validPlugin #undef vstObjectPtr +static struct Cleanup { + std::vector effects; + + ~Cleanup() + { + for (std::vector::iterator it = effects.begin(), end = effects.end(); it != end; ++it) + { + AEffect* const effect = *it; + delete (VstObject*)effect->object; + delete effect; + } + } +} sCleanup; + // ----------------------------------------------------------------------- END_NAMESPACE_DISTRHO @@ -1714,12 +1729,13 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) effect->processReplacing = vst_processReplacingCallback; // pointers - VstObject* const obj(new VstObject()); + VstObject* const obj = new VstObject(); obj->audioMaster = audioMaster; obj->plugin = nullptr; // done effect->object = obj; + sCleanup.effects.push_back(effect); return effect; } diff --git a/dpf/tests/Makefile b/dpf/tests/Makefile index c154b11..9b652ea 100644 --- a/dpf/tests/Makefile +++ b/dpf/tests/Makefile @@ -31,6 +31,7 @@ endif ifeq ($(HAVE_OPENGL),true) MANUAL_TESTS += Demo.opengl MANUAL_TESTS += FileBrowserDialog +MANUAL_TESTS += NanoImage MANUAL_TESTS += NanoSubWidgets UNIT_TESTS += Window.opengl endif @@ -140,11 +141,15 @@ clean: $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(VULKAN_LIBS) -o $@ ../build/tests/FileBrowserDialog$(APP_EXT): ../build/tests/FileBrowserDialog.cpp.o ../build/libdgl-opengl.a - @echo "Linking Demo (OpenGL)" + @echo "Linking FileBrowserDialog (OpenGL)" + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ + +../build/tests/NanoImage$(APP_EXT): ../build/tests/NanoImage.cpp.o ../build/libdgl-opengl.a + @echo "Linking NanoImage (OpenGL)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ ../build/tests/NanoSubWidgets$(APP_EXT): ../build/tests/NanoSubWidgets.cpp.o ../build/libdgl-opengl.a - @echo "Linking Demo (OpenGL)" + @echo "Linking NanoSubWidgets (OpenGL)" $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(DGL_SYSTEM_LIBS) $(OPENGL_LIBS) -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/dpf/tests/NanoImage.cpp b/dpf/tests/NanoImage.cpp new file mode 100644 index 0000000..5140bfd --- /dev/null +++ b/dpf/tests/NanoImage.cpp @@ -0,0 +1,229 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "tests.hpp" + +#include "dgl/NanoVG.hpp" + +START_NAMESPACE_DGL + +// -------------------------------------------------------------------------------------------------------------------- +// Images + +#include "images_res/CatPics.cpp" + +// -------------------------------------------------------------------------------------------------------------------- + +class NanoImageExample : public NanoStandaloneWindow, + public IdleCallback +{ + static const int kImg1y = 0; + static const int kImg2y = 500/2-CatPics::cat2Height/2; + static const int kImg3x = 400/3-CatPics::cat3Width/3; + + static const int kImg1max = 500-CatPics::cat1Width; + static const int kImg2max = 500-CatPics::cat2Width; + static const int kImg3max = 400-CatPics::cat3Height; + + static const int kImgFlags = IMAGE_GENERATE_MIPMAPS | IMAGE_REPEAT_X; + + int imgTop1st, imgTop2nd, imgTop3rd; + int img1x, img2x, img3y; + bool img1rev, img2rev, img3rev; + NanoImage img1, img2, img3; + +public: + NanoImageExample(Application& app) + : NanoStandaloneWindow(app), + imgTop1st(1), + imgTop2nd(2), + imgTop3rd(3), + img1x(0), + img2x(kImg2max), + img3y(kImg3max), + img1rev(false), + img2rev(true), + img3rev(true), + img1(createImageFromRawMemory(CatPics::cat1Width, CatPics::cat1Height, (uchar*)CatPics::cat1Data, kImgFlags, kImageFormatBGR)), + img2(createImageFromRawMemory(CatPics::cat2Width, CatPics::cat2Height, (uchar*)CatPics::cat2Data, kImgFlags, kImageFormatBGR)), + img3(createImageFromRawMemory(CatPics::cat3Width, CatPics::cat3Height, (uchar*)CatPics::cat3Data, kImgFlags, kImageFormatBGR)) + { + DISTRHO_SAFE_ASSERT(img1.isValid()); + DISTRHO_SAFE_ASSERT(img2.isValid()); + DISTRHO_SAFE_ASSERT(img3.isValid()); + + DISTRHO_SAFE_ASSERT_UINT2(img1.getSize().getWidth() == CatPics::cat1Width, + img1.getSize().getWidth(), CatPics::cat1Width); + + DISTRHO_SAFE_ASSERT_UINT2(img1.getSize().getHeight() == CatPics::cat1Height, + img1.getSize().getHeight(), CatPics::cat1Height); + + DISTRHO_SAFE_ASSERT_UINT2(img2.getSize().getWidth() == CatPics::cat2Width, + img2.getSize().getWidth(), CatPics::cat2Width); + + DISTRHO_SAFE_ASSERT_UINT2(img2.getSize().getHeight() == CatPics::cat2Height, + img2.getSize().getHeight(), CatPics::cat2Height); + + DISTRHO_SAFE_ASSERT_UINT2(img3.getSize().getWidth() == CatPics::cat3Width, + img3.getSize().getWidth(), CatPics::cat3Width); + + DISTRHO_SAFE_ASSERT_UINT2(img3.getSize().getHeight() == CatPics::cat3Height, + img3.getSize().getHeight(), CatPics::cat3Height); + + setResizable(true); + setSize(500, 500); + // setGeometryConstraints(500, 500, false); + setTitle("NanoImage"); + done(); + + addIdleCallback(this); + } + +protected: + void onNanoDisplay() override + { + // bottom image + beginPath(); + fillPaint(setupImagePaint(imgTop3rd)); + fill(); + + // middle image + beginPath(); + fillPaint(setupImagePaint(imgTop2nd)); + fill(); + + // top image + beginPath(); + fillPaint(setupImagePaint(imgTop1st)); + fill(); + } + + void idleCallback() noexcept override + { + if (img1rev) + { + img1x -= 2; + if (img1x <= -50) + { + img1rev = false; + setNewTopImg(1); + } + } + else + { + img1x += 2; + if (img1x >= kImg1max+50) + { + img1rev = true; + setNewTopImg(1); + } + } + + if (img2rev) + { + img2x -= 1; + if (img2x <= -50) + { + img2rev = false; + setNewTopImg(2); + } + } + else + { + img2x += 4; + if (img2x >= kImg2max+50) + { + img2rev = true; + setNewTopImg(2); + } + } + + if (img3rev) + { + img3y -= 3; + if (img3y <= -50) + { + img3rev = false; + setNewTopImg(3); + } + } + else + { + img3y += 3; + if (img3y >= kImg3max+50) + { + img3rev = true; + setNewTopImg(3); + } + } + + repaint(); + } + +private: + Paint setupImagePaint(const int imgId) noexcept + { + switch (imgId) + { + case 1: + rect(img1x, kImg1y, CatPics::cat1Width, CatPics::cat1Height); + return imagePattern(img1x, kImg1y, CatPics::cat1Width, CatPics::cat1Height, 0, img1, 1.0f); + case 2: + rect(img2x, kImg2y, CatPics::cat2Width, CatPics::cat2Height); + return imagePattern(img2x, kImg2y, CatPics::cat2Width, CatPics::cat2Height, 0, img2, 1.0f); + case 3: + rect(kImg3x, img3y, CatPics::cat3Width, CatPics::cat3Height); + return imagePattern(kImg3x, img3y, CatPics::cat3Width, CatPics::cat3Height, 0, img3, 1.0f); + }; + + return Paint(); + } + + void setNewTopImg(const int imgId) noexcept + { + if (imgTop1st == imgId) + return; + + if (imgTop2nd == imgId) + { + imgTop2nd = imgTop1st; + imgTop1st = imgId; + return; + } + + imgTop3rd = imgTop2nd; + imgTop2nd = imgTop1st; + imgTop1st = imgId; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DGL + +int main() +{ + USE_NAMESPACE_DGL; + + Application app(true); + NanoImageExample win(app); + win.show(); + app.exec(); + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/dpf/utils/lv2-ttl-generator/GNUmakefile b/dpf/utils/lv2-ttl-generator/GNUmakefile index 460b04f..9a0bace 100644 --- a/dpf/utils/lv2-ttl-generator/GNUmakefile +++ b/dpf/utils/lv2-ttl-generator/GNUmakefile @@ -9,19 +9,19 @@ ifeq ($(WINDOWS),true) build: ../lv2_ttl_generator.exe ../lv2_ttl_generator.exe: lv2_ttl_generator.c - $(CC) $< $(CFLAGS) -o $@ $(LDFLAGS) -static + $(CC) $< $(BUILD_C_FLAGS) -o $@ $(LINK_FLAGS) -static touch ../lv2_ttl_generator else # WINDOWS -ifneq ($(HAIKU),true) -LDFLAGS += -ldl +ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +LINK_FLAGS += -ldl endif build: ../lv2_ttl_generator ../lv2_ttl_generator: lv2_ttl_generator.c - $(CC) $< $(CFLAGS) -o $@ $(LDFLAGS) + $(CC) $< $(BUILD_C_FLAGS) -o $@ $(LINK_FLAGS) endif # WINDOWS diff --git a/dpf/utils/symbols/dssi.def b/dpf/utils/symbols/dssi.def new file mode 100644 index 0000000..365e577 --- /dev/null +++ b/dpf/utils/symbols/dssi.def @@ -0,0 +1,3 @@ +EXPORTS +ladspa_descriptor +dssi_descriptor diff --git a/dpf/utils/symbols/dssi.exp b/dpf/utils/symbols/dssi.exp new file mode 100644 index 0000000..4d02470 --- /dev/null +++ b/dpf/utils/symbols/dssi.exp @@ -0,0 +1,2 @@ +_ladspa_descriptor +_dssi_descriptor diff --git a/dpf/utils/symbols/dssi.version b/dpf/utils/symbols/dssi.version new file mode 100644 index 0000000..9c092b0 --- /dev/null +++ b/dpf/utils/symbols/dssi.version @@ -0,0 +1,4 @@ +{ + global: ladspa_descriptor; dssi_descriptor; + local: *; +}; diff --git a/dpf/utils/symbols/ladspa.def b/dpf/utils/symbols/ladspa.def new file mode 100644 index 0000000..d8600a8 --- /dev/null +++ b/dpf/utils/symbols/ladspa.def @@ -0,0 +1,2 @@ +EXPORTS +ladspa_descriptor diff --git a/dpf/utils/symbols/ladspa.exp b/dpf/utils/symbols/ladspa.exp new file mode 100644 index 0000000..853ffc6 --- /dev/null +++ b/dpf/utils/symbols/ladspa.exp @@ -0,0 +1 @@ +_ladspa_descriptor diff --git a/dpf/utils/symbols/ladspa.version b/dpf/utils/symbols/ladspa.version new file mode 100644 index 0000000..5764c26 --- /dev/null +++ b/dpf/utils/symbols/ladspa.version @@ -0,0 +1,4 @@ +{ + global: ladspa_descriptor; + local: *; +}; diff --git a/dpf/utils/symbols/lv2-dsp.def b/dpf/utils/symbols/lv2-dsp.def new file mode 100644 index 0000000..1c863e5 --- /dev/null +++ b/dpf/utils/symbols/lv2-dsp.def @@ -0,0 +1,3 @@ +EXPORTS +lv2_descriptor +lv2_generate_ttl diff --git a/dpf/utils/symbols/lv2-dsp.exp b/dpf/utils/symbols/lv2-dsp.exp new file mode 100644 index 0000000..ecbc4e5 --- /dev/null +++ b/dpf/utils/symbols/lv2-dsp.exp @@ -0,0 +1,2 @@ +_lv2_descriptor +_lv2_generate_ttl diff --git a/dpf/utils/symbols/lv2-dsp.version b/dpf/utils/symbols/lv2-dsp.version new file mode 100644 index 0000000..45aa9e2 --- /dev/null +++ b/dpf/utils/symbols/lv2-dsp.version @@ -0,0 +1,4 @@ +{ + global: lv2_descriptor; lv2_generate_ttl; + local: *; +}; diff --git a/dpf/utils/symbols/lv2-ui.def b/dpf/utils/symbols/lv2-ui.def new file mode 100644 index 0000000..b09d6ed --- /dev/null +++ b/dpf/utils/symbols/lv2-ui.def @@ -0,0 +1,2 @@ +EXPORTS +lv2ui_descriptor diff --git a/dpf/utils/symbols/lv2-ui.exp b/dpf/utils/symbols/lv2-ui.exp new file mode 100644 index 0000000..be0ab95 --- /dev/null +++ b/dpf/utils/symbols/lv2-ui.exp @@ -0,0 +1 @@ +_lv2ui_descriptor diff --git a/dpf/utils/symbols/lv2-ui.version b/dpf/utils/symbols/lv2-ui.version new file mode 100644 index 0000000..8700fd0 --- /dev/null +++ b/dpf/utils/symbols/lv2-ui.version @@ -0,0 +1,4 @@ +{ + global: lv2ui_descriptor; + local: *; +}; diff --git a/dpf/utils/symbols/lv2.def b/dpf/utils/symbols/lv2.def new file mode 100644 index 0000000..6d5dc5b --- /dev/null +++ b/dpf/utils/symbols/lv2.def @@ -0,0 +1,4 @@ +EXPORTS +lv2_descriptor +lv2ui_descriptor +lv2_generate_ttl diff --git a/dpf/utils/symbols/lv2.exp b/dpf/utils/symbols/lv2.exp new file mode 100644 index 0000000..fd101c9 --- /dev/null +++ b/dpf/utils/symbols/lv2.exp @@ -0,0 +1,3 @@ +_lv2_descriptor +_lv2ui_descriptor +_lv2_generate_ttl diff --git a/dpf/utils/symbols/lv2.version b/dpf/utils/symbols/lv2.version new file mode 100644 index 0000000..8f21307 --- /dev/null +++ b/dpf/utils/symbols/lv2.version @@ -0,0 +1,4 @@ +{ + global: lv2_descriptor; lv2ui_descriptor; lv2_generate_ttl; + local: *; +}; diff --git a/dpf/utils/symbols/vst2.def b/dpf/utils/symbols/vst2.def new file mode 100644 index 0000000..ef9055d --- /dev/null +++ b/dpf/utils/symbols/vst2.def @@ -0,0 +1,3 @@ +EXPORTS +VSTPluginMain +main=VSTPluginMain diff --git a/dpf/utils/symbols/vst2.exp b/dpf/utils/symbols/vst2.exp new file mode 100644 index 0000000..9083ed2 --- /dev/null +++ b/dpf/utils/symbols/vst2.exp @@ -0,0 +1 @@ +_VSTPluginMain diff --git a/dpf/utils/symbols/vst2.version b/dpf/utils/symbols/vst2.version new file mode 100644 index 0000000..7ecf460 --- /dev/null +++ b/dpf/utils/symbols/vst2.version @@ -0,0 +1,4 @@ +{ + global: VSTPluginMain; main; + local: *; +}; diff --git a/dpf/utils/symbols/vst3.def b/dpf/utils/symbols/vst3.def new file mode 100644 index 0000000..6af2a8c --- /dev/null +++ b/dpf/utils/symbols/vst3.def @@ -0,0 +1,4 @@ +EXPORTS +GetPluginFactory +InitDll +ExitDll diff --git a/dpf/utils/symbols/vst3.exp b/dpf/utils/symbols/vst3.exp new file mode 100644 index 0000000..0060ddb --- /dev/null +++ b/dpf/utils/symbols/vst3.exp @@ -0,0 +1,3 @@ +_GetPluginFactory +_bundleEntry +_bundleExit diff --git a/dpf/utils/symbols/vst3.version b/dpf/utils/symbols/vst3.version new file mode 100644 index 0000000..a87372b --- /dev/null +++ b/dpf/utils/symbols/vst3.version @@ -0,0 +1,4 @@ +{ + global: GetPluginFactory; ModuleEntry; ModuleExit; + local: *; +}; diff --git a/dpf/utils/valgrind-dpf.supp b/dpf/utils/valgrind-dpf.supp new file mode 100644 index 0000000..a7c866f --- /dev/null +++ b/dpf/utils/valgrind-dpf.supp @@ -0,0 +1,41 @@ +{ + libdl is full of leaks + Memcheck:Leak + ... + fun:_dl_open + ... +} +{ + libdl is full of leaks + Memcheck:Leak + ... + fun:_dl_close + ... +} +{ + libdl is full of leaks + Memcheck:Leak + ... + fun:_dl_init +} +{ + libdl is full of leaks + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls + ... +} +{ + libdl is full of leaks + Memcheck:Leak + ... + fun:call_init.part.0 +} +{ + ignore XInitThreads + Memcheck:Leak + ... + fun:XInitThreads + ... +} diff --git a/plugins/Nekobi/DistrhoPluginNekobi.cpp b/plugins/Nekobi/DistrhoPluginNekobi.cpp index 8689cd1..54fa72d 100644 --- a/plugins/Nekobi/DistrhoPluginNekobi.cpp +++ b/plugins/Nekobi/DistrhoPluginNekobi.cpp @@ -135,7 +135,6 @@ DistrhoPluginNekobi::DistrhoPluginNekobi() fParams.decay = 75.0f; fParams.accent = 25.0f; fParams.volume = 75.0f; - fParams.bypass = false; // Internal stuff fSynth.waveform = 0.0f; @@ -251,9 +250,6 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter) parameter.ranges.max = 100.0f; parameter.midiCC = 7; //Volume break; - case paramBypass: - parameter.initDesignation(kParameterDesignationBypass); - break; } } @@ -280,8 +276,6 @@ float DistrhoPluginNekobi::getParameterValue(uint32_t index) const return fParams.accent; case paramVolume: return fParams.volume; - case paramBypass: - return fParams.bypass ? 1.0f : 0.0f; } return 0.0f; @@ -331,14 +325,6 @@ void DistrhoPluginNekobi::setParameterValue(uint32_t index, float value) fSynth.volume = value/100.0f; DISTRHO_SAFE_ASSERT(fSynth.volume >= 0.0f && fSynth.volume <= 1.0f); break; - case paramBypass: { - const bool bypass = (value > 0.5f); - if (fParams.bypass != bypass) - { - fParams.bypass = bypass; - nekobee_synth_all_voices_off(&fSynth); - } - } break; } } @@ -374,10 +360,6 @@ void DistrhoPluginNekobi::run(const float**, float** outputs, uint32_t frames, c return; } - // ignore midi input if bypassed - if (fParams.bypass) - midiEventCount = 0; - while (framesDone < frames) { if (fSynth.nugget_remains == 0) diff --git a/plugins/Nekobi/DistrhoPluginNekobi.hpp b/plugins/Nekobi/DistrhoPluginNekobi.hpp index d4f5e80..813d2fc 100644 --- a/plugins/Nekobi/DistrhoPluginNekobi.hpp +++ b/plugins/Nekobi/DistrhoPluginNekobi.hpp @@ -42,7 +42,6 @@ public: paramDecay, paramAccent, paramVolume, - paramBypass, paramCount }; @@ -118,7 +117,6 @@ private: float decay; float accent; float volume; - bool bypass; } fParams; nekobee_synth_t fSynth; diff --git a/plugins/ProM/DistrhoUIProM.cpp b/plugins/ProM/DistrhoUIProM.cpp index 71d4b9a..1d91980 100644 --- a/plugins/ProM/DistrhoUIProM.cpp +++ b/plugins/ProM/DistrhoUIProM.cpp @@ -96,6 +96,9 @@ DistrhoUIProM::DistrhoUIProM() DistrhoUIProM::~DistrhoUIProM() { + if (fPM == nullptr) + return; + if (DistrhoPluginProM* const dspPtr = (DistrhoPluginProM*)getPluginInstancePointer()) { const MutexLocker csm(dspPtr->fMutex); diff --git a/plugins/glBars/DistrhoPluginGLBars.cpp b/plugins/glBars/DistrhoPluginGLBars.cpp index cc9e746..c462e6c 100644 --- a/plugins/glBars/DistrhoPluginGLBars.cpp +++ b/plugins/glBars/DistrhoPluginGLBars.cpp @@ -74,8 +74,8 @@ void DistrhoPluginGLBars::initParameter(uint32_t index, Parameter& parameter) parameter.name = "X"; parameter.symbol = "x"; parameter.unit = ""; - parameter.ranges.def = -4.0f; - parameter.ranges.min = 0.0f; + parameter.ranges.def = 0.0f; + parameter.ranges.min = -4.0f; parameter.ranges.max = 4.0f; break; diff --git a/plugins/glBars/DistrhoUIGLBars.cpp b/plugins/glBars/DistrhoUIGLBars.cpp index cd7f3a6..8cb7e3e 100644 --- a/plugins/glBars/DistrhoUIGLBars.cpp +++ b/plugins/glBars/DistrhoUIGLBars.cpp @@ -26,13 +26,17 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- DistrhoUIGLBars::DistrhoUIGLBars() - : UI(512, 512) + : UI(512, 512), + fInitialized(false) { setGeometryConstraints(256, 256, true); } DistrhoUIGLBars::~DistrhoUIGLBars() { + if (! fInitialized) + return; + if (DistrhoPluginGLBars* const dspPtr = (DistrhoPluginGLBars*)getPluginInstancePointer()) { const MutexLocker csm(dspPtr->fMutex); @@ -81,6 +85,8 @@ void DistrhoUIGLBars::uiIdle() if (dspPtr->fState != nullptr) return; + fInitialized = true; + const MutexLocker csm(dspPtr->fMutex); dspPtr->fState = &fState; } diff --git a/plugins/glBars/DistrhoUIGLBars.hpp b/plugins/glBars/DistrhoUIGLBars.hpp index 997522b..953ad88 100644 --- a/plugins/glBars/DistrhoUIGLBars.hpp +++ b/plugins/glBars/DistrhoUIGLBars.hpp @@ -53,6 +53,7 @@ protected: bool onKeyboard(const KeyboardEvent&) override; private: + bool fInitialized; glBarsState fState; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DistrhoUIGLBars)