From 8ce56f345fa7c2b68014e5781e584d431c72eff7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 15 Sep 2021 01:09:01 +0100 Subject: [PATCH 001/504] Typos Signed-off-by: falkTX --- distrho/DistrhoPluginUtils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index d381ca61..aea97e68 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/distrho/DistrhoPluginUtils.hpp @@ -44,7 +44,7 @@ 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 { From 4d96e1207c0b8b4f439b1df2a79835bdb5661385 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 15 Sep 2021 01:10:44 +0100 Subject: [PATCH 002/504] Change AudioMidiSyncHelper to be a struct Signed-off-by: falkTX --- distrho/DistrhoPluginUtils.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index aea97e68..e9bf1c24 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/distrho/DistrhoPluginUtils.hpp @@ -47,8 +47,8 @@ START_NAMESPACE_DISTRHO 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; From 25c398382a0f86e2497986ed7a0720e07ab35f9b Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 15 Sep 2021 01:12:22 +0100 Subject: [PATCH 003/504] Update some dates Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 2 +- distrho/DistrhoPluginMain.cpp | 2 +- distrho/DistrhoPluginUtils.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 0ce83ecf..c70752e8 100644 --- a/distrho/DistrhoInfo.hpp +++ b/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/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp index 10e54e3b..ab6f0efd 100644 --- a/distrho/DistrhoPluginMain.cpp +++ b/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/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index e9bf1c24..47eb0e66 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/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 From 2c956cabead8f73af85427f9a22a01ccc51f5f26 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 16 Sep 2021 14:46:09 +0100 Subject: [PATCH 004/504] Tweak OS auto-detection, prevent duplicates Signed-off-by: falkTX --- Makefile.base.mk | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index dfa64dd9..5c30c328 100644 --- a/Makefile.base.mk +++ b/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 From 92dc4eeddcdbe5b5b2a646652154d222a4483f2a Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 16 Sep 2021 16:14:55 +0100 Subject: [PATCH 005/504] NanoVG: Allow to load raw images of any format, fix size Signed-off-by: falkTX --- dgl/NanoVG.hpp | 18 ++++++++- dgl/src/NanoVG.cpp | 46 +++++++++++++++++++++- dgl/src/nanovg/nanovg.c | 9 ++++- dgl/src/nanovg/nanovg.h | 17 ++++++--- dgl/src/nanovg/nanovg_gl.h | 78 +++++++++++++++++++++++++++++++++----- 5 files changed, 147 insertions(+), 21 deletions(-) diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index e1462a5d..08b8f50b 100644 --- a/dgl/NanoVG.hpp +++ b/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/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 7b0cf0cc..3ad9032c 100644 --- a/dgl/src/NanoVG.cpp +++ b/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/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index aa863547..d8494ace 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/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) @@ -948,7 +953,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, p.image = image; - p.innerColor = p.outerColor = nvgRGBAf(1,1,1,alpha); + p.innerColor = p.outerColor = nvgRGBAf(123,244,1,alpha); return p; } diff --git a/dgl/src/nanovg/nanovg.h b/dgl/src/nanovg/nanovg.h index 032575fa..a2abfe97 100644 --- a/dgl/src/nanovg/nanovg.h +++ b/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/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index b69fc305..fd4b4165 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/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 { From 6ccd50f0a81d482847663f3a4a884f377d629e16 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 16 Sep 2021 16:18:05 +0100 Subject: [PATCH 006/504] Add example to test NanoImage usage Signed-off-by: falkTX --- tests/Makefile | 9 +- tests/NanoImage.cpp | 229 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 tests/NanoImage.cpp diff --git a/tests/Makefile b/tests/Makefile index c154b11d..9b652ea5 100644 --- a/tests/Makefile +++ b/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/tests/NanoImage.cpp b/tests/NanoImage.cpp new file mode 100644 index 00000000..5140bfd4 --- /dev/null +++ b/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; +} + +// -------------------------------------------------------------------------------------------------------------------- From 1606091b33489c9f2441cebd937e79abff000f27 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 16 Sep 2021 16:24:38 +0100 Subject: [PATCH 007/504] Revert 1 testing change Signed-off-by: falkTX --- dgl/src/nanovg/nanovg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index d8494ace..209c34f9 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/dgl/src/nanovg/nanovg.c @@ -953,7 +953,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, p.image = image; - p.innerColor = p.outerColor = nvgRGBAf(123,244,1,alpha); + p.innerColor = p.outerColor = nvgRGBAf(1,1,1,alpha); return p; } From 07e8cee9256e39b3003bc9cbc879ed2c0bbc35b0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Sep 2021 20:02:26 +0100 Subject: [PATCH 008/504] Add method to render window contents to picture file (opengl) --- dgl/Window.hpp | 7 +++++++ dgl/src/Cairo.cpp | 7 +++++++ dgl/src/OpenGL.cpp | 28 ++++++++++++++++++++++++++++ dgl/src/Vulkan.cpp | 7 +++++++ dgl/src/Window.cpp | 5 +++++ dgl/src/WindowPrivateData.cpp | 13 +++++++++++++ dgl/src/WindowPrivateData.hpp | 5 +++++ 7 files changed, 72 insertions(+) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 744f7f2d..9effa695 100644 --- a/dgl/Window.hpp +++ b/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/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index ebefaa56..6cd7f201 100644 --- a/dgl/src/Cairo.cpp +++ b/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/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 098c33e6..739669cd 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -667,6 +667,34 @@ 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/dgl/src/Vulkan.cpp b/dgl/src/Vulkan.cpp index d18b3c32..2fc97589 100644 --- a/dgl/src/Vulkan.cpp +++ b/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/dgl/src/Window.cpp b/dgl/src/Window.cpp index b418fdad..7d27613c 100644 --- a/dgl/src/Window.cpp +++ b/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/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index deaa5595..e869fa67 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/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; @@ -744,6 +749,14 @@ void Window::PrivateData::onPuglExpose() widget->pData->display(); } #endif + + 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); + } } void Window::PrivateData::onPuglClose() diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 6687afc5..5f9f8982 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/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(); From d64c1acc695f024b61fb3964f40bbc9d22454308 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Sep 2021 20:11:47 +0100 Subject: [PATCH 009/504] Fix tests --- dgl/src/OpenGL.cpp | 6 ++++-- dgl/src/WindowPrivateData.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 739669cd..aa2c3960 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -681,8 +681,10 @@ void Window::PrivateData::renderToPicture(const char* const filename, 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++) { + 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]); } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index e869fa67..ce0dce32 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -748,7 +748,6 @@ void Window::PrivateData::onPuglExpose() if (widget->isVisible()) widget->pData->display(); } -#endif if (char* const filename = filenameToRenderInto) { @@ -757,6 +756,7 @@ void Window::PrivateData::onPuglExpose() renderToPicture(filename, getGraphicsContext(), static_cast(rect.width), static_cast(rect.height)); std::free(filename); } +#endif } void Window::PrivateData::onPuglClose() From 28a88dbd65dc4ee501e037c229cbd69401d18722 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 09:42:28 +0100 Subject: [PATCH 010/504] String: Add asBasic, asLower and asUpper methods Signed-off-by: falkTX --- distrho/extra/String.hpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index fe3d9936..04a959be 100644 --- a/distrho/extra/String.hpp +++ b/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). */ From eac2757ce7334818f16e49e41291c86766fdfc77 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 09:43:44 +0100 Subject: [PATCH 011/504] LV2: Convert to license string to URL as much as we can Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 47 ++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index b5296277..2f54931d 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -895,13 +895,54 @@ 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"; - else if (license.contains('"')) + } + // String contaning quotes or spaces, use as-is + else if (license.contains('"') || 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 lowlicense(license.asLower()); + + /**/ if (lowlicense.startsWith("apache")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense == "bsd2" || lowlicense == "bsd-2") + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("bsd")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("cddl")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("epl")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense == "gpl") + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("gpl2") || lowlicense.startsWith("gplv2")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("gpl3") || lowlicense.startsWith("gplv3")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense == "isc") + pluginString += " doap:license ;\n\n"; + else if (lowlicense == "lgpl") + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("lgpl2.0") || lowlicense.startsWith("lgplv2.0")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("lgpl2") || lowlicense.startsWith("lgplv2")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("lgpl3") || lowlicense.startsWith("lgplv3")) + pluginString += " doap:license ;\n\n"; + else if (lowlicense == "mit") + pluginString += " doap:license ;\n\n"; + else if (lowlicense.startsWith("mpl")) + pluginString += " doap:license ;\n\n"; + else + pluginString += " doap:license \"" + license + "\" ;\n\n"; + } } // developer From 15e5b575d72590ccf37f81429c48d2b607653d64 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 10:53:07 +0100 Subject: [PATCH 012/504] LV2: Set plugin type as project too, prevent invalid presets Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 2f54931d..62238c06 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -368,11 +368,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"; @@ -1086,8 +1086,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]; @@ -1103,17 +1108,6 @@ void lv2_generate_ttl(const char* const basename) presetString = "<" DISTRHO_PLUGIN_URI + presetSeparator + "preset" + strBuf + ">\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 Date: Sat, 18 Sep 2021 11:07:17 +0100 Subject: [PATCH 013/504] Switch back to requiring full-state when using programs+state Signed-off-by: falkTX --- distrho/src/DistrhoPluginChecks.h | 11 +++++------ examples/States/DistrhoPluginInfo.h | 3 +++ examples/States/ExamplePluginStates.cpp | 10 ++++------ examples/States/Makefile | 4 ---- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index 7d7883f6..910089db 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/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/examples/States/DistrhoPluginInfo.h b/examples/States/DistrhoPluginInfo.h index 52e13568..383313a0 100644 --- a/examples/States/DistrhoPluginInfo.h +++ b/examples/States/DistrhoPluginInfo.h @@ -29,4 +29,7 @@ #define DISTRHO_PLUGIN_WANT_STATE 1 #define DISTRHO_UI_USER_RESIZABLE 1 +// states and presets together require this in order to function +#define DISTRHO_PLUGIN_WANT_FULL_STATE 1 + #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/examples/States/ExamplePluginStates.cpp b/examples/States/ExamplePluginStates.cpp index 8d1c6c94..df52e07b 100644 --- a/examples/States/ExamplePluginStates.cpp +++ b/examples/States/ExamplePluginStates.cpp @@ -105,9 +105,9 @@ The plugin will be treated as an effect, but it will not change the host audio." * Init */ /** - This plugin has no parameters.. + This plugin has no parameters. */ - void initParameter(uint32_t, Parameter&) override {} + void initParameter(uint32_t, Parameter&) override {} /** Set the name of the program @a index. @@ -172,8 +172,8 @@ The plugin will be treated as an effect, but it will not change the host audio." /** This plugin has no parameters.. */ - void setParameterValue(uint32_t, float) override {} - float getParameterValue(uint32_t) const override { return 0.0f; } + void setParameterValue(uint32_t, float) override {} + float getParameterValue(uint32_t) const override { return 0.0f; } /** Load a program. @@ -208,7 +208,6 @@ The plugin will be treated as an effect, but it will not change the host audio." } } -#if DISTRHO_PLUGIN_WANT_FULL_STATE /* FIXME */ /** Get the value of an internal state. The host may call this function from any non-realtime context. @@ -240,7 +239,6 @@ The plugin will be treated as an effect, but it will not change the host audio." return sFalse; } -#endif /** Change an internal state. diff --git a/examples/States/Makefile b/examples/States/Makefile index 408bd014..0a0bfb77 100644 --- a/examples/States/Makefile +++ b/examples/States/Makefile @@ -30,10 +30,6 @@ ifeq ($(HAVE_OPENGL),true) TARGETS += jack endif # HAVE_OPENGL -ifneq ($(MACOS_OR_WINDOWS),true) -TARGETS += dssi -endif # MACOS_OR_WINDOWS - ifeq ($(HAVE_OPENGL),true) TARGETS += lv2_sep else From fce8f5a880551c8ce97275846549ecad7fb23f65 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 11:07:47 +0100 Subject: [PATCH 014/504] LV2: Generate properties for plugin state keys, passing validation Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 62238c06..9e86b595 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -1078,7 +1078,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"; @@ -1100,6 +1103,18 @@ 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 Date: Sat, 18 Sep 2021 11:20:02 +0100 Subject: [PATCH 015/504] Use flags from base makefile to build lv2-generator Signed-off-by: falkTX --- utils/lv2-ttl-generator/GNUmakefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/lv2-ttl-generator/GNUmakefile b/utils/lv2-ttl-generator/GNUmakefile index 460b04f9..9a0baceb 100644 --- a/utils/lv2-ttl-generator/GNUmakefile +++ b/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 From 6b0402935b16fd721317ba30c6980326b8d7efea Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 11:38:03 +0100 Subject: [PATCH 016/504] Only export the needed symbols for plugins to work Signed-off-by: falkTX --- Makefile.plugins.mk | 15 ++++++++++++--- utils/symbols/dssi.version | 4 ++++ utils/symbols/ladspa.version | 4 ++++ utils/symbols/lv2-dsp.version | 4 ++++ utils/symbols/lv2-ui.version | 4 ++++ utils/symbols/lv2.version | 4 ++++ utils/symbols/vst2.version | 4 ++++ utils/symbols/vst3.version | 4 ++++ 8 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 utils/symbols/dssi.version create mode 100644 utils/symbols/ladspa.version create mode 100644 utils/symbols/lv2-dsp.version create mode 100644 utils/symbols/lv2-ui.version create mode 100644 utils/symbols/lv2.version create mode 100644 utils/symbols/vst2.version create mode 100644 utils/symbols/vst3.version diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index d057e28c..67c3b2e3 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -114,10 +114,19 @@ endif 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_LV2DSP = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor +SYMBOLS_LV2 = $(SYMBOLS_LV2DSP) $(SYMBOLS_LV2UI) SYMBOLS_VST2 = -Wl,-exported_symbol,_VSTPluginMain SYMBOLS_VST3 = -Wl,-exported_symbol,_GetPluginFactory -Wl,-exported_symbol,_bundleEntry -Wl,-exported_symbol,_bundleExit +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 +346,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/utils/symbols/dssi.version b/utils/symbols/dssi.version new file mode 100644 index 00000000..9c092b0f --- /dev/null +++ b/utils/symbols/dssi.version @@ -0,0 +1,4 @@ +{ + global: ladspa_descriptor; dssi_descriptor; + local: *; +}; diff --git a/utils/symbols/ladspa.version b/utils/symbols/ladspa.version new file mode 100644 index 00000000..5764c26c --- /dev/null +++ b/utils/symbols/ladspa.version @@ -0,0 +1,4 @@ +{ + global: ladspa_descriptor; + local: *; +}; diff --git a/utils/symbols/lv2-dsp.version b/utils/symbols/lv2-dsp.version new file mode 100644 index 00000000..45aa9e2c --- /dev/null +++ b/utils/symbols/lv2-dsp.version @@ -0,0 +1,4 @@ +{ + global: lv2_descriptor; lv2_generate_ttl; + local: *; +}; diff --git a/utils/symbols/lv2-ui.version b/utils/symbols/lv2-ui.version new file mode 100644 index 00000000..8700fd02 --- /dev/null +++ b/utils/symbols/lv2-ui.version @@ -0,0 +1,4 @@ +{ + global: lv2ui_descriptor; + local: *; +}; diff --git a/utils/symbols/lv2.version b/utils/symbols/lv2.version new file mode 100644 index 00000000..8f213074 --- /dev/null +++ b/utils/symbols/lv2.version @@ -0,0 +1,4 @@ +{ + global: lv2_descriptor; lv2ui_descriptor; lv2_generate_ttl; + local: *; +}; diff --git a/utils/symbols/vst2.version b/utils/symbols/vst2.version new file mode 100644 index 00000000..7ecf460e --- /dev/null +++ b/utils/symbols/vst2.version @@ -0,0 +1,4 @@ +{ + global: VSTPluginMain; main; + local: *; +}; diff --git a/utils/symbols/vst3.version b/utils/symbols/vst3.version new file mode 100644 index 00000000..838c3f75 --- /dev/null +++ b/utils/symbols/vst3.version @@ -0,0 +1,4 @@ +{ + global: GetPluginFactory; InitDll; ExitDll; ModuleEntry; ModuleExit; + local: *; +}; From d96d0497eb02b2388ceb6e09afa500222b11f28c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 12:31:03 +0100 Subject: [PATCH 017/504] Fix lv2 validation when using state files Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 9e86b595..7c833ace 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -599,6 +599,9 @@ void lv2_generate_ttl(const char* const basename) # if DISTRHO_PLUGIN_WANT_MIDI_INPUT pluginString += " atom:supports <" LV2_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"; # endif @@ -619,6 +622,9 @@ void lv2_generate_ttl(const char* const basename) # endif # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; +# endif +# if DISTRHO_PLUGIN_WANT_STATEFILES + pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; # endif pluginString += " ] ;\n\n"; ++portIndex; @@ -1106,6 +1112,10 @@ void lv2_generate_ttl(const char* const basename) # 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"; From a45661fb8b75a5f95c452dc1a80630f7871d253d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 13:12:18 +0100 Subject: [PATCH 018/504] Fix uninitialized values in metronome example Signed-off-by: falkTX --- examples/Metronome/ExamplePluginMetronome.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/Metronome/ExamplePluginMetronome.cpp b/examples/Metronome/ExamplePluginMetronome.cpp index cb76ca3d..586e4b35 100644 --- a/examples/Metronome/ExamplePluginMetronome.cpp +++ b/examples/Metronome/ExamplePluginMetronome.cpp @@ -72,7 +72,9 @@ public: : Plugin(4, 0, 0), // 4 parameters, 0 programs, 0 states sampleRate(getSampleRate()), counter(0), + wasPlaying(false), phase(0.0f), + envelope(1.0f), decay(0.0f), gain(0.5f), semitone(72), From 37eb4c0832b34afa3664f91f1605a57b47430c2e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 13:33:17 +0100 Subject: [PATCH 019/504] VST2: Cleanup created objects on module/dll unload Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index e8af1507..35ed3d1e 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -42,6 +42,7 @@ #define VST_FORCE_DEPRECATED 0 #include +#include #include #include @@ -1635,6 +1636,20 @@ static void vst_processReplacingCallback(AEffect* effect, float** inputs, float* #undef validPlugin #undef vstObjectPtr +static struct Cleanup { + std::list effects; + + ~Cleanup() + { + for (std::list::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; } From 50579e9fc1b0bffbdb9e69265cd2851ad655611b Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 14:15:14 +0100 Subject: [PATCH 020/504] Start validating/testing plugins Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 85 +++++++++++++++++++++++++++ utils/valgrind-dpf.supp | 40 +++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 utils/valgrind-dpf.supp diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 818dd474..109d8b5a 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -235,3 +235,88 @@ jobs: bin/* !bin/*-ladspa.dll !bin/*-dssi.dll + + plugin-validation: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - name: Set up dependencies + run: | + # custom repos + wget https://launchpad.net/~kxstudio-debian/+archive/kxstudio/+files/kxstudio-repos_10.0.3_all.deb + sudo dpkg -i kxstudio-repos_10.0.3_all.deb + sudo apt-get update -qq + # build-deps + sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev + # runtime testing + sudo apt-get install -yq carla-git lv2-dev lv2lint valgrind + - name: Build plugins + env: + CFLAGS: -g + CXXFLAGS: -g + LDFLAGS: -static-libgcc -static-libstdc++ + run: | + make features + make NOOPT=true SKIP_STRIPPING=true -j $(nproc) + - name: Validate LV2 ttl syntax + run: | + lv2_validate \ + /usr/lib/lv2/mod.lv2/*.ttl \ + /usr/lib/lv2/kx-meta/*.ttl \ + /usr/lib/lv2/kx-control-input-port-change-request.lv2/*.ttl \ + /usr/lib/lv2/kx-programs.lv2/*.ttl \ + ./bin/*.lv2/*.ttl + - name: Validate LV2 metadata and binaries + run: | + export LV2_PATH=/tmp/lv2-path + mkdir ${LV2_PATH} + cp -r bin/*.lv2 \ + /usr/lib/lv2/{atom,buf-size,core,data-access,kx-control-input-port-change-request,kx-programs,instance-access,midi,parameters,port-groups,port-props,options,patch,presets,resize-port,state,time,ui,units,urid,worker}.lv2 \ + ${LV2_PATH} + lv2lint -s lv2_generate_ttl -M nopack $(lv2ls) + - name: Test LADSPA plugins + run: | + for p in $(ls bin/ | grep ladspa.so); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native ladspa ./bin/${p} "" 1>/dev/null; \ + done + - name: Test DSSI plugins + run: | + for p in $(ls bin/ | grep dssi.so); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native dssi ./bin/${p} "" 1>/dev/null; \ + done + - name: Test LV2 plugins + run: | + for p in $(lv2ls); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native lv2 "" ${p} 1>/dev/null; \ + done + - name: Test VST2 plugins + run: | + for p in $(ls bin/ | grep dssi.so); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native vst2 ./bin/${p} "" 1>/dev/null; \ + done diff --git a/utils/valgrind-dpf.supp b/utils/valgrind-dpf.supp new file mode 100644 index 00000000..9ad455e7 --- /dev/null +++ b/utils/valgrind-dpf.supp @@ -0,0 +1,40 @@ +{ + 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_allocate_tls +# ... +# } +{ + libdl is full of leaks + Memcheck:Leak + ... + fun:_dl_init +} +{ + libdl is full of leaks + Memcheck:Leak + ... + fun:call_init.part.0 +} +{ + ignore XInitThreads + Memcheck:Leak + ... + fun:XInitThreads + ... +} From 6c336b78e1102c2d54fbfc23b95ff12edeab6491 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 14:17:10 +0100 Subject: [PATCH 021/504] LV2: Use spdx as license URL Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 83 ++++++++++++++++---------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 7c833ace..cce26313 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -341,6 +341,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 @@ -914,40 +915,62 @@ void lv2_generate_ttl(const char* const basename) // Regular license string, convert to URL as much as we can else { - const String lowlicense(license.asLower()); - - /**/ if (lowlicense.startsWith("apache")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense == "bsd2" || lowlicense == "bsd-2") - pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("bsd")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("cddl")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("epl")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense == "gpl") + 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+") + 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+") + 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 (lowlicense.startsWith("gpl2") || lowlicense.startsWith("gplv2")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("gpl3") || lowlicense.startsWith("gplv3")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense == "isc") - pluginString += " doap:license ;\n\n"; - else if (lowlicense == "lgpl") + else if (uplicense.startsWith("LGPL")) pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("lgpl2.0") || lowlicense.startsWith("lgplv2.0")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("lgpl2") || lowlicense.startsWith("lgplv2")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("lgpl3") || lowlicense.startsWith("lgplv3")) - pluginString += " doap:license ;\n\n"; - else if (lowlicense == "mit") - pluginString += " doap:license ;\n\n"; - else if (lowlicense.startsWith("mpl")) - pluginString += " doap:license ;\n\n"; + + // unknown or not handled yet, log a warning else + { + d_stderr("Unkown license string '%s'", license.buffer()); pluginString += " doap:license \"" + license + "\" ;\n\n"; + } } } From 9d368a4477bb345e002fb2119d2e6e1812367446 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 14:18:02 +0100 Subject: [PATCH 022/504] There was a typo Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 109d8b5a..3478d7b9 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -311,7 +311,7 @@ jobs: done - name: Test VST2 plugins run: | - for p in $(ls bin/ | grep dssi.so); do \ + for p in $(ls bin/ | grep vst.so); do \ env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ valgrind \ --error-exitcode=255 \ From 80e9c5d557e12aef168ebd1aa6d4b54d35e5ba3a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 14:22:31 +0100 Subject: [PATCH 023/504] lilv-utils is needed for lv2ls tool Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 3478d7b9..9b732ddc 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -251,7 +251,7 @@ jobs: # build-deps sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev # runtime testing - sudo apt-get install -yq carla-git lv2-dev lv2lint valgrind + sudo apt-get install -yq carla-git lilv-utils lv2-dev lv2lint valgrind - name: Build plugins env: CFLAGS: -g From 622e283a8042b9222462dd496a1b1b80ab867364 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 14:29:03 +0100 Subject: [PATCH 024/504] Skip plugin runtime memory checks for now, need to fix carla Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 88 +++++++++++++-------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 9b732ddc..e476689d 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -276,47 +276,47 @@ jobs: /usr/lib/lv2/{atom,buf-size,core,data-access,kx-control-input-port-change-request,kx-programs,instance-access,midi,parameters,port-groups,port-props,options,patch,presets,resize-port,state,time,ui,units,urid,worker}.lv2 \ ${LV2_PATH} lv2lint -s lv2_generate_ttl -M nopack $(lv2ls) - - name: Test LADSPA plugins - run: | - for p in $(ls bin/ | grep ladspa.so); do \ - env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - valgrind \ - --error-exitcode=255 \ - --leak-check=full \ - --track-origins=yes \ - --suppressions=./utils/valgrind-dpf.supp \ - /usr/lib/carla/carla-bridge-native ladspa ./bin/${p} "" 1>/dev/null; \ - done - - name: Test DSSI plugins - run: | - for p in $(ls bin/ | grep dssi.so); do \ - env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - valgrind \ - --error-exitcode=255 \ - --leak-check=full \ - --track-origins=yes \ - --suppressions=./utils/valgrind-dpf.supp \ - /usr/lib/carla/carla-bridge-native dssi ./bin/${p} "" 1>/dev/null; \ - done - - name: Test LV2 plugins - run: | - for p in $(lv2ls); do \ - env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - valgrind \ - --error-exitcode=255 \ - --leak-check=full \ - --track-origins=yes \ - --suppressions=./utils/valgrind-dpf.supp \ - /usr/lib/carla/carla-bridge-native lv2 "" ${p} 1>/dev/null; \ - done - - name: Test VST2 plugins - run: | - for p in $(ls bin/ | grep vst.so); do \ - env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - valgrind \ - --error-exitcode=255 \ - --leak-check=full \ - --track-origins=yes \ - --suppressions=./utils/valgrind-dpf.supp \ - /usr/lib/carla/carla-bridge-native vst2 ./bin/${p} "" 1>/dev/null; \ - done + #- name: Test LADSPA plugins + #run: | + #for p in $(ls bin/ | grep ladspa.so); do \ + #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + #valgrind \ + #--error-exitcode=255 \ + #--leak-check=full \ + #--track-origins=yes \ + #--suppressions=./utils/valgrind-dpf.supp \ + #/usr/lib/carla/carla-bridge-native ladspa ./bin/${p} "" 1>/dev/null; \ + #done + #- name: Test DSSI plugins + #run: | + #for p in $(ls bin/ | grep dssi.so); do \ + #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + #valgrind \ + #--error-exitcode=255 \ + #--leak-check=full \ + #--track-origins=yes \ + #--suppressions=./utils/valgrind-dpf.supp \ + #/usr/lib/carla/carla-bridge-native dssi ./bin/${p} "" 1>/dev/null; \ + #done + #- name: Test LV2 plugins + #run: | + #for p in $(lv2ls); do \ + #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + #valgrind \ + #--error-exitcode=255 \ + #--leak-check=full \ + #--track-origins=yes \ + #--suppressions=./utils/valgrind-dpf.supp \ + #/usr/lib/carla/carla-bridge-native lv2 "" ${p} 1>/dev/null; \ + #done + #- name: Test VST2 plugins + #run: | + #for p in $(ls bin/ | grep vst.so); do \ + #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + #valgrind \ + #--error-exitcode=255 \ + #--leak-check=full \ + #--track-origins=yes \ + #--suppressions=./utils/valgrind-dpf.supp \ + #/usr/lib/carla/carla-bridge-native vst2 ./bin/${p} "" 1>/dev/null; \ + #done From 0a16af9e6ca2e0839bc36ea0ed46365b7792f840 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 15:55:43 +0100 Subject: [PATCH 025/504] VST2: use a vector for cleanup effects instead of list Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 35ed3d1e..784d89f1 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -42,9 +42,9 @@ #define VST_FORCE_DEPRECATED 0 #include -#include #include #include +#include #if VESTIGE_HEADER # include "vestige/vestige.h" @@ -1637,11 +1637,11 @@ static void vst_processReplacingCallback(AEffect* effect, float** inputs, float* #undef vstObjectPtr static struct Cleanup { - std::list effects; + std::vector effects; ~Cleanup() { - for (std::list::iterator it = effects.begin(), end = effects.end(); it != end; ++it) + for (std::vector::iterator it = effects.begin(), end = effects.end(); it != end; ++it) { AEffect* const effect = *it; delete (VstObject*)effect->object; From b9270eea59af430a25bc09e8e7d965d95e073940 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 16:20:27 +0100 Subject: [PATCH 026/504] Enable runtime plugin tests again Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 88 +++++++++++++-------------- utils/valgrind-dpf.supp | 15 ++--- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index e476689d..9b732ddc 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -276,47 +276,47 @@ jobs: /usr/lib/lv2/{atom,buf-size,core,data-access,kx-control-input-port-change-request,kx-programs,instance-access,midi,parameters,port-groups,port-props,options,patch,presets,resize-port,state,time,ui,units,urid,worker}.lv2 \ ${LV2_PATH} lv2lint -s lv2_generate_ttl -M nopack $(lv2ls) - #- name: Test LADSPA plugins - #run: | - #for p in $(ls bin/ | grep ladspa.so); do \ - #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - #valgrind \ - #--error-exitcode=255 \ - #--leak-check=full \ - #--track-origins=yes \ - #--suppressions=./utils/valgrind-dpf.supp \ - #/usr/lib/carla/carla-bridge-native ladspa ./bin/${p} "" 1>/dev/null; \ - #done - #- name: Test DSSI plugins - #run: | - #for p in $(ls bin/ | grep dssi.so); do \ - #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - #valgrind \ - #--error-exitcode=255 \ - #--leak-check=full \ - #--track-origins=yes \ - #--suppressions=./utils/valgrind-dpf.supp \ - #/usr/lib/carla/carla-bridge-native dssi ./bin/${p} "" 1>/dev/null; \ - #done - #- name: Test LV2 plugins - #run: | - #for p in $(lv2ls); do \ - #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - #valgrind \ - #--error-exitcode=255 \ - #--leak-check=full \ - #--track-origins=yes \ - #--suppressions=./utils/valgrind-dpf.supp \ - #/usr/lib/carla/carla-bridge-native lv2 "" ${p} 1>/dev/null; \ - #done - #- name: Test VST2 plugins - #run: | - #for p in $(ls bin/ | grep vst.so); do \ - #env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ - #valgrind \ - #--error-exitcode=255 \ - #--leak-check=full \ - #--track-origins=yes \ - #--suppressions=./utils/valgrind-dpf.supp \ - #/usr/lib/carla/carla-bridge-native vst2 ./bin/${p} "" 1>/dev/null; \ - #done + - name: Test LADSPA plugins + run: | + for p in $(ls bin/ | grep ladspa.so); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native ladspa ./bin/${p} "" 1>/dev/null; \ + done + - name: Test DSSI plugins + run: | + for p in $(ls bin/ | grep dssi.so); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native dssi ./bin/${p} "" 1>/dev/null; \ + done + - name: Test LV2 plugins + run: | + for p in $(lv2ls); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native lv2 "" ${p} 1>/dev/null; \ + done + - name: Test VST2 plugins + run: | + for p in $(ls bin/ | grep vst.so); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native vst2 ./bin/${p} "" 1>/dev/null; \ + done diff --git a/utils/valgrind-dpf.supp b/utils/valgrind-dpf.supp index 9ad455e7..a7c866f1 100644 --- a/utils/valgrind-dpf.supp +++ b/utils/valgrind-dpf.supp @@ -12,19 +12,20 @@ fun:_dl_close ... } -# { -# libdl is full of leaks -# Memcheck:Leak -# ... -# fun:_dl_allocate_tls -# ... -# } { 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 From 03fb16c1bf53c8e5a29f6f07c6f63a9dd6670f51 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 16:40:00 +0100 Subject: [PATCH 027/504] Fix lv2 tests Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 9b732ddc..833b2fd9 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -300,6 +300,7 @@ jobs: done - name: Test LV2 plugins run: | + export LV2_PATH=/tmp/lv2-path for p in $(lv2ls); do \ env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ valgrind \ From 4165681819f934ef2ae8a262cc07cb643d0de025 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 19:03:00 +0100 Subject: [PATCH 028/504] More tweaks to LV2 license conversion Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 118 ++++++++++++++++++++----- 1 file changed, 98 insertions(+), 20 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index cce26313..1c6028a1 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -907,8 +907,8 @@ void lv2_generate_ttl(const char* const basename) { pluginString += " doap:license <" + license + "> ;\n\n"; } - // String contaning quotes or spaces, use as-is - else if (license.contains('"') || license.contains(' ')) + // String contaning quotes, use as-is + else if (license.contains('"')) { pluginString += " doap:license \"\"\"" + license + "\"\"\" ;\n\n"; } @@ -920,55 +920,133 @@ void lv2_generate_ttl(const char* const basename) // for reference, see https://spdx.org/licenses/ // common licenses - /**/ if (uplicense == "AGPL-1.0-ONLY" || uplicense == "AGPL1" || uplicense == "AGPLV1") + /**/ 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+") + } + 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") + } + 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+") + } + 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") + } + 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") + } + 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") + } + 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") + } + 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+") + } + 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") + } + 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+") + } + 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") + } + 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+") + } + 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") + } + 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+") + } + 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") + } + 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+") + } + 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("Unkown license string '%s'", license.buffer()); + d_stderr("Unknown license string '%s'", license.buffer()); pluginString += " doap:license \"" + license + "\" ;\n\n"; } } From 4685979dc16e0a236180603cf6f5ce1c8f04a19f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 19:21:07 +0100 Subject: [PATCH 029/504] Export "main" symbol for VST2 on Windows Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 784d89f1..91d6e170 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1740,4 +1740,13 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) return effect; } +#if DISTRHO_OS_WINDOWS +// backwards compat with old hosts +DISTRHO_PLUGIN_EXPORT const AEffect* main(audioMasterCallback audioMaster); +DISTRHO_PLUGIN_EXPORT const AEffect* main(audioMasterCallback audioMaster) +{ + return VSTPluginMain(audioMaster): +} +#endif + // ----------------------------------------------------------------------- From 417f234b16911ab36311a4f31c5b82017e5e56e5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 19:28:02 +0100 Subject: [PATCH 030/504] Write MIDI CC binding LV2 meta-data Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 1c6028a1..1456696e 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/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"; @@ -595,10 +596,10 @@ 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"; @@ -619,10 +620,10 @@ 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"; @@ -753,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)); @@ -821,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) { @@ -839,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); From f4bf9e5cc8c7c9ba697c75ff8aff856f654896db Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Sep 2021 20:18:42 +0200 Subject: [PATCH 031/504] Have the macOS symbol in exp files --- Makefile.plugins.mk | 14 +++++++------- utils/symbols/dssi.exp | 2 ++ utils/symbols/ladspa.exp | 1 + utils/symbols/lv2-dsp.exp | 2 ++ utils/symbols/lv2-ui.exp | 1 + utils/symbols/lv2.exp | 3 +++ utils/symbols/vst2.exp | 1 + utils/symbols/vst3.exp | 3 +++ 8 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 utils/symbols/dssi.exp create mode 100644 utils/symbols/ladspa.exp create mode 100644 utils/symbols/lv2-dsp.exp create mode 100644 utils/symbols/lv2-ui.exp create mode 100644 utils/symbols/lv2.exp create mode 100644 utils/symbols/vst2.exp create mode 100644 utils/symbols/vst3.exp diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 67c3b2e3..136f74b5 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -112,13 +112,13 @@ 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_LV2DSP = -Wl,-exported_symbol,_lv2_descriptor -Wl,-exported_symbol,_lv2_generate_ttl -SYMBOLS_LV2UI = -Wl,-exported_symbol,_lv2ui_descriptor -SYMBOLS_LV2 = $(SYMBOLS_LV2DSP) $(SYMBOLS_LV2UI) -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 SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version SYMBOLS_DSSI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/dssi.version diff --git a/utils/symbols/dssi.exp b/utils/symbols/dssi.exp new file mode 100644 index 00000000..4d02470b --- /dev/null +++ b/utils/symbols/dssi.exp @@ -0,0 +1,2 @@ +_ladspa_descriptor +_dssi_descriptor diff --git a/utils/symbols/ladspa.exp b/utils/symbols/ladspa.exp new file mode 100644 index 00000000..853ffc66 --- /dev/null +++ b/utils/symbols/ladspa.exp @@ -0,0 +1 @@ +_ladspa_descriptor diff --git a/utils/symbols/lv2-dsp.exp b/utils/symbols/lv2-dsp.exp new file mode 100644 index 00000000..ecbc4e5a --- /dev/null +++ b/utils/symbols/lv2-dsp.exp @@ -0,0 +1,2 @@ +_lv2_descriptor +_lv2_generate_ttl diff --git a/utils/symbols/lv2-ui.exp b/utils/symbols/lv2-ui.exp new file mode 100644 index 00000000..be0ab955 --- /dev/null +++ b/utils/symbols/lv2-ui.exp @@ -0,0 +1 @@ +_lv2ui_descriptor diff --git a/utils/symbols/lv2.exp b/utils/symbols/lv2.exp new file mode 100644 index 00000000..fd101c99 --- /dev/null +++ b/utils/symbols/lv2.exp @@ -0,0 +1,3 @@ +_lv2_descriptor +_lv2ui_descriptor +_lv2_generate_ttl diff --git a/utils/symbols/vst2.exp b/utils/symbols/vst2.exp new file mode 100644 index 00000000..9083ed2b --- /dev/null +++ b/utils/symbols/vst2.exp @@ -0,0 +1 @@ +_VSTPluginMain diff --git a/utils/symbols/vst3.exp b/utils/symbols/vst3.exp new file mode 100644 index 00000000..0060ddb1 --- /dev/null +++ b/utils/symbols/vst3.exp @@ -0,0 +1,3 @@ +_GetPluginFactory +_bundleEntry +_bundleExit From b9598ec387f579e714142f934c3c46ce431f5539 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Sep 2021 20:34:01 +0200 Subject: [PATCH 032/504] Revert "Export "main" symbol for VST2 on Windows" This reverts commit 4685979dc16e0a236180603cf6f5ce1c8f04a19f. --- distrho/src/DistrhoPluginVST2.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 91d6e170..784d89f1 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1740,13 +1740,4 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) return effect; } -#if DISTRHO_OS_WINDOWS -// backwards compat with old hosts -DISTRHO_PLUGIN_EXPORT const AEffect* main(audioMasterCallback audioMaster); -DISTRHO_PLUGIN_EXPORT const AEffect* main(audioMasterCallback audioMaster) -{ - return VSTPluginMain(audioMaster): -} -#endif - // ----------------------------------------------------------------------- From 8061ea254b3832b7d090ab044112bc28e40a62d7 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Sep 2021 20:40:10 +0200 Subject: [PATCH 033/504] make: windows exports with def files --- Makefile.plugins.mk | 8 ++++++++ utils/symbols/dssi.def | 3 +++ utils/symbols/ladspa.def | 2 ++ utils/symbols/lv2-dsp.def | 3 +++ utils/symbols/lv2-ui.def | 2 ++ utils/symbols/lv2.def | 4 ++++ utils/symbols/vst2.def | 3 +++ utils/symbols/vst3.def | 4 ++++ 8 files changed, 29 insertions(+) create mode 100644 utils/symbols/dssi.def create mode 100644 utils/symbols/ladspa.def create mode 100644 utils/symbols/lv2-dsp.def create mode 100644 utils/symbols/lv2-ui.def create mode 100644 utils/symbols/lv2.def create mode 100644 utils/symbols/vst2.def create mode 100644 utils/symbols/vst3.def diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 136f74b5..a3e3d96c 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -119,6 +119,14 @@ 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 diff --git a/utils/symbols/dssi.def b/utils/symbols/dssi.def new file mode 100644 index 00000000..365e577a --- /dev/null +++ b/utils/symbols/dssi.def @@ -0,0 +1,3 @@ +EXPORTS +ladspa_descriptor +dssi_descriptor diff --git a/utils/symbols/ladspa.def b/utils/symbols/ladspa.def new file mode 100644 index 00000000..d8600a88 --- /dev/null +++ b/utils/symbols/ladspa.def @@ -0,0 +1,2 @@ +EXPORTS +ladspa_descriptor diff --git a/utils/symbols/lv2-dsp.def b/utils/symbols/lv2-dsp.def new file mode 100644 index 00000000..1c863e5e --- /dev/null +++ b/utils/symbols/lv2-dsp.def @@ -0,0 +1,3 @@ +EXPORTS +lv2_descriptor +lv2_generate_ttl diff --git a/utils/symbols/lv2-ui.def b/utils/symbols/lv2-ui.def new file mode 100644 index 00000000..b09d6ed7 --- /dev/null +++ b/utils/symbols/lv2-ui.def @@ -0,0 +1,2 @@ +EXPORTS +lv2ui_descriptor diff --git a/utils/symbols/lv2.def b/utils/symbols/lv2.def new file mode 100644 index 00000000..6d5dc5b2 --- /dev/null +++ b/utils/symbols/lv2.def @@ -0,0 +1,4 @@ +EXPORTS +lv2_descriptor +lv2ui_descriptor +lv2_generate_ttl diff --git a/utils/symbols/vst2.def b/utils/symbols/vst2.def new file mode 100644 index 00000000..ef9055d3 --- /dev/null +++ b/utils/symbols/vst2.def @@ -0,0 +1,3 @@ +EXPORTS +VSTPluginMain +main=VSTPluginMain diff --git a/utils/symbols/vst3.def b/utils/symbols/vst3.def new file mode 100644 index 00000000..6af2a8c1 --- /dev/null +++ b/utils/symbols/vst3.def @@ -0,0 +1,4 @@ +EXPORTS +GetPluginFactory +InitDll +ExitDll From 848e28cd36c6c563c35607b0f6231903c0c0f178 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 18 Sep 2021 21:01:26 +0200 Subject: [PATCH 034/504] cmake: use symbol export lists --- cmake/DPF-plugin.cmake | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 0253dbca..18860e79 100644 --- a/cmake/DPF-plugin.cmake +++ b/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 # ------------------------------------------------------------------------------ # From 8d83f11df227f8a7672026e1deb05288c684ca92 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 20:33:21 +0100 Subject: [PATCH 035/504] Remove win32 specific vst3 exports from version file Signed-off-by: falkTX --- utils/symbols/vst3.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/symbols/vst3.version b/utils/symbols/vst3.version index 838c3f75..a87372b3 100644 --- a/utils/symbols/vst3.version +++ b/utils/symbols/vst3.version @@ -1,4 +1,4 @@ { - global: GetPluginFactory; InitDll; ExitDll; ModuleEntry; ModuleExit; + global: GetPluginFactory; ModuleEntry; ModuleExit; local: *; }; From e44a908375610d7f46b507532b4602c9f89f085a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Sep 2021 20:55:14 +0100 Subject: [PATCH 036/504] lv2lint: Ignore a known linking warning Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 833b2fd9..be6f35d5 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -275,7 +275,7 @@ jobs: cp -r bin/*.lv2 \ /usr/lib/lv2/{atom,buf-size,core,data-access,kx-control-input-port-change-request,kx-programs,instance-access,midi,parameters,port-groups,port-props,options,patch,presets,resize-port,state,time,ui,units,urid,worker}.lv2 \ ${LV2_PATH} - lv2lint -s lv2_generate_ttl -M nopack $(lv2ls) + lv2lint -s lv2_generate_ttl -l ld-linux-x86-64.so.2 -M nopack $(lv2ls) - name: Test LADSPA plugins run: | for p in $(ls bin/ | grep ladspa.so); do \ From 6d29d5d83d856ada7aef96400c99bdd567796a2e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Sep 2021 11:39:16 +0100 Subject: [PATCH 037/504] Reduce amount of Plugin pure virtual methods, use runtime warnings Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 2 +- Makefile.plugins.mk | 1 + distrho/DistrhoPlugin.hpp | 14 ++-- distrho/src/DistrhoPlugin.cpp | 38 +++++++++-- distrho/src/DistrhoPluginChecks.h | 5 +- distrho/src/DistrhoPluginInternal.hpp | 94 +++++++++++++++++++++++++++ 6 files changed, 139 insertions(+), 15 deletions(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index be6f35d5..295953e2 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -255,7 +255,7 @@ jobs: - name: Build plugins env: CFLAGS: -g - CXXFLAGS: -g + CXXFLAGS: -g -DDPF_ABORT_ON_ERROR LDFLAGS: -static-libgcc -static-libstdc++ run: | make features diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index a3e3d96c..81143871 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -33,6 +33,7 @@ endif BUILD_C_FLAGS += -I. BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl +BUILD_CXX_FLAGS += -Wno-pmf-conversions ifeq ($(HAVE_ALSA),true) BASE_FLAGS += -DHAVE_ALSA diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index b23298f1..048fb93d 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -943,7 +943,7 @@ protected: Initialize the parameter @a index.@n This function will be called once, shortly after the plugin is created. */ - virtual void initParameter(uint32_t index, Parameter& parameter) = 0; + virtual void initParameter(uint32_t index, Parameter& parameter); /** Initialize the port group @a groupId.@n @@ -967,7 +967,7 @@ protected: This function will be called once, shortly after the plugin is created.@n Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. */ - virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0; + virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue); #endif #if DISTRHO_PLUGIN_WANT_STATEFILES @@ -984,7 +984,7 @@ protected: Get the current value of a parameter.@n The host may call this function from any context, including realtime processing. */ - virtual float getParameterValue(uint32_t index) const = 0; + virtual float getParameterValue(uint32_t index) const; /** Change a parameter value.@n @@ -992,7 +992,7 @@ protected: When a parameter is marked as automable, you must ensure no non-realtime operations are performed. @note This function will only be called for parameter inputs. */ - virtual void setParameterValue(uint32_t index, float value) = 0; + virtual void setParameterValue(uint32_t index, float value); #if DISTRHO_PLUGIN_WANT_PROGRAMS /** @@ -1000,7 +1000,7 @@ protected: The host may call this function from any context, including realtime processing.@n Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_PROGRAMS is enabled. */ - virtual void loadProgram(uint32_t index) = 0; + virtual void loadProgram(uint32_t index); #endif #if DISTRHO_PLUGIN_WANT_FULL_STATE @@ -1010,7 +1010,7 @@ protected: Must be implemented by your plugin class if DISTRHO_PLUGIN_WANT_FULL_STATE is enabled. @note The use of this function breaks compatibility with the DSSI format. */ - virtual String getState(const char* key) const = 0; + virtual String getState(const char* key) const; #endif #if DISTRHO_PLUGIN_WANT_STATE @@ -1018,7 +1018,7 @@ protected: Change an internal state @a key to @a value.@n Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. */ - virtual void setState(const char* key, const char* value) = 0; + virtual void setState(const char* key, const char* value); #endif /* -------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 0399820e..8540015a 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -56,8 +56,6 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou pData->programCount = programCount; pData->programNames = new String[programCount]; } -#else - DISTRHO_SAFE_ASSERT(programCount == 0); #endif #if DISTRHO_PLUGIN_WANT_STATE @@ -67,8 +65,6 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou pData->stateKeys = new String[stateCount]; pData->stateDefValues = new String[stateCount]; } -#else - DISTRHO_SAFE_ASSERT(stateCount == 0); #endif } @@ -144,16 +140,48 @@ void Plugin::initAudioPort(bool input, uint32_t index, AudioPort& port) } } +void Plugin::initParameter(uint32_t, Parameter&) {} + void Plugin::initPortGroup(const uint32_t groupId, PortGroup& portGroup) { fillInPredefinedPortGroupData(groupId, portGroup); } +#if DISTRHO_PLUGIN_WANT_PROGRAMS +void Plugin::initProgramName(uint32_t, String&) {} +#endif + +#if DISTRHO_PLUGIN_WANT_STATE +void Plugin::initState(uint32_t, String&, String&) {} +#endif + +#if DISTRHO_PLUGIN_WANT_STATEFILES +bool Plugin::isStateFile(uint32_t index) { return false; } +#endif + +/* ------------------------------------------------------------------------------------------------------------ + * Init */ + +float Plugin::getParameterValue(uint32_t) const { return 0.0f; } +void Plugin::setParameterValue(uint32_t, float) {} + +#if DISTRHO_PLUGIN_WANT_PROGRAMS +void Plugin::loadProgram(uint32_t) {} +#endif + +#if DISTRHO_PLUGIN_WANT_FULL_STATE +String Plugin::getState(const char*) const { return String(); } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE +void Plugin::setState(const char*, const char*) {} +#endif + /* ------------------------------------------------------------------------------------------------------------ * Callbacks (optional) */ void Plugin::bufferSizeChanged(uint32_t) {} -void Plugin::sampleRateChanged(double) {} +void Plugin::sampleRateChanged(double) {} // ----------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index 910089db..b885dd74 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -87,6 +87,7 @@ #ifndef DISTRHO_PLUGIN_WANT_FULL_STATE # define DISTRHO_PLUGIN_WANT_FULL_STATE 0 +# define DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET #endif #ifndef DISTRHO_PLUGIN_WANT_TIMEPOS @@ -146,8 +147,8 @@ // ----------------------------------------------------------------------- // Enable full state if plugin exports presets -#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 +#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET) +# warning Plugins with programs and state should implement full state API too # undef DISTRHO_PLUGIN_WANT_FULL_STATE # define DISTRHO_PLUGIN_WANT_FULL_STATE 1 #endif diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 3cd2298e..c91a20bf 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -247,6 +247,100 @@ public: DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); + /* Verify that virtual functions are overriden if parameters, programs or states are in use. + * This does not work on all compilers, but we use it purely as informational check anyway. */ +#ifdef __GNUC__ +# ifdef DPF_ABORT_ON_ERROR +# define DPF_ABORT abort(); +# else +# define DPF_ABORT +# endif + if (fData->parameterCount != 0) + { + if ((void*)(fPlugin->*(&Plugin::initParameter)) == (void*)&Plugin::initParameter) + { + d_stderr2("DPF warning: Plugins with parameters must implement `initParameter`"); + DPF_ABORT + } + if ((void*)(fPlugin->*(&Plugin::getParameterValue)) == (void*)&Plugin::getParameterValue) + { + d_stderr2("DPF warning: Plugins with parameters must implement `getParameterValue`"); + DPF_ABORT + } + if ((void*)(fPlugin->*(&Plugin::setParameterValue)) == (void*)&Plugin::setParameterValue) + { + d_stderr2("DPF warning: Plugins with parameters must implement `setParameterValue`"); + DPF_ABORT + } + } + +# if DISTRHO_PLUGIN_WANT_PROGRAMS + if (fData->programCount != 0) + { + if ((void*)(fPlugin->*(&Plugin::initProgramName)) == (void*)&Plugin::initProgramName) + { + d_stderr2("DPF warning: Plugins with programs must implement `initProgramName`"); + DPF_ABORT + } + if ((void*)(fPlugin->*(&Plugin::loadProgram)) == (void*)&Plugin::loadProgram) + { + d_stderr2("DPF warning: Plugins with programs must implement `loadProgram`"); + DPF_ABORT + } + } +# else + if (fData->programCount != 0) + { + d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); + DPF_ABORT + } +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + if (fData->stateCount != 0) + { + if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState) + { + d_stderr2("DPF warning: Plugins with state must implement `initState`"); + DPF_ABORT + } + + if ((void*)(fPlugin->*(&Plugin::setState)) == (void*)&Plugin::setState) + { + d_stderr2("DPF warning: Plugins with state must implement `setState`"); + DPF_ABORT + } + } +# else + if (fData->stateCount != 0) + { + d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); + DPF_ABORT + } +# endif + +# if DISTRHO_PLUGIN_WANT_FULL_STATE + if (fData->stateCount != 0) + { + if ((void*)(fPlugin->*(&Plugin::getState)) == (void*)&Plugin::getState) + { + d_stderr2("DPF warning: Plugins with full state must implement `getState`"); + DPF_ABORT + } + } + else + { + if (fData->stateCount == 0) + { + d_stderr2("DPF warning: Plugins with full state must have at least 1 state"); + DPF_ABORT + } + } +# endif + +# undef DPF_ABORT +#endif + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 { uint32_t j=0; From 96dcfa87d7bc3e7ec7f2fd358c2ffe4eea7fa21f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Sep 2021 11:47:40 +0100 Subject: [PATCH 038/504] Correct checks in the previous commit Signed-off-by: falkTX --- distrho/src/DistrhoPlugin.cpp | 24 +++++++++++++++++++----- distrho/src/DistrhoPluginInternal.hpp | 19 ++----------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 8540015a..c517abfc 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -44,28 +44,42 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou pData->audioPorts = new AudioPort[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; #endif +#ifdef DPF_ABORT_ON_ERROR +# define DPF_ABORT abort(); +#else +# define DPF_ABORT +#endif + if (parameterCount > 0) { pData->parameterCount = parameterCount; pData->parameters = new Parameter[parameterCount]; } -#if DISTRHO_PLUGIN_WANT_PROGRAMS if (programCount > 0) { +#if DISTRHO_PLUGIN_WANT_PROGRAMS pData->programCount = programCount; pData->programNames = new String[programCount]; - } +#else + d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); + DPF_ABORT #endif + } -#if DISTRHO_PLUGIN_WANT_STATE if (stateCount > 0) { +#if DISTRHO_PLUGIN_WANT_STATE pData->stateCount = stateCount; pData->stateKeys = new String[stateCount]; pData->stateDefValues = new String[stateCount]; - } +#else + d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); + DPF_ABORT #endif + } + +#undef DPF_ABORT } Plugin::~Plugin() @@ -156,7 +170,7 @@ void Plugin::initState(uint32_t, String&, String&) {} #endif #if DISTRHO_PLUGIN_WANT_STATEFILES -bool Plugin::isStateFile(uint32_t index) { return false; } +bool Plugin::isStateFile(uint32_t) { return false; } #endif /* ------------------------------------------------------------------------------------------------------------ diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index c91a20bf..43067d0b 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -288,12 +288,6 @@ public: DPF_ABORT } } -# else - if (fData->programCount != 0) - { - d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); - DPF_ABORT - } # endif # if DISTRHO_PLUGIN_WANT_STATE @@ -311,12 +305,6 @@ public: DPF_ABORT } } -# else - if (fData->stateCount != 0) - { - d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); - DPF_ABORT - } # endif # if DISTRHO_PLUGIN_WANT_FULL_STATE @@ -330,11 +318,8 @@ public: } else { - if (fData->stateCount == 0) - { - d_stderr2("DPF warning: Plugins with full state must have at least 1 state"); - DPF_ABORT - } + d_stderr2("DPF warning: Plugins with full state must have at least 1 state"); + DPF_ABORT } # endif From 9d8df7e122c688551d245775ded7130edbc0d295 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Sep 2021 11:57:01 +0100 Subject: [PATCH 039/504] Fix some compiler warnings Signed-off-by: falkTX --- distrho/src/DistrhoDefines.h | 6 ++++++ distrho/src/DistrhoPluginInternal.hpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index 615550f8..afff900e 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -193,6 +193,12 @@ private: \ # define DISTRHO_OS_SPLIT_STR ":" #endif +/* MSVC warnings */ +#ifdef _MSC_VER +# define strdup _strdup +# pragma warning(disable:4244) /* possible loss of data */ +#endif + /* Useful typedefs */ typedef unsigned char uchar; typedef unsigned short int ushort; diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 43067d0b..011936a6 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -249,7 +249,7 @@ public: /* Verify that virtual functions are overriden if parameters, programs or states are in use. * This does not work on all compilers, but we use it purely as informational check anyway. */ -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(__clang__) # ifdef DPF_ABORT_ON_ERROR # define DPF_ABORT abort(); # else From 1adef358f4bfcf9b8eb8afcaf560cd6ad6db84f8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Sep 2021 12:20:35 +0100 Subject: [PATCH 040/504] More compiler warnings fixing Signed-off-by: falkTX --- dgl/EventHandlers.hpp | 13 +++++++++++-- dgl/NanoVG.hpp | 8 ++++++++ dgl/src/Color.cpp | 4 ++-- dgl/src/Geometry.cpp | 8 +++++--- dgl/src/OpenGL.cpp | 2 +- dgl/src/SubWidget.cpp | 4 +++- dgl/src/Window.cpp | 4 ++-- distrho/src/DistrhoPluginInternal.hpp | 2 +- 8 files changed, 33 insertions(+), 12 deletions(-) diff --git a/dgl/EventHandlers.hpp b/dgl/EventHandlers.hpp index dddeb427..a460440e 100644 --- a/dgl/EventHandlers.hpp +++ b/dgl/EventHandlers.hpp @@ -52,7 +52,7 @@ public: }; explicit ButtonEventHandler(SubWidget* self); - ~ButtonEventHandler(); + virtual ~ButtonEventHandler(); bool isActive() noexcept; void setActive(bool active, bool sendCallback) noexcept; @@ -117,7 +117,7 @@ public: explicit KnobEventHandler(SubWidget* self); explicit KnobEventHandler(SubWidget* self, const KnobEventHandler& other); KnobEventHandler& operator=(const KnobEventHandler& other); - ~KnobEventHandler(); + virtual ~KnobEventHandler(); // returns raw value, is assumed to be scaled if using log float getValue() const noexcept; @@ -154,6 +154,15 @@ private: struct PrivateData; PrivateData* const pData; + /* not for use */ +#ifdef DISTRHO_PROPER_CPP11_SUPPORT + KnobEventHandler(KnobEventHandler& other) = delete; + KnobEventHandler(const KnobEventHandler& other) = delete; +#else + KnobEventHandler(KnobEventHandler& other); + KnobEventHandler(const KnobEventHandler& other); +#endif + DISTRHO_LEAK_DETECTOR(KnobEventHandler) }; diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index 08b8f50b..4a8fd52c 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -23,6 +23,10 @@ #include "TopLevelWidget.hpp" #include "StandaloneWindow.hpp" +#ifdef _MSC_VER +# pragma warning(disable:4661) /* instantiated template classes whose methods are defined elsewhere */ +#endif + #ifndef DGL_NO_SHARED_RESOURCES # define NANOVG_DEJAVU_SANS_TTF "__dpf_dejavusans_ttf__" #endif @@ -964,4 +968,8 @@ typedef NanoSubWidget NanoWidget; END_NAMESPACE_DGL +#ifdef _MSC_VER +# pragma warning(enable:4661) +#endif + #endif // DGL_NANO_WIDGET_HPP_INCLUDED diff --git a/dgl/src/Color.cpp b/dgl/src/Color.cpp index 4aa6b45d..10382fbd 100644 --- a/dgl/src/Color.cpp +++ b/dgl/src/Color.cpp @@ -114,10 +114,10 @@ Color::Color(const Color& color1, const Color& color2, const float u) noexcept interpolate(color2, u); } -Color Color::withAlpha(const float alpha) noexcept +Color Color::withAlpha(const float alpha2) noexcept { Color color(*this); - color.alpha = alpha; + color.alpha = alpha2; return color; } diff --git a/dgl/src/Geometry.cpp b/dgl/src/Geometry.cpp index 44e99185..eb8df6e5 100644 --- a/dgl/src/Geometry.cpp +++ b/dgl/src/Geometry.cpp @@ -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 @@ -15,8 +15,10 @@ */ #ifdef _MSC_VER -// instantiated template classes whose methods are defined elsewhere -# pragma warning(disable:4661) +# pragma warning(disable:4661) /* instantiated template classes whose methods are defined elsewhere */ +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" #endif #include "../Geometry.hpp" diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index aa2c3960..eb619c3a 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -678,7 +678,7 @@ void Window::PrivateData::renderToPicture(const char* const filename, GLubyte* const pixels = new GLubyte[width * height * 3 * sizeof(GLubyte)]; glFlush(); - glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); + glReadPixels(0, 0, static_cast(width), static_cast(height), GL_RGB, GL_UNSIGNED_BYTE, pixels); fprintf(f, "P3\n%d %d\n255\n", width, height); for (uint y = 0; y < height; y++) diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index c4cfd0b8..32c93221 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -34,7 +34,9 @@ SubWidget::~SubWidget() template bool SubWidget::contains(const T x, const T y) const noexcept { - return Rectangle(0, 0, getWidth()-pData->margin.getX(), getHeight()-pData->margin.getY()).contains(x, y); + return Rectangle(0, 0, + static_cast(getWidth()) - pData->margin.getX(), + static_cast(getHeight()) - pData->margin.getY()).contains(x, y); } template diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 7d27613c..c9d12df7 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -220,10 +220,10 @@ void Window::setSize(uint width, uint height) { // fix width if (reqRatio > ratio) - width = height * ratio; + width = static_cast(height * ratio + 0.5); // fix height else - height = width / ratio; + height = static_cast(static_cast(width) / ratio + 0.5); } } } diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 011936a6..af82b84e 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -355,7 +355,7 @@ public: portGroupIndices.erase(kPortGroupNone); - if (const size_t portGroupSize = portGroupIndices.size()) + if (const uint32_t portGroupSize = portGroupIndices.size()) { fData->portGroups = new PortGroupWithId[portGroupSize]; fData->portGroupCount = portGroupSize; From 8f478f979f70b837a70259b1123da7162cbae35b Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Sep 2021 13:16:54 +0100 Subject: [PATCH 041/504] Yet a few more compiler warning fixes, msvc stuff this time Signed-off-by: falkTX --- dgl/NanoVG.hpp | 3 ++- distrho/src/DistrhoPluginInternal.hpp | 2 +- distrho/src/DistrhoUILV2.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index 4a8fd52c..cf693d35 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -24,6 +24,7 @@ #include "StandaloneWindow.hpp" #ifdef _MSC_VER +# pragma warning(push) # pragma warning(disable:4661) /* instantiated template classes whose methods are defined elsewhere */ #endif @@ -969,7 +970,7 @@ typedef NanoSubWidget NanoWidget; END_NAMESPACE_DGL #ifdef _MSC_VER -# pragma warning(enable:4661) +# pragma warning(pop) #endif #endif // DGL_NANO_WIDGET_HPP_INCLUDED diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index af82b84e..78066fdd 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -355,7 +355,7 @@ public: portGroupIndices.erase(kPortGroupNone); - if (const uint32_t portGroupSize = portGroupIndices.size()) + if (const uint32_t portGroupSize = static_cast(portGroupIndices.size())) { fData->portGroups = new PortGroupWithId[portGroupSize]; fData->portGroupCount = portGroupSize; diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 34d0ac92..19df7a1b 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -285,11 +285,13 @@ protected: tmpStr[std::strlen(key)] = '\0'; // set msg size (key + separator + value + null terminator) - const size_t msgSize = tmpStr.length() + 1U; + const uint32_t msgSize = static_cast(tmpStr.length()) + 1U; // reserve atom space - const size_t atomSize = sizeof(LV2_Atom) + msgSize; + const uint32_t atomSize = sizeof(LV2_Atom) + msgSize; char* const atomBuf = (char*)malloc(atomSize); + DISTRHO_SAFE_ASSERT_RETURN(atomBuf != nullptr,); + std::memset(atomBuf, 0, atomSize); // set atom info From 9aec1687c8ef5304efd8da6a65bdd1d0913fa612 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Sep 2021 13:27:45 +0100 Subject: [PATCH 042/504] And even more fixes Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 4 ++-- distrho/src/DistrhoPluginJACK.cpp | 2 +- distrho/src/DistrhoPluginLV2.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index ce0dce32..6a1b5ed3 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -542,13 +542,13 @@ bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& opti // set start directory in UTF-16 encoding std::vector startDirW; startDirW.resize(startDir.length() + 1); - if (MultiByteToWideChar(CP_UTF8, 0, startDir.buffer(), -1, startDirW.data(), startDirW.size())) + if (MultiByteToWideChar(CP_UTF8, 0, startDir.buffer(), -1, startDirW.data(), static_cast(startDirW.size()))) ofn.lpstrInitialDir = startDirW.data(); // set title in UTF-16 encoding std::vector titleW; titleW.resize(title.length() + 1); - if (MultiByteToWideChar(CP_UTF8, 0, title.buffer(), -1, titleW.data(), titleW.size())) + if (MultiByteToWideChar(CP_UTF8, 0, title.buffer(), -1, titleW.data(), static_cast(titleW.size()))) ofn.lpstrTitle = titleW.data(); // prepare a buffer to receive the result diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index bb5fce12..55cdaa56 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -469,7 +469,7 @@ protected: MidiEvent& midiEvent(midiEvents[midiEventCount++]); midiEvent.frame = jevent.time; - midiEvent.size = jevent.size; + midiEvent.size = static_cast(jevent.size); if (midiEvent.size > MidiEvent::kDataSize) midiEvent.dataExt = jevent.buffer; diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 3a7412bd..849438b6 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -708,7 +708,7 @@ public: const String& value(cit->second); // set msg size (key + value + separator + 2x null terminator) - const size_t msgSize = key.length()+value.length()+3; + const uint32_t msgSize = static_cast(key.length()+value.length())+3U; if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) { From d6f29567473fc81ca217711d364add6d4203c4a6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Sep 2021 16:35:49 +0100 Subject: [PATCH 043/504] Change some words for clarity Signed-off-by: falkTX --- dgl/NanoVG.hpp | 8 ++++---- dgl/Window.hpp | 4 ++-- dgl/src/Window.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index cf693d35..af33ab87 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -903,7 +903,7 @@ public: Constructor for a NanoSubWidget. @see CreateFlags */ - explicit NanoBaseWidget(Widget* const parentGroupWidget, int flags = CREATE_ANTIALIAS); + explicit NanoBaseWidget(Widget* parentGroupWidget, int flags = CREATE_ANTIALIAS); /** Constructor for a NanoTopLevelWidget. @@ -912,16 +912,16 @@ public: explicit NanoBaseWidget(Window& windowToMapTo, int flags = CREATE_ANTIALIAS); /** - Constructor for a NanoStandaloneWindow without parent window. + Constructor for a NanoStandaloneWindow without transient parent window. @see CreateFlags */ explicit NanoBaseWidget(Application& app, int flags = CREATE_ANTIALIAS); /** - Constructor for a NanoStandaloneWindow with parent window. + Constructor for a NanoStandaloneWindow with transient parent window. @see CreateFlags */ - explicit NanoBaseWidget(Application& app, Window& parentWindow, int flags = CREATE_ANTIALIAS); + explicit NanoBaseWidget(Application& app, Window& transientParentWindow, int flags = CREATE_ANTIALIAS); /** Destructor. diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 9effa695..b1ef1918 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -157,10 +157,10 @@ public: explicit Window(Application& app); /** - Constructor for a modal window, by having another window as its parent. + Constructor for a modal window, by having another window as its transient parent. The Application instance must be the same between the 2 windows. */ - explicit Window(Application& app, Window& parent); + explicit Window(Application& app, Window& transientParentWindow); /** Constructor for an embed Window without known size, diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index c9d12df7..79538e07 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -66,8 +66,8 @@ Window::Window(Application& app) pData->initPost(); } -Window::Window(Application& app, Window& parent) - : pData(new PrivateData(app, this, parent.pData)) +Window::Window(Application& app, Window& transientParentWindow) + : pData(new PrivateData(app, this, transientParentWindow.pData)) { pData->initPost(); } From ab0b0d45272537bc6e6117b0ea924d8b3b55415e Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Sep 2021 22:56:44 +0100 Subject: [PATCH 044/504] Allow to use Window::openFileBrowser() without arguments Signed-off-by: falkTX --- dgl/Window.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index b1ef1918..27e72dcd 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -369,7 +369,7 @@ public: This function does not block the event loop. */ - bool openFileBrowser(const FileBrowserOptions& options); + bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); #endif /** From 8b8c76e9d2b0dab9a15e7dffbeaf8c4d2a165123 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 12:39:52 +0100 Subject: [PATCH 045/504] Fix EmbedExternalUI build under Haiku (no implementation) Signed-off-by: falkTX --- .../EmbedExternalExampleUI.cpp | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/examples/EmbedExternalUI/EmbedExternalExampleUI.cpp b/examples/EmbedExternalUI/EmbedExternalExampleUI.cpp index f37f2851..8aa5983d 100644 --- a/examples/EmbedExternalUI/EmbedExternalExampleUI.cpp +++ b/examples/EmbedExternalUI/EmbedExternalExampleUI.cpp @@ -19,7 +19,8 @@ #include "DistrhoUI.hpp" -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) # import #elif defined(DISTRHO_OS_WINDOWS) # define WIN32_CLASS_NAME "DPF-EmbedExternalExampleUI" @@ -60,7 +61,9 @@ START_NAMESPACE_DISTRHO class EmbedExternalExampleUI : public UI { -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) + char d; +#elif defined(DISTRHO_OS_MAC) NSView* fView; NSExternalWindow* fWindow; #elif defined(DISTRHO_OS_WINDOWS) @@ -73,7 +76,9 @@ class EmbedExternalExampleUI : public UI public: EmbedExternalExampleUI() : UI(512, 256), -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) + d(0) +#elif defined(DISTRHO_OS_MAC) fView(nullptr), fWindow(nullptr) #elif defined(DISTRHO_OS_WINDOWS) @@ -86,7 +91,8 @@ public: const bool standalone = isStandalone(); d_stdout("isStandalone %d", (int)standalone); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc]init]; [NSApplication sharedApplication]; @@ -218,7 +224,8 @@ public: ~EmbedExternalExampleUI() { -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) if (fView == nullptr) return; @@ -274,7 +281,8 @@ protected: void focus() override { d_stdout("focus"); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); [fWindow orderFrontRegardless]; @@ -295,13 +303,14 @@ protected: uintptr_t getNativeWindowHandle() const noexcept override { -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) return (uintptr_t)fView; #elif defined(DISTRHO_OS_WINDOWS) + return (uintptr_t)fWindow; #else return (uintptr_t)fWindow; #endif - return 0; } void sizeChanged(uint width, uint height) override @@ -309,7 +318,8 @@ protected: d_stdout("sizeChanged %u %u", width, height); UI::sizeChanged(width, height); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) NSRect rect = [fView frame]; rect.size = CGSizeMake((CGFloat)width, (CGFloat)height); [fView setFrame:rect]; @@ -329,7 +339,8 @@ protected: void titleChanged(const char* const title) override { d_stdout("titleChanged %s", title); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) if (fWindow != nil) { if (NSString* const nsTitle = [[NSString alloc] @@ -351,7 +362,8 @@ protected: void transientParentWindowChanged(const uintptr_t winId) override { d_stdout("transientParentWindowChanged %lu", winId); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) #elif defined(DISTRHO_OS_WINDOWS) #else DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); @@ -362,7 +374,8 @@ protected: void visibilityChanged(const bool visible) override { d_stdout("visibilityChanged %d", visible); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); if (fWindow != nil) { @@ -394,7 +407,8 @@ protected: void uiIdle() override { // d_stdout("uiIdle"); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) if (isEmbed()) { return; } From 71b4e5d1c5ae019f507573e13345dadf0e613a79 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 12:41:00 +0100 Subject: [PATCH 046/504] Put the runtime checks behind a DPF_RUNTIME_TESTING macro Signed-off-by: falkTX --- Makefile.plugins.mk | 8 ++++++- distrho/DistrhoInfo.hpp | 28 +++++++++++++++++++++++++ distrho/src/DistrhoPluginInternal.hpp | 30 +++++++++++---------------- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 81143871..d4f69a5a 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -33,7 +33,6 @@ endif BUILD_C_FLAGS += -I. BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl -BUILD_CXX_FLAGS += -Wno-pmf-conversions ifeq ($(HAVE_ALSA),true) BASE_FLAGS += -DHAVE_ALSA @@ -224,6 +223,13 @@ endif # TODO split dsp and ui object build flags BASE_FLAGS += $(DGL_FLAGS) +# --------------------------------------------------------------------------------------------------------------------- +# Runtime test build + +ifeq ($(DPF_RUNTIME_TESTING),true) +BUILD_CXX_FLAGS += -DDPF_RUNTIME_TESTING -Wno-pmf-conversions +endif + # --------------------------------------------------------------------------------------------------------------------- # all needs to be first diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index c70752e8..06ff42a9 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -615,6 +615,34 @@ START_NAMESPACE_DISTRHO /** @} */ +/* ------------------------------------------------------------------------------------------------------------ + * Plugin Macros */ + +/** + @defgroup ExtraPluginMacros Extra Plugin Macros + + C Macros to customize DPF behaviour. + + These are macros that do not set plugin features or information, but instead change DPF internals.@n + They are all optional, none are enabled by default. + + All values are a simple define, their value is meaningless (and unused). + @{ + */ + +/** + Whether to enable runtime plugin tests.@n + This will check, during initialization of the plugin, if parameters, programs and states are setup properly.@n + Useful to enable as part of CI, can safely be skipped.@n + Under DPF makefiles can be enabled by using `make DPF_RUNTIME_TESTING=true`. + + @note Some checks are only available with the GCC compiler, + for detecting if a virtual function has been reimplemented. + */ +#define DPF_RUNTIME_TESTING + +/** @} */ + // ----------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 78066fdd..f46e5e8e 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -247,30 +247,26 @@ public: DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); - /* Verify that virtual functions are overriden if parameters, programs or states are in use. +#if defined(DPF_RUNTIME_TESTING) && defined(__GNUC__) && !defined(__clang__) + /* Run-time testing build. + * Verify that virtual functions are overriden if parameters, programs or states are in use. * This does not work on all compilers, but we use it purely as informational check anyway. */ -#if defined(__GNUC__) && !defined(__clang__) -# ifdef DPF_ABORT_ON_ERROR -# define DPF_ABORT abort(); -# else -# define DPF_ABORT -# endif if (fData->parameterCount != 0) { if ((void*)(fPlugin->*(&Plugin::initParameter)) == (void*)&Plugin::initParameter) { d_stderr2("DPF warning: Plugins with parameters must implement `initParameter`"); - DPF_ABORT + abort(); } if ((void*)(fPlugin->*(&Plugin::getParameterValue)) == (void*)&Plugin::getParameterValue) { d_stderr2("DPF warning: Plugins with parameters must implement `getParameterValue`"); - DPF_ABORT + abort(); } if ((void*)(fPlugin->*(&Plugin::setParameterValue)) == (void*)&Plugin::setParameterValue) { d_stderr2("DPF warning: Plugins with parameters must implement `setParameterValue`"); - DPF_ABORT + abort(); } } @@ -280,12 +276,12 @@ public: if ((void*)(fPlugin->*(&Plugin::initProgramName)) == (void*)&Plugin::initProgramName) { d_stderr2("DPF warning: Plugins with programs must implement `initProgramName`"); - DPF_ABORT + abort(); } if ((void*)(fPlugin->*(&Plugin::loadProgram)) == (void*)&Plugin::loadProgram) { d_stderr2("DPF warning: Plugins with programs must implement `loadProgram`"); - DPF_ABORT + abort(); } } # endif @@ -296,13 +292,13 @@ public: if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState) { d_stderr2("DPF warning: Plugins with state must implement `initState`"); - DPF_ABORT + abort(); } if ((void*)(fPlugin->*(&Plugin::setState)) == (void*)&Plugin::setState) { d_stderr2("DPF warning: Plugins with state must implement `setState`"); - DPF_ABORT + abort(); } } # endif @@ -313,17 +309,15 @@ public: if ((void*)(fPlugin->*(&Plugin::getState)) == (void*)&Plugin::getState) { d_stderr2("DPF warning: Plugins with full state must implement `getState`"); - DPF_ABORT + abort(); } } else { d_stderr2("DPF warning: Plugins with full state must have at least 1 state"); - DPF_ABORT + abort(); } # endif - -# undef DPF_ABORT #endif #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 From 1d234f776973c6386d545afc4d45a8e7737eb776 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 13:00:25 +0100 Subject: [PATCH 047/504] Document internal macros Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 06ff42a9..3f1eae6b 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -507,6 +507,20 @@ START_NAMESPACE_DISTRHO */ #define DISTRHO_PLUGIN_IS_SYNTH 1 +/** + Request the minimum buffer size for the input and output event ports.@n + Currently only used in LV2, with a default value of 2048 if unset. + */ +#define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 + +/** + Whether the plugin has an LV2 modgui. + + This will simply add a "rdfs:seeAlso " on the LV2 manifest.@n + It is up to you to create this file. + */ +#define DISTRHO_PLUGIN_USES_MODGUI 0 + /** Enable direct access between the %UI and plugin code. @see UI::getPluginInstancePointer() @@ -626,7 +640,7 @@ START_NAMESPACE_DISTRHO These are macros that do not set plugin features or information, but instead change DPF internals.@n They are all optional, none are enabled by default. - All values are a simple define, their value is meaningless (and unused). + Unless stated otherwise, values are assumed to be a simple/empty define. @{ */ @@ -634,13 +648,36 @@ START_NAMESPACE_DISTRHO Whether to enable runtime plugin tests.@n This will check, during initialization of the plugin, if parameters, programs and states are setup properly.@n Useful to enable as part of CI, can safely be skipped.@n - Under DPF makefiles can be enabled by using `make DPF_RUNTIME_TESTING=true`. + Under DPF makefiles this can be enabled by using `make DPF_RUNTIME_TESTING=true`. @note Some checks are only available with the GCC compiler, for detecting if a virtual function has been reimplemented. */ #define DPF_RUNTIME_TESTING +/** + Whether to show parameter outputs in the VST2 plugins.@n + This is disabled (unset) by default, as the VST2 format has no notion of read-only parameters. + */ +#define DPF_VST_SHOW_PARAMETER_OUTPUTS + +/** + Whether to use OpenGL3 instead of the default OpenGL2 compatility profile. + Under DPF makefiles this can be enabled by using `make USE_OPENGL3=true` on the dgl build step. + + @note This is experimental and incomplete, contributions are welcome and appreciated. + */ +#define DGL_USE_OPENGL3 + +/** + Whether to use the GPLv2+ vestige header instead of the official Steinberg VST2 SDK.@n + This is a boolean, and enabled (set to 1) by default.@n + Set this to 0 in order to create non-GPL binaries. + (but then at your own discretion in regards to Steinberg licensing)@n + When set to 0, DPF will import the VST2 definitions from `"vst/aeffectx.h"` (not shipped with DPF). + */ +#define VESTIGE_HEADER 1 + /** @} */ // ----------------------------------------------------------------------------------------------------------- From b84abed238d11fde5d73d88f27f9ccb4b5fdb971 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 13:17:51 +0100 Subject: [PATCH 048/504] Cleanup Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 3f1eae6b..7c1713f5 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -106,13 +106,6 @@ START_NAMESPACE_DISTRHO return d_cconst('M', 'u', 't', 'e'); } - /* ---------------------------------------------------------------------------------------- - * This example has no parameters, so skip parameter stuff */ - - void initParameter(uint32_t, Parameter&) override {} - float getParameterValue(uint32_t) const override { return 0.0f; } - void setParameterValue(uint32_t, float) override {} - /* ---------------------------------------------------------------------------------------- * Audio/MIDI Processing */ @@ -637,8 +630,8 @@ START_NAMESPACE_DISTRHO C Macros to customize DPF behaviour. - These are macros that do not set plugin features or information, but instead change DPF internals.@n - They are all optional, none are enabled by default. + These are macros that do not set plugin features or information, but instead change DPF internals. + They are all optional. Unless stated otherwise, values are assumed to be a simple/empty define. @{ From addfb8ce3a3ac971627b7ff0cba8abd3a5a65f55 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 15:01:52 +0100 Subject: [PATCH 049/504] Mark SpecialEvent as deprecated, will not use Signed-off-by: falkTX --- dgl/Base.hpp | 16 +++--- dgl/TopLevelWidget.hpp | 1 - dgl/Widget.hpp | 31 +++++----- dgl/src/TopLevelWidget.cpp | 5 -- dgl/src/TopLevelWidgetPrivateData.cpp | 14 ----- dgl/src/TopLevelWidgetPrivateData.hpp | 1 - dgl/src/Widget.cpp | 5 -- dgl/src/WidgetPrivateData.cpp | 18 ------ dgl/src/WidgetPrivateData.hpp | 1 - dgl/src/WindowPrivateData.cpp | 29 +++------- dgl/src/WindowPrivateData.hpp | 1 - distrho/src/DistrhoPluginVST2.cpp | 82 +++++++++++++++------------ distrho/src/DistrhoUIInternal.hpp | 26 +++++---- 13 files changed, 93 insertions(+), 137 deletions(-) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index d726c6fc..0288cb06 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -49,16 +49,16 @@ enum Modifier { /** Keyboard key codepoints. - All keys are identified by a Unicode code point in PuglEventKey::key. This - enumeration defines constants for special keys that do not have a standard - code point, and some convenience constants for control characters. Note - that all keys are handled in the same way, this enumeration is just for + All keys are identified by a Unicode code point in Widget::KeyboardEvent::key. + This enumeration defines constants for special keys that do not have a standard + code point, and some convenience constants for control characters. + Note that all keys are handled in the same way, this enumeration is just for convenience when writing hard-coded key bindings. Keys that do not have a standard code point use values in the Private Use - Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). Applications - must take care to not interpret these values beyond key detection, the - mapping used here is arbitrary and specific to DPF. + Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). + Applications must take care to not interpret these values beyond key detection, + the mapping used here is arbitrary and specific to DPF. */ enum Key { // Convenience symbols for ASCII control characters @@ -116,7 +116,7 @@ enum Key { /** Common flags for all events. */ -enum Flag { +enum EventFlag { kFlagSendEvent = 1, ///< Event is synthetic kFlagIsHint = 2 ///< Event is a hint (not direct user input) }; diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 6c014296..5441dbcd 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -117,7 +117,6 @@ public: protected: bool onKeyboard(const KeyboardEvent&) override; - bool onSpecial(const SpecialEvent&) override; bool onCharacterInput(const CharacterInputEvent&) override; bool onMouse(const MouseEvent&) override; bool onMotion(const MotionEvent&) override; diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 904e0057..8a95dfce 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -58,7 +58,7 @@ public: These are the fields present on all Widget events. @a mod Currently active keyboard modifiers, @see Modifier. - @a mod Event flags, @see Flag. + @a mod Event flags, @see EventFlag. @a time Event timestamp (if any). */ struct BaseEvent { @@ -107,14 +107,10 @@ public: /** Special keyboard event. - This event allows the use of keys that do not have unicode points. - Note that some are non-printable keys. - - @a press True if the key was pressed, false if released. - @a key The key pressed. - @see onSpecial + DEPRECATED This used to be part of DPF due to pugl, but now deprecated and simply non-functional. + All events go through KeyboardEvent or CharacterInputEvent, use those instead. */ - struct SpecialEvent : BaseEvent { + struct DISTRHO_DEPRECATED_BY("KeyboardEvent") SpecialEvent : BaseEvent { bool press; Key key; @@ -398,12 +394,6 @@ protected: */ virtual bool onKeyboard(const KeyboardEvent&); - /** - A function called when a special key is pressed or released. - @return True to stop event propagation, false otherwise. - */ - virtual bool onSpecial(const SpecialEvent&); - /** A function called when an UTF-8 character is received. @return True to stop event propagation, false otherwise. @@ -433,6 +423,19 @@ protected: */ virtual void onResize(const ResizeEvent&); + /** + A function called when a special key is pressed or released. + DEPRECATED use onKeyboard or onCharacterInput + */ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + virtual bool onSpecial(const SpecialEvent&) { return false; } +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 +# pragma GCC diagnostic pop +#endif + private: struct PrivateData; PrivateData* const pData; diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index e044b03d..d714324c 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -100,11 +100,6 @@ bool TopLevelWidget::onKeyboard(const KeyboardEvent&) return false; } -bool TopLevelWidget::onSpecial(const SpecialEvent&) -{ - return false; -} - bool TopLevelWidget::onCharacterInput(const CharacterInputEvent&) { return false; diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 618cf357..29514bb6 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -50,20 +50,6 @@ bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) return selfw->pData->giveKeyboardEventForSubWidgets(ev); } -bool TopLevelWidget::PrivateData::specialEvent(const SpecialEvent& ev) -{ - // ignore event if we are not visible - if (! selfw->pData->visible) - return false; - - // give top-level widget chance to catch this event first - if (self->onSpecial(ev)) - return true; - - // propagate event to all subwidgets recursively - return selfw->pData->giveSpecialEventForSubWidgets(ev); -} - bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& ev) { // ignore event if we are not visible diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index 6c882868..7105b2f8 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -34,7 +34,6 @@ struct TopLevelWidget::PrivateData { ~PrivateData(); void display(); bool keyboardEvent(const KeyboardEvent& ev); - bool specialEvent(const SpecialEvent& ev); bool characterInputEvent(const CharacterInputEvent& ev); bool mouseEvent(const MouseEvent& ev); bool motionEvent(const MotionEvent& ev); diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index e6e41d5a..8aaa90d5 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -167,11 +167,6 @@ bool Widget::onKeyboard(const KeyboardEvent& ev) return pData->giveKeyboardEventForSubWidgets(ev); } -bool Widget::onSpecial(const SpecialEvent& ev) -{ - return pData->giveSpecialEventForSubWidgets(ev); -} - bool Widget::onCharacterInput(const CharacterInputEvent& ev) { return pData->giveCharacterInputEventForSubWidgets(ev); diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 304ad6ef..3cbe644a 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -87,24 +87,6 @@ bool Widget::PrivateData::giveKeyboardEventForSubWidgets(const KeyboardEvent& ev return false; } -bool Widget::PrivateData::giveSpecialEventForSubWidgets(const SpecialEvent& ev) -{ - if (! visible) - return false; - if (subWidgets.size() == 0) - return false; - - FOR_EACH_SUBWIDGET_INV(rit) - { - SubWidget* const widget(*rit); - - if (widget->isVisible() && widget->onSpecial(ev)) - return true; - } - - return false; -} - bool Widget::PrivateData::giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev) { if (! visible) diff --git a/dgl/src/WidgetPrivateData.hpp b/dgl/src/WidgetPrivateData.hpp index ab388522..15b84097 100644 --- a/dgl/src/WidgetPrivateData.hpp +++ b/dgl/src/WidgetPrivateData.hpp @@ -44,7 +44,6 @@ struct Widget::PrivateData { void displaySubWidgets(uint width, uint height, double autoScaleFactor); bool giveKeyboardEventForSubWidgets(const KeyboardEvent& ev); - bool giveSpecialEventForSubWidgets(const SpecialEvent& ev); bool giveCharacterInputEventForSubWidgets(const CharacterInputEvent& ev); bool giveMouseEventForSubWidgets(MouseEvent& ev); bool giveMotionEventForSubWidgets(MotionEvent& ev); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 6a1b5ed3..27705e63 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -820,24 +820,6 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) #endif } -void Window::PrivateData::onPuglSpecial(const Widget::SpecialEvent& ev) -{ - DGL_DBGp("onPuglSpecial : %i %u\n", ev.press, ev.key); - - if (modal.child != nullptr) - return modal.child->focus(); - -#ifndef DPF_TEST_WINDOW_CPP - FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) - { - TopLevelWidget* const widget(*rit); - - if (widget->isVisible() && widget->pData->specialEvent(ev)) - break; - } -#endif -} - void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) { DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string); @@ -982,7 +964,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_KEY_RELEASE: { // unused x, y, xRoot, yRoot (double) - // TODO special keys? Widget::KeyboardEvent ev; ev.mod = event->key.state; ev.flags = event->key.flags; @@ -990,8 +971,14 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ev.press = event->type == PUGL_KEY_PRESS; ev.key = event->key.key; ev.keycode = event->key.keycode; - if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z') - ev.key -= 'a' - 'A'; // a-z -> A-Z + + // keyboard events must always be lowercase + if (ev.key >= 'A' && ev.key <= 'Z') + { + ev.key += 'a' - 'A'; // A-Z -> a-z + ev.mod |= kModifierShift; + } + pData->onPuglKey(ev); break; } diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 5f9f8982..d8e0f8cb 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -178,7 +178,6 @@ struct Window::PrivateData : IdleCallback { void onPuglClose(); void onPuglFocus(bool focus, CrossingMode mode); void onPuglKey(const Widget::KeyboardEvent& ev); - void onPuglSpecial(const Widget::SpecialEvent& ev); void onPuglText(const Widget::CharacterInputEvent& ev); void onPuglMouse(const Widget::MouseEvent& ev); void onPuglMotion(const Widget::MotionEvent& ev); diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 784d89f1..8494abc5 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -261,7 +261,6 @@ public: using namespace DGL_NAMESPACE; - int special = 0; switch (value) { // convert some VST special values to normal keys @@ -304,37 +303,37 @@ public: - kKeyCapsLock - kKeyPrintScreen */ - case 40: special = kKeyF1; break; - case 41: special = kKeyF2; break; - case 42: special = kKeyF3; break; - case 43: special = kKeyF4; break; - case 44: special = kKeyF5; break; - case 45: special = kKeyF6; break; - case 46: special = kKeyF7; break; - case 47: special = kKeyF8; break; - case 48: special = kKeyF9; break; - case 49: special = kKeyF10; break; - case 50: special = kKeyF11; break; - case 51: special = kKeyF12; break; - case 11: special = kKeyLeft; break; - case 12: special = kKeyUp; break; - case 13: special = kKeyRight; break; - case 14: special = kKeyDown; break; - case 15: special = kKeyPageUp; break; - case 16: special = kKeyPageDown; break; - case 10: special = kKeyHome; break; - case 9: special = kKeyEnd; break; - case 21: special = kKeyInsert; break; - case 54: special = kKeyShift; break; - case 55: special = kKeyControl; break; - case 56: special = kKeyAlt; break; - case 58: special = kKeyMenu; break; - case 52: special = kKeyNumLock; break; - case 53: special = kKeyScrollLock; break; - case 5: special = kKeyPause; break; + case 40: index = kKeyF1; break; + case 41: index = kKeyF2; break; + case 42: index = kKeyF3; break; + case 43: index = kKeyF4; break; + case 44: index = kKeyF5; break; + case 45: index = kKeyF6; break; + case 46: index = kKeyF7; break; + case 47: index = kKeyF8; break; + case 48: index = kKeyF9; break; + case 49: index = kKeyF10; break; + case 50: index = kKeyF11; break; + case 51: index = kKeyF12; break; + case 11: index = kKeyLeft; break; + case 12: index = kKeyUp; break; + case 13: index = kKeyRight; break; + case 14: index = kKeyDown; break; + case 15: index = kKeyPageUp; break; + case 16: index = kKeyPageDown; break; + case 10: index = kKeyHome; break; + case 9: index = kKeyEnd; break; + case 21: index = kKeyInsert; break; + case 54: index = kKeyShift; break; + case 55: index = kKeyControl; break; + case 56: index = kKeyAlt; break; + case 58: index = kKeyMenu; break; + case 52: index = kKeyNumLock; break; + case 53: index = kKeyScrollLock; break; + case 5: index = kKeyPause; break; } - switch (special) + switch (index) { case kKeyShift: if (down) @@ -356,15 +355,26 @@ public: break; } - if (special != 0) - { - fUI.handlePluginSpecial(down, static_cast(special), fKeyboardModifiers); - return 1; - } - if (index > 0) { + // keyboard events must always be lowercase + bool needsShiftRevert = false; + if (index >= 'A' && index <= 'Z') + { + index += 'a' - 'A'; // A-Z -> a-z + + if ((fKeyboardModifiers & kModifierShift) == 0x0) + { + needsShiftRevert = true; + fKeyboardModifiers |= kModifierShift; + } + } + fUI.handlePluginKeyboard(down, static_cast(index), fKeyboardModifiers); + + if (needsShiftRevert) + fKeyboardModifiers &= ~kModifierShift; + return 1; } diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index e1d143af..083d5bd2 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -260,21 +260,23 @@ public: #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI bool handlePluginKeyboard(const bool press, const uint key, const uint16_t mods) { - // TODO also trigger Character input event DGL_NAMESPACE::Widget::KeyboardEvent ev; + ev.mod = mods; ev.press = press; - ev.key = key; - ev.mod = mods; - return ui->onKeyboard(ev); - } + ev.key = key; - bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key, const uint16_t mods) - { - DGL_NAMESPACE::Widget::SpecialEvent ev; - ev.press = press; - ev.key = key; - ev.mod = mods; - return ui->onSpecial(ev); + const bool ret = ui->onKeyboard(ev); + + DGL_NAMESPACE::Widget::CharacterInputEvent cev; + cev.mod = mods; + cev.character = key; + + // if shift modifier is on, convert a-z -> A-Z for character input + if (key >= 'a' && key <= 'z' && (mods & kModifierShift) != 0) + cev.character -= 'a' - 'A'; + + ui->onCharacterInput(cev); + return ret; } #endif From b18bce29457e15def7006c72f35257cefcb4e1f0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 15:32:25 +0100 Subject: [PATCH 050/504] Fix no namespace build Signed-off-by: falkTX --- distrho/src/DistrhoUIInternal.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 083d5bd2..232fdb66 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -272,7 +272,7 @@ public: cev.character = key; // if shift modifier is on, convert a-z -> A-Z for character input - if (key >= 'a' && key <= 'z' && (mods & kModifierShift) != 0) + if (key >= 'a' && key <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) cev.character -= 'a' - 'A'; ui->onCharacterInput(cev); From ed7bc8408125b781b98c8b11caadd51b5b289011 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 15:49:46 +0100 Subject: [PATCH 051/504] Document 2 more macros Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 7c1713f5..71eb3cd1 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -654,6 +654,18 @@ START_NAMESPACE_DISTRHO */ #define DPF_VST_SHOW_PARAMETER_OUTPUTS +/** + Disable all file browser related code.@n + Must be set as compiler macro when building DGL. (e.g. `CXXFLAGS="-DDGL_FILE_BROWSER_DISABLED"`) + */ +#define DGL_FILE_BROWSER_DISABLED + +/** + Disable resource files, like internally used fonts.@n + Must be set as compiler macro when building DGL. (e.g. `CXXFLAGS="-DDGL_NO_SHARED_RESOURCES"`) + */ +#define DGL_NO_SHARED_RESOURCES + /** Whether to use OpenGL3 instead of the default OpenGL2 compatility profile. Under DPF makefiles this can be enabled by using `make USE_OPENGL3=true` on the dgl build step. From fcf03070778661bdfc649761a2bbc4576fc1f9b6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 19:31:06 +0100 Subject: [PATCH 052/504] A few more VST3 details, passing carla-discovery now Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 227 ++++++++++++++++++++++++++---- 1 file changed, 197 insertions(+), 30 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index a86c7d5f..827533c0 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -43,6 +43,7 @@ static constexpr const uint32_t dpf_id_ctrl = d_cconst('c', 't', 'r', 'l'); static constexpr const uint32_t dpf_id_proc = d_cconst('p', 'r', 'o', 'c'); static constexpr const uint32_t dpf_id_view = d_cconst('v', 'i', 'e', 'w'); +// plugin uids, values are filled in during plugin init static dpf_tuid dpf_tuid_class = { dpf_id_entry, dpf_id_clas, 0, 0 }; static dpf_tuid dpf_tuid_component = { dpf_id_entry, dpf_id_comp, 0, 0 }; static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, 0 }; @@ -178,25 +179,138 @@ struct ProcessorComponent : ComponentAdapter static ScopedPointer gPluginInfo; -static void gPluginInit() -{ - if (gPluginInfo != nullptr) - return; +// -------------------------------------------------------------------------------------------------------------------- +// dpf_component - d_lastBufferSize = 512; - d_lastSampleRate = 44100.0; - gPluginInfo = new PluginExporter(nullptr, nullptr, nullptr); - d_lastBufferSize = 0; - d_lastSampleRate = 0.0; +struct v3_component_cpp : v3_funknown { + v3_plugin_base base; + v3_component comp; +}; - dpf_tuid_class[3] = dpf_tuid_component[3] = dpf_tuid_controller[3] - = dpf_tuid_processor[3] = dpf_tuid_view[3] = gPluginInfo->getUniqueId(); -} +struct dpf_component : v3_component_cpp { + dpf_component() + { + static const uint8_t* kSupportedFactories[] = { + v3_funknown_iid, + v3_plugin_base_iid, + v3_component_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_component::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* factory_iid : kSupportedFactories) + { + if (v3_tuid_match(factory_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // we only support 1 plugin per binary, so don't have to care here + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_component::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 1; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_component::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_base + + base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result + { + d_stdout("dpf_component::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 41, self, context); + return V3_OK; + }; + + base.terminate = []V3_API(void* self) -> v3_result + { + d_stdout("dpf_component::terminate => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_component + + comp.get_controller_class_id = []V3_API(void* self, v3_tuid class_id) -> v3_result + { + d_stdout("dpf_component::get_controller_class_id => %s | %p %p", __PRETTY_FUNCTION__ + 41, self, class_id); + return V3_NOT_IMPLEMENTED; + }; + + comp.set_io_mode = []V3_API(void* self, int32_t io_mode) -> v3_result + { + d_stdout("dpf_component::set_io_mode => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, io_mode); + return V3_INTERNAL_ERR; + }; + + comp.get_bus_count = []V3_API(void* self, int32_t media_type, int32_t bus_direction) -> int32_t + { + d_stdout("dpf_component::get_bus_count => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction); + return 0; + }; + + comp.get_bus_info = []V3_API(void* self, int32_t media_type, int32_t bus_direction, + int32_t bus_idx, v3_bus_info* bus_info) -> v3_result + { + d_stdout("dpf_component::get_bus_info => %s | %p %i %i %i %p", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, bus_info); + return V3_INTERNAL_ERR; + }; + + comp.get_routing_info = []V3_API(void* self, v3_routing_info* input, v3_routing_info* output) -> v3_result + { + d_stdout("dpf_component::get_routing_info => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, input, output); + return V3_INTERNAL_ERR; + }; + + comp.activate_bus = []V3_API(void* self, int32_t media_type, int32_t bus_direction, + int32_t bus_idx, v3_bool state) -> v3_result + { + d_stdout("dpf_component::activate_bus => %s | %p %i %i %i %u", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, state); + return V3_INTERNAL_ERR; + }; + + comp.set_active = []V3_API(void* self, v3_bool state) -> v3_result + { + d_stdout("dpf_component::set_active => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); + return V3_INTERNAL_ERR; + }; + + comp.set_state = []V3_API(void* self, v3_bstream**) -> v3_result + { + d_stdout("dpf_component::set_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_INTERNAL_ERR; + }; + + comp.get_state = []V3_API(void* self, v3_bstream**) -> v3_result + { + d_stdout("dpf_component::get_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_INTERNAL_ERR; + }; + } +}; // -------------------------------------------------------------------------------------------------------------------- // dpf_factory -struct v3_plugin_factory_cpp : v3_funknown, v3_plugin_factory { +struct v3_plugin_factory_cpp : v3_funknown { + v3_plugin_factory v1; v3_plugin_factory_2 v2; v3_plugin_factory_3 v3; }; @@ -207,7 +321,8 @@ struct dpf_factory : v3_plugin_factory_cpp { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, v3_plugin_factory_iid, - v3_plugin_factory_2_iid + v3_plugin_factory_2_iid /*, + v3_plugin_factory_3_iid */ }; // ------------------------------------------------------------------------------------------------------------ @@ -215,6 +330,7 @@ struct dpf_factory : v3_plugin_factory_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { + d_stdout("dpf_factory::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 37, self, iid, iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -231,27 +347,39 @@ struct dpf_factory : v3_plugin_factory_cpp { }; // we only support 1 plugin per binary, so don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_factory::ref => %s | %p", __PRETTY_FUNCTION__ + 37, self); + return 1; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_factory::unref => %s | %p", __PRETTY_FUNCTION__ + 37, self); + return 0; + }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory - get_factory_info = []V3_API(void*, struct v3_factory_info* const info) -> v3_result + v1.get_factory_info = []V3_API(void* self, struct v3_factory_info* const info) -> v3_result { + d_stdout("dpf_factory::get_factory_info => %s | %p %p", __PRETTY_FUNCTION__ + 37, self, info); DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo->getHomePage(), sizeof(info->url)); DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO return V3_OK; }; - num_classes = []V3_API(void*) -> int32_t + v1.num_classes = []V3_API(void* self) -> int32_t { + d_stdout("dpf_factory::num_classes => %s | %p", __PRETTY_FUNCTION__ + 37, self); return 1; }; - get_class_info = []V3_API(void*, int32_t /*idx*/, struct v3_class_info* const info) -> v3_result + v1.get_class_info = []V3_API(void* self, int32_t idx, struct v3_class_info* const info) -> v3_result { + d_stdout("dpf_factory::get_class_info => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); @@ -259,21 +387,31 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; }; - create_instance = []V3_API(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) -> v3_result + v1.create_instance = []V3_API(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) -> v3_result { - d_stdout("%s %i %p %p %p %p", __PRETTY_FUNCTION__, __LINE__, self, class_id, iid, instance); + d_stdout("dpf_factory::create_instance => %s | %p %p %p %p", __PRETTY_FUNCTION__ + 37, self, class_id, iid, instance); DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); - *instance = nullptr; // new ComponentAdapter(); - return V3_INTERNAL_ERR; + static dpf_component* component = nullptr; + + if (component == nullptr) + { + component = new dpf_component(); + *instance = &component; + return V3_OK; + } + + *instance = nullptr; + return V3_NO_INTERFACE; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_2 - v2.get_class_info_2 = []V3_API(void*, int32_t /*idx*/, struct v3_class_info_2 *info) -> v3_result + v2.get_class_info_2 = []V3_API(void* self, int32_t idx, struct v3_class_info_2* info) -> v3_result { + d_stdout("dpf_factory::get_class_info_2 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); // get_class_info memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; @@ -283,15 +421,28 @@ struct dpf_factory : v3_plugin_factory_cpp { info->class_flags = 0; DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); - DISTRHO_NAMESPACE::strncpy(info->version, "", sizeof(info->version)); // TODO + std::snprintf(info->version, sizeof(info->version), "%u", gPluginInfo->getVersion()); // TODO DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_factory_3 + + v3.get_class_info_utf16 = []V3_API(void* self, int32_t idx, struct v3_class_info_3* info) -> v3_result + { + d_stdout("dpf_factory::get_class_info_utf16 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); + return V3_INTERNAL_ERR; + }; + + v3.set_host_context = []V3_API (void* self, struct v3_funknown* host) -> v3_result + { + d_stdout("dpf_factory::set_host_context => %s | %p %p", __PRETTY_FUNCTION__ + 37, self, host); + return V3_INTERNAL_ERR; + }; } }; -static const dpf_factory dpf_factory; - END_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- @@ -303,8 +454,12 @@ const void* GetPluginFactory(void); const void* GetPluginFactory(void) { USE_NAMESPACE_DISTRHO; - static const struct v3_plugin_factory_2* const factory = (v3_plugin_factory_2*)&dpf_factory; - return &factory; + static const dpf_factory factory; + static const struct v3_plugin_factory_2* factories[] = { + (const v3_plugin_factory_2*)&factory, + nullptr + }; + return factories; } // -------------------------------------------------------------------------------------------------------------------- @@ -327,7 +482,19 @@ bool ENTRYFNNAME(void*); bool ENTRYFNNAME(void*) { USE_NAMESPACE_DISTRHO; - gPluginInit(); + + if (gPluginInfo == nullptr) + { + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + gPluginInfo = new PluginExporter(nullptr, nullptr, nullptr); + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + dpf_tuid_class[3] = dpf_tuid_component[3] = dpf_tuid_controller[3] + = dpf_tuid_processor[3] = dpf_tuid_view[3] = gPluginInfo->getUniqueId(); + } + return true; } From 5ab80c5d2dc799ed54c07f3aeae180f5f274850b Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 20:54:21 +0100 Subject: [PATCH 053/504] VST3: Add stubs for more interfaces Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 355 +++++++++++++++++++++++-- distrho/src/travesty/edit_controller.h | 2 +- distrho/src/travesty/view.h | 18 +- 3 files changed, 340 insertions(+), 35 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 827533c0..3bc33531 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -116,26 +116,24 @@ private: }; -// ----------------------------------------------------------------------- -// WIP this whole section still TODO +// -------------------------------------------------------------------------------------------------------------------- +// Dummy plugin to get data from + +static ScopedPointer gPluginInfo; -struct ControllerComponent; -struct ProcessorComponent; +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view -struct ComponentAdapter : v3_funknown, v3_plugin_base -{ - // needs atomic refcount, starts at 1 +struct v3_plugin_view_cpp : v3_funknown { + v3_plugin_view view; +}; - ComponentAdapter() +struct dpf_plugin_view : v3_plugin_view_cpp { + dpf_plugin_view() { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, - v3_plugin_base_iid, - /* - v3_component_iid, - v3_edit_controller_iid, - v3_audio_processor_iid - */ + v3_plugin_view_iid }; // ------------------------------------------------------------------------------------------------------------ @@ -143,8 +141,7 @@ struct ComponentAdapter : v3_funknown, v3_plugin_base query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("ComponentAdapter::query_interface %p %p %p", self, iid, iface); - + d_stdout("dpf_plugin_view::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -160,24 +157,332 @@ struct ComponentAdapter : v3_funknown, v3_plugin_base return V3_NO_INTERFACE; }; - // TODO use atomic counter - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; + // we only support 1 plugin per binary, so don't have to care here + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 1; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_base + + view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result + { + d_stdout("dpf_plugin_view::is_platform_type_supported => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, platform_type); + return V3_OK; + }; + + view.attached = []V3_API(void* self, void* parent, const char* platform_type) -> v3_result + { + d_stdout("dpf_plugin_view::attached => %s | %p %p %s", __PRETTY_FUNCTION__ + 41, self, parent, platform_type); + return V3_OK; + }; + + view.removed = []V3_API(void* self) -> v3_result + { + d_stdout("dpf_plugin_view::removed => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + view.on_wheel = []V3_API(void* self, float distance) -> v3_result + { + d_stdout("dpf_plugin_view::on_wheel => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, distance); + return V3_OK; + }; + + view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result + { + d_stdout("dpf_plugin_view::on_key_down => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); + return V3_OK; + }; + + view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result + { + d_stdout("dpf_plugin_view::on_key_up => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); + return V3_OK; + }; + + view.get_size = []V3_API(void* self, v3_view_rect*) -> v3_result + { + d_stdout("dpf_plugin_view::get_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + view.set_size = []V3_API(void* self, v3_view_rect*) -> v3_result + { + d_stdout("dpf_plugin_view::set_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result + { + d_stdout("dpf_plugin_view::on_focus => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); + return V3_OK; + }; + + view.set_frame = []V3_API(void* self, v3_plug_frame*) -> v3_result + { + d_stdout("dpf_plugin_view::set_frame => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + view.can_resize = []V3_API(void* self) -> v3_result + { + d_stdout("dpf_plugin_view::can_resize => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + view.check_size_constraint = []V3_API(void* self, v3_view_rect*) -> v3_result + { + d_stdout("dpf_plugin_view::check_size_constraint => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; } }; -struct ControllerComponent : ComponentAdapter -{ +// -------------------------------------------------------------------------------------------------------------------- +// dpf_edit_controller + +struct v3_edit_controller_cpp : v3_funknown { + v3_edit_controller controller; }; -struct ProcessorComponent : ComponentAdapter -{ +struct dpf_edit_controller : v3_edit_controller_cpp { + dpf_edit_controller() + { + static const uint8_t* kSupportedFactories[] = { + v3_funknown_iid, + v3_edit_controller_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_edit_controller::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* factory_iid : kSupportedFactories) + { + if (v3_tuid_match(factory_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // we only support 1 plugin per binary, so don't have to care here + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 1; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_base + + controller.set_component_state = []V3_API(void* self, v3_bstream*) -> v3_result + { + d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + controller.set_state = []V3_API(void* self, v3_bstream*) -> v3_result + { + d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + controller.get_state = []V3_API(void* self, v3_bstream*) -> v3_result + { + d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + controller.get_parameter_count = []V3_API(void* self) -> int32_t + { + d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0; + }; + + controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info*) -> v3_result + { + d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, param_idx); + return V3_OK; + }; + + controller.get_param_string_for_value = []V3_API(void* self, v3_param_id, double normalised, v3_str_128 /*output*/) -> v3_result + { + d_stdout("dpf_edit_controller::get_param_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); + return V3_OK; + }; + + controller.get_param_value_for_string = []V3_API(void* self, v3_param_id, int16_t* input, double* output) -> v3_result + { + d_stdout("dpf_edit_controller::get_param_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, input, output); + return V3_OK; + }; + + controller.normalised_param_to_plain = []V3_API(void* self, v3_param_id, double normalised) -> double + { + d_stdout("dpf_edit_controller::normalised_param_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); + return 0.0; + }; + + controller.plain_param_to_normalised = []V3_API(void* self, v3_param_id, double normalised) -> double + { + d_stdout("dpf_edit_controller::plain_param_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); + return 0.0; + }; + + controller.get_param_normalised = []V3_API(void* self, v3_param_id) -> double + { + d_stdout("dpf_edit_controller::get_param_normalised => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0.0; + }; + + controller.set_param_normalised = []V3_API(void* self, v3_param_id, double normalised) -> v3_result + { + d_stdout("dpf_edit_controller::set_param_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); + return V3_OK; + }; + + controller.set_component_handler = []V3_API(void* self, v3_component_handler**) -> v3_result + { + d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + controller.create_view = []V3_API(void* self, const char* name) -> v3_plug_view** + { + d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, name); + return nullptr; + }; + } }; // -------------------------------------------------------------------------------------------------------------------- -// Dummy plugin to get data from +// dpf_audio_processor -static ScopedPointer gPluginInfo; +struct v3_audio_processor_cpp : v3_funknown { + v3_audio_processor processor; +}; + +struct dpf_audio_processor : v3_audio_processor_cpp { + dpf_audio_processor() + { + static const uint8_t* kSupportedFactories[] = { + v3_funknown_iid, + v3_audio_processor_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_audio_processor::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* factory_iid : kSupportedFactories) + { + if (v3_tuid_match(factory_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // we only support 1 plugin per binary, so don't have to care here + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 1; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_base + + processor.set_bus_arrangements = []V3_API(void* self, + v3_speaker_arrangement* inputs, int32_t num_inputs, + v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result + { + d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 41, self, inputs, num_inputs, outputs, num_outputs); + return V3_OK; + }; + + processor.get_bus_arrangement = []V3_API(void* self, int32_t bus_direction, + int32_t idx, v3_speaker_arrangement*) -> v3_result + { + d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, bus_direction, idx); + return V3_OK; + }; + + processor.can_process_sample_size = []V3_API(void* self, int32_t symbolic_sample_size) -> v3_result + { + d_stdout("dpf_audio_processor::can_process_sample_size => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, symbolic_sample_size); + return V3_OK; + }; + + processor.get_latency_samples = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0; + }; + + processor.setup_processing = []V3_API(void* self, v3_process_setup*) -> v3_result + { + d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + processor.set_processing = []V3_API(void* self, v3_bool state) -> v3_result + { + d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); + return V3_OK; + }; + + processor.process = []V3_API(void* self, v3_process_data*) -> v3_result + { + d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return V3_OK; + }; + + processor.get_tail_samples = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 41, self); + return 0; + }; + } +}; // -------------------------------------------------------------------------------------------------------------------- // dpf_component diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 349786de..a516664a 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -83,7 +83,7 @@ struct v3_edit_controller { (void *self, struct v3_bstream *); V3_API int32_t (*get_parameter_count)(void *self); - V3_API v3_result (*get_param_info) + V3_API v3_result (*get_parameter_info) (void *self, int32_t param_idx, struct v3_param_info *); V3_API v3_result (*get_param_string_for_value) diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index e9c885cb..3fe8b717 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -41,9 +41,9 @@ struct v3_view_rect { # define V3_VIEW_PLATFORM_TYPE_NATIVE V3_VIEW_PLATFORM_TYPE_X11 #endif -struct v3_plug_frame; +struct v3_plugin_frame; -struct v3_plug_view { +struct v3_plugin_view { struct v3_funknown; V3_API v3_result (*is_platform_type_supported) @@ -74,17 +74,17 @@ struct v3_plug_view { (void *self, struct v3_view_rect *); }; -static const v3_tuid v3_plug_view_iid = +static const v3_tuid v3_plugin_view_iid = V3_ID(0x5BC32507, 0xD06049EA, 0xA6151B52, 0x2B755B29); -struct v3_plug_frame { +struct v3_plugin_frame { struct v3_funknown; V3_API v3_result (*resize_view) (void *self, struct v3_plug_view *, struct v3_view_rect *); }; -static const v3_tuid v3_plug_frame_iid = +static const v3_tuid v3_plugin_frame_iid = V3_ID(0x367FAF01, 0xAFA94693, 0x8D4DA2A0, 0xED0882A3); /** @@ -92,26 +92,26 @@ static const v3_tuid v3_plug_frame_iid = * (same IID/iface as presonus view scaling) */ -struct v3_plug_view_content_scale_steinberg { +struct v3_plugin_view_content_scale_steinberg { struct v3_funknown; V3_API v3_result (*set_content_scale_factor) (void *self, float factor); }; -static const v3_tuid v3_plug_view_content_scale_steinberg_iid = +static const v3_tuid v3_plugin_view_content_scale_steinberg_iid = V3_ID(0x65ED9690, 0x8AC44525, 0x8AADEF7A, 0x72EA703F); /** * support for querying the view to find what control is underneath the mouse */ -struct v3_plug_view_param_finder { +struct v3_plugin_view_param_finder { struct v3_funknown; V3_API v3_result (*find_parameter) (void *self, int32_t x, int32_t y, v3_param_id *); }; -static const v3_tuid v3_plug_view_param_finder_iid = +static const v3_tuid v3_plugin_view_param_finder_iid = V3_ID(0x0F618302, 0x215D4587, 0xA512073C, 0x77B9D383); From d6fdcbd34edde956a2a9d4afe88badbb8efa73ce Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 23:11:19 +0100 Subject: [PATCH 054/504] Continue battling vst3, can show parameters now Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 306 +++++++++++++++++++------ distrho/src/travesty/README.txt | 6 +- distrho/src/travesty/align_pop.h | 2 +- distrho/src/travesty/align_push.h | 2 +- distrho/src/travesty/audio_processor.h | 2 +- distrho/src/travesty/base.h | 102 ++++----- distrho/src/travesty/bstream.h | 2 +- distrho/src/travesty/component.h | 2 +- distrho/src/travesty/edit_controller.h | 2 +- distrho/src/travesty/events.h | 2 +- distrho/src/travesty/factory.h | 2 +- distrho/src/travesty/view.h | 10 +- 12 files changed, 306 insertions(+), 134 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 3bc33531..86700742 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -22,8 +22,12 @@ #include "travesty/edit_controller.h" #include "travesty/factory.h" +#include + START_NAMESPACE_DISTRHO +// -------------------------------------------------------------------------------------------------------------------- + #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT static const writeMidiFunc writeMidiCallback = nullptr; #endif @@ -31,11 +35,15 @@ static const writeMidiFunc writeMidiCallback = nullptr; static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif +// -------------------------------------------------------------------------------------------------------------------- // custom v3_tuid compatible type + typedef uint32_t dpf_tuid[4]; static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch"); -// custom uids, fully created during module init +// -------------------------------------------------------------------------------------------------------------------- +// custom, constant uids related to DPF + static constexpr const uint32_t dpf_id_entry = d_cconst('D', 'P', 'F', ' '); static constexpr const uint32_t dpf_id_clas = d_cconst('c', 'l', 'a', 's'); static constexpr const uint32_t dpf_id_comp = d_cconst('c', 'o', 'm', 'p'); @@ -43,14 +51,75 @@ static constexpr const uint32_t dpf_id_ctrl = d_cconst('c', 't', 'r', 'l'); static constexpr const uint32_t dpf_id_proc = d_cconst('p', 'r', 'o', 'c'); static constexpr const uint32_t dpf_id_view = d_cconst('v', 'i', 'e', 'w'); -// plugin uids, values are filled in during plugin init +// -------------------------------------------------------------------------------------------------------------------- +// plugin specific uids (values are filled in during plugin init) + static dpf_tuid dpf_tuid_class = { dpf_id_entry, dpf_id_clas, 0, 0 }; static dpf_tuid dpf_tuid_component = { dpf_id_entry, dpf_id_comp, 0, 0 }; static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, 0 }; static dpf_tuid dpf_tuid_processor = { dpf_id_entry, dpf_id_proc, 0, 0 }; static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +// Utility functions + +const char* tuid2str(const v3_tuid iid) +{ + if (v3_tuid_match(iid, v3_funknown_iid)) + return "{v3_funknown}"; + if (v3_tuid_match(iid, v3_plugin_base_iid)) + return "{v3_plugin_base}"; + if (v3_tuid_match(iid, v3_plugin_factory_iid)) + return "{v3_plugin_factory}"; + if (v3_tuid_match(iid, v3_plugin_factory_2_iid)) + return "{v3_plugin_factory_2}"; + if (v3_tuid_match(iid, v3_plugin_factory_3_iid)) + return "{v3_plugin_factory_3}"; + if (v3_tuid_match(iid, v3_component_iid)) + return "{v3_component}"; + if (v3_tuid_match(iid, v3_bstream_iid)) + return "{v3_bstream}"; + if (v3_tuid_match(iid, v3_event_list_iid)) + return "{v3_event_list}"; + if (v3_tuid_match(iid, v3_param_value_queue_iid)) + return "{v3_param_value_queue}"; + if (v3_tuid_match(iid, v3_param_changes_iid)) + return "{v3_param_changes}"; + if (v3_tuid_match(iid, v3_process_context_requirements_iid)) + return "{v3_process_context_requirements}"; + if (v3_tuid_match(iid, v3_audio_processor_iid)) + return "{v3_audio_processor}"; + if (v3_tuid_match(iid, v3_component_handler_iid)) + return "{v3_component_handler}"; + if (v3_tuid_match(iid, v3_edit_controller_iid)) + return "{v3_edit_controller}"; + if (v3_tuid_match(iid, v3_plugin_view_iid)) + return "{v3_plugin_view}"; + if (v3_tuid_match(iid, v3_plugin_frame_iid)) + return "{v3_plugin_frame}"; + if (v3_tuid_match(iid, v3_plugin_view_content_scale_steinberg_iid)) + return "{v3_plugin_view_content_scale_steinberg}"; + if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid)) + return "{v3_plugin_view_parameter_finder}"; + if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_class}"; + if (std::memcmp(iid, dpf_tuid_component, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_component}"; + if (std::memcmp(iid, dpf_tuid_controller, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_controller}"; + if (std::memcmp(iid, dpf_tuid_processor, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_processor}"; + if (std::memcmp(iid, dpf_tuid_view, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_view}"; + + static char buf[46]; + std::snprintf(buf, sizeof(buf), "{0x%08X,0x%08X,0x%08X,0x%08X}", + (uint32_t)d_cconst(iid[ 0], iid[ 1], iid[ 2], iid[ 3]), + (uint32_t)d_cconst(iid[ 4], iid[ 5], iid[ 6], iid[ 7]), + (uint32_t)d_cconst(iid[ 8], iid[ 9], iid[10], iid[11]), + (uint32_t)d_cconst(iid[12], iid[13], iid[14], iid[15])); + return buf; +} void strncpy(char* const dst, const char* const src, const size_t size) { @@ -67,11 +136,29 @@ void strncpy(char* const dst, const char* const src, const size_t size) } } -// ----------------------------------------------------------------------- +void strncpy_16from8(int16_t* const dst, const char* const src, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + + if (const size_t len = std::min(std::strlen(src), size-1U)) + { + for (size_t i=0; i= 0x80) + continue; -// v3_funknown, v3_plugin_base, v3_component + dst[i] = src[i]; + } + dst[len] = 0; + } + else + { + dst[0] = 0; + } +} -// audio_processor +// ----------------------------------------------------------------------- class PluginVst3 { @@ -141,7 +228,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_plugin_view::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); + d_stdout("dpf_plugin_view::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 41, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -171,7 +258,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { }; // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_base + // v3_plugin_view view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result { @@ -227,7 +314,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; }; - view.set_frame = []V3_API(void* self, v3_plug_frame*) -> v3_result + view.set_frame = []V3_API(void* self, v3_plugin_frame*) -> v3_result { d_stdout("dpf_plugin_view::set_frame => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_OK; @@ -251,6 +338,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // dpf_edit_controller struct v3_edit_controller_cpp : v3_funknown { + v3_plugin_base base; v3_edit_controller controller; }; @@ -267,7 +355,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_edit_controller::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); + d_stdout("dpf_edit_controller::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 53, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -286,94 +374,163 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base + base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result + { + d_stdout("dpf_edit_controller::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 53, self, context); + return V3_OK; + }; + + base.terminate = []V3_API(void* self) -> v3_result + { + d_stdout("dpf_edit_controller::terminate => %s | %p", __PRETTY_FUNCTION__ + 53, self); + return V3_OK; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_edit_controller + controller.set_component_state = []V3_API(void* self, v3_bstream*) -> v3_result { - d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; controller.set_state = []V3_API(void* self, v3_bstream*) -> v3_result { - d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; controller.get_state = []V3_API(void* self, v3_bstream*) -> v3_result { - d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; controller.get_parameter_count = []V3_API(void* self) -> int32_t { - d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return 0; + d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 53, self); + return gPluginInfo->getParameterCount(); }; - controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info*) -> v3_result + controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, param_idx); + d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 53, self, param_idx); + + // set up flags + int32_t flags = 0; + + const auto desig = gPluginInfo->getParameterDesignation(param_idx); + const auto hints = gPluginInfo->getParameterHints(param_idx); + + const ParameterRanges& ranges(gPluginInfo->getParameterRanges(param_idx)); + + switch (desig) + { + case kParameterDesignationNull: + break; + case kParameterDesignationBypass: + flags |= V3_PARAM_IS_BYPASS; + break; + } + + if (hints & kParameterIsAutomable) + flags |= V3_PARAM_CAN_AUTOMATE; + if (hints & kParameterIsOutput) + flags |= V3_PARAM_READ_ONLY; + // TODO V3_PARAM_IS_LIST + + // set up step_count + int32_t step_count = 0; + + if (hints & kParameterIsBoolean) + step_count = 1; + if ((hints & kParameterIsInteger) && ranges.max - ranges.min > 1) + step_count = ranges.max - ranges.min - 1; + + std::memset(param_info, 0, sizeof(v3_param_info)); + param_info->param_id = param_idx; + param_info->flags = flags; + param_info->step_count = step_count; + param_info->default_normalised_value = ranges.getNormalizedValue(ranges.def); + // int32_t unit_id; + strncpy_16from8(param_info->title, gPluginInfo->getParameterName(param_idx), 128); + strncpy_16from8(param_info->short_title, gPluginInfo->getParameterShortName(param_idx), 128); + strncpy_16from8(param_info->units, gPluginInfo->getParameterUnit(param_idx), 128); + + /* + v3_str_128 title; + v3_str_128 short_title; + v3_str_128 units; + */ + return V3_OK; }; - controller.get_param_string_for_value = []V3_API(void* self, v3_param_id, double normalised, v3_str_128 /*output*/) -> v3_result + controller.get_param_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { - d_stdout("dpf_edit_controller::get_param_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); + d_stdout("dpf_edit_controller::get_param_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + + const ParameterRanges& ranges(gPluginInfo->getParameterRanges(index)); + const float realvalue = ranges.getUnnormalizedValue(normalised); + char buf[24]; + sprintf(buf, "%f", realvalue); + strncpy_16from8(output, buf, 128); return V3_OK; }; controller.get_param_value_for_string = []V3_API(void* self, v3_param_id, int16_t* input, double* output) -> v3_result { - d_stdout("dpf_edit_controller::get_param_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, input, output); - return V3_OK; + d_stdout("dpf_edit_controller::get_param_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 53, self, input, output); + return V3_NOT_IMPLEMENTED; }; controller.normalised_param_to_plain = []V3_API(void* self, v3_param_id, double normalised) -> double { - d_stdout("dpf_edit_controller::normalised_param_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); - return 0.0; + d_stdout("dpf_edit_controller::normalised_param_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + return normalised; }; controller.plain_param_to_normalised = []V3_API(void* self, v3_param_id, double normalised) -> double { - d_stdout("dpf_edit_controller::plain_param_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); - return 0.0; + d_stdout("dpf_edit_controller::plain_param_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + return normalised; }; controller.get_param_normalised = []V3_API(void* self, v3_param_id) -> double { - d_stdout("dpf_edit_controller::get_param_normalised => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_edit_controller::get_param_normalised => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0.0; }; controller.set_param_normalised = []V3_API(void* self, v3_param_id, double normalised) -> v3_result { - d_stdout("dpf_edit_controller::set_param_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, normalised); + d_stdout("dpf_edit_controller::set_param_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); return V3_OK; }; controller.set_component_handler = []V3_API(void* self, v3_component_handler**) -> v3_result { - d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; controller.create_view = []V3_API(void* self, const char* name) -> v3_plug_view** { - d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, name); + d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 53, self, name); return nullptr; }; } @@ -399,7 +556,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_audio_processor::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); + d_stdout("dpf_audio_processor::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 53, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -418,67 +575,67 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_base + // v3_audio_processor processor.set_bus_arrangements = []V3_API(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result { - d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 41, self, inputs, num_inputs, outputs, num_outputs); + d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 53, self, inputs, num_inputs, outputs, num_outputs); return V3_OK; }; processor.get_bus_arrangement = []V3_API(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*) -> v3_result { - d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, bus_direction, idx); + d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i", __PRETTY_FUNCTION__ + 53, self, bus_direction, idx); return V3_OK; }; processor.can_process_sample_size = []V3_API(void* self, int32_t symbolic_sample_size) -> v3_result { - d_stdout("dpf_audio_processor::can_process_sample_size => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, symbolic_sample_size); + d_stdout("dpf_audio_processor::can_process_sample_size => %s | %p %i", __PRETTY_FUNCTION__ + 53, self, symbolic_sample_size); return V3_OK; }; processor.get_latency_samples = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; processor.setup_processing = []V3_API(void* self, v3_process_setup*) -> v3_result { - d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; processor.set_processing = []V3_API(void* self, v3_bool state) -> v3_result { - d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); + d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 53, self, state); return V3_OK; }; processor.process = []V3_API(void* self, v3_process_data*) -> v3_result { - d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; processor.get_tail_samples = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; } @@ -493,9 +650,12 @@ struct v3_component_cpp : v3_funknown { }; struct dpf_component : v3_component_cpp { + dpf_audio_processor* processor = nullptr; + dpf_edit_controller* controller = nullptr; + dpf_component() { - static const uint8_t* kSupportedFactories[] = { + static const uint8_t* kSupportedBaseFactories[] = { v3_funknown_iid, v3_plugin_base_iid, v3_component_iid @@ -506,11 +666,11 @@ struct dpf_component : v3_component_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_component::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, iid, iface); + d_stdout("dpf_component::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 41, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* factory_iid : kSupportedFactories) + for (const uint8_t* factory_iid : kSupportedBaseFactories) { if (v3_tuid_match(factory_iid, iid)) { @@ -519,6 +679,24 @@ struct dpf_component : v3_component_cpp { } } + if (v3_tuid_match(v3_audio_processor_iid, iid)) + { + dpf_component* const component = *(dpf_component**)self; + if (component->processor == nullptr) + component->processor = new dpf_audio_processor(); + *iface = (v3_funknown*)&component->processor; + return V3_OK; + } + + if (v3_tuid_match(v3_edit_controller_iid, iid)) + { + dpf_component* const component = *(dpf_component**)self; + if (component->controller == nullptr) + component->controller = new dpf_edit_controller(); + *iface = (v3_funknown*)&component->controller; + return V3_OK; + } + return V3_NO_INTERFACE; }; @@ -555,7 +733,7 @@ struct dpf_component : v3_component_cpp { comp.get_controller_class_id = []V3_API(void* self, v3_tuid class_id) -> v3_result { - d_stdout("dpf_component::get_controller_class_id => %s | %p %p", __PRETTY_FUNCTION__ + 41, self, class_id); + d_stdout("dpf_component::get_controller_class_id => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, tuid2str(class_id)); return V3_NOT_IMPLEMENTED; }; @@ -620,6 +798,8 @@ struct v3_plugin_factory_cpp : v3_funknown { v3_plugin_factory_3 v3; }; +std::list components; + struct dpf_factory : v3_plugin_factory_cpp { dpf_factory() { @@ -635,7 +815,7 @@ struct dpf_factory : v3_plugin_factory_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_factory::query_interface => %s | %p %p %p", __PRETTY_FUNCTION__ + 37, self, iid, iface); + d_stdout("dpf_factory::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 37, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -685,6 +865,8 @@ struct dpf_factory : v3_plugin_factory_cpp { v1.get_class_info = []V3_API(void* self, int32_t idx, struct v3_class_info* const info) -> v3_result { d_stdout("dpf_factory::get_class_info => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); @@ -694,21 +876,14 @@ struct dpf_factory : v3_plugin_factory_cpp { v1.create_instance = []V3_API(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) -> v3_result { - d_stdout("dpf_factory::create_instance => %s | %p %p %p %p", __PRETTY_FUNCTION__ + 37, self, class_id, iid, instance); + d_stdout("dpf_factory::create_instance => %s | %p %s %s %p", __PRETTY_FUNCTION__ + 37, self, tuid2str(class_id), tuid2str(iid), instance); DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); - static dpf_component* component = nullptr; - - if (component == nullptr) - { - component = new dpf_component(); - *instance = &component; - return V3_OK; - } - - *instance = nullptr; - return V3_NO_INTERFACE; + dpf_component* const component = new dpf_component(); + components.push_back(component); + *instance = &components.back(); + return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ @@ -760,11 +935,8 @@ const void* GetPluginFactory(void) { USE_NAMESPACE_DISTRHO; static const dpf_factory factory; - static const struct v3_plugin_factory_2* factories[] = { - (const v3_plugin_factory_2*)&factory, - nullptr - }; - return factories; + static const v3_funknown* const factoryptr = &factory; + return &factoryptr; } // -------------------------------------------------------------------------------------------------------------------- @@ -796,8 +968,8 @@ bool ENTRYFNNAME(void*) d_lastBufferSize = 0; d_lastSampleRate = 0.0; - dpf_tuid_class[3] = dpf_tuid_component[3] = dpf_tuid_controller[3] - = dpf_tuid_processor[3] = dpf_tuid_view[3] = gPluginInfo->getUniqueId(); + dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] + = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo->getUniqueId(); } return true; diff --git a/distrho/src/travesty/README.txt b/distrho/src/travesty/README.txt index af5808ef..558dc709 100644 --- a/distrho/src/travesty/README.txt +++ b/distrho/src/travesty/README.txt @@ -1,9 +1,9 @@ -This folder contains a pure C interface to Steinberg VST3 SDK, codenamed "travesty". +This folder contains a pure C VST3-compatible interface, codenamed "travesty". Name is a play on words from morphing "vestige" (the good old free VST2 reverse-engineered header file) and "three". The main target is to be able to create VST3-compatible plugins without a bloated SDK. -Everything that is required for plugins fits in a few small header files. -Also, being able to build VST3-compatible plugins in pure C code, something not possible with the original SDK. +Everything that is required for plugins fits in a few small header files as presented here. +Also being able to build VST3-compatible plugins in pure C code, something not possible with the original SDK. Please note this project is still a work in progress. Use at your own risk, and please report any issues to https://github.com/DISTRHO/DPF/. diff --git a/distrho/src/travesty/align_pop.h b/distrho/src/travesty/align_pop.h index 729c066d..bf88a3f6 100644 --- a/distrho/src/travesty/align_pop.h +++ b/distrho/src/travesty/align_pop.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/align_push.h b/distrho/src/travesty/align_push.h index 55855a2a..4a7ea9a9 100644 --- a/distrho/src/travesty/align_push.h +++ b/distrho/src/travesty/align_push.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h index be1b1ad5..2386e637 100644 --- a/distrho/src/travesty/audio_processor.h +++ b/distrho/src/travesty/audio_processor.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 1da5530c..3b01d63e 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with @@ -54,42 +54,42 @@ v3_tuid_match(const v3_tuid a, const v3_tuid b) #if V3_COM_COMPAT enum { - V3_NO_INTERFACE = 0x80004002L, - V3_OK = 0, - V3_TRUE = 0, - V3_FALSE = 1, - V3_INVALID_ARG = 0x80070057L, - V3_NOT_IMPLEMENTED = 0x80004001L, - V3_INTERNAL_ERR = 0x80004005L, - V3_NOT_INITIALISED = 0x8000FFFFL, - V3_NOMEM = 0x8007000EL + V3_NO_INTERFACE = 0x80004002L, + V3_OK = 0, + V3_TRUE = 0, + V3_FALSE = 1, + V3_INVALID_ARG = 0x80070057L, + V3_NOT_IMPLEMENTED = 0x80004001L, + V3_INTERNAL_ERR = 0x80004005L, + V3_NOT_INITIALISED = 0x8000FFFFL, + V3_NOMEM = 0x8007000EL }; -# define V3_ID(a, b, c, d) { \ - ((a) & 0x000000FF), \ - ((a) & 0x0000FF00) >> 8, \ - ((a) & 0x00FF0000) >> 16, \ - ((a) & 0xFF000000) >> 24, \ - \ - ((b) & 0x00FF0000) >> 16, \ - ((b) & 0xFF000000) >> 24, \ - ((b) & 0x000000FF), \ - ((b) & 0x0000FF00) >> 8, \ - \ - ((c) & 0xFF000000) >> 24, \ - ((c) & 0x00FF0000) >> 16, \ - ((c) & 0x0000FF00) >> 8, \ - ((c) & 0x000000FF), \ - \ - ((d) & 0xFF000000) >> 24, \ - ((d) & 0x00FF0000) >> 16, \ - ((d) & 0x0000FF00) >> 8, \ - ((d) & 0x000000FF), \ +# define V3_ID(a, b, c, d) { \ + ((a) & 0x000000FF), \ + ((a) & 0x0000FF00) >> 8, \ + ((a) & 0x00FF0000) >> 16, \ + ((a) & 0xFF000000) >> 24, \ + \ + ((b) & 0x00FF0000) >> 16, \ + ((b) & 0xFF000000) >> 24, \ + ((b) & 0x000000FF), \ + ((b) & 0x0000FF00) >> 8, \ + \ + ((c) & 0xFF000000) >> 24, \ + ((c) & 0x00FF0000) >> 16, \ + ((c) & 0x0000FF00) >> 8, \ + ((c) & 0x000000FF), \ + \ + ((d) & 0xFF000000) >> 24, \ + ((d) & 0x00FF0000) >> 16, \ + ((d) & 0x0000FF00) >> 8, \ + ((d) & 0x000000FF), \ } #else // V3_COM_COMPAT enum { - V3_NO_INTERFACE = -1, + V3_NO_INTERFACE = -1, V3_OK, V3_TRUE = V3_OK, V3_FALSE, @@ -100,26 +100,26 @@ enum { V3_NOMEM }; -# define V3_ID(a, b, c, d) { \ - ((a) & 0xFF000000) >> 24, \ - ((a) & 0x00FF0000) >> 16, \ - ((a) & 0x0000FF00) >> 8, \ - ((a) & 0x000000FF), \ - \ - ((b) & 0xFF000000) >> 24, \ - ((b) & 0x00FF0000) >> 16, \ - ((b) & 0x0000FF00) >> 8, \ - ((b) & 0x000000FF), \ - \ - ((c) & 0xFF000000) >> 24, \ - ((c) & 0x00FF0000) >> 16, \ - ((c) & 0x0000FF00) >> 8, \ - ((c) & 0x000000FF), \ - \ - ((d) & 0xFF000000) >> 24, \ - ((d) & 0x00FF0000) >> 16, \ - ((d) & 0x0000FF00) >> 8, \ - ((d) & 0x000000FF), \ +# define V3_ID(a, b, c, d) { \ + ((a) & 0xFF000000) >> 24, \ + ((a) & 0x00FF0000) >> 16, \ + ((a) & 0x0000FF00) >> 8, \ + ((a) & 0x000000FF), \ + \ + ((b) & 0xFF000000) >> 24, \ + ((b) & 0x00FF0000) >> 16, \ + ((b) & 0x0000FF00) >> 8, \ + ((b) & 0x000000FF), \ + \ + ((c) & 0xFF000000) >> 24, \ + ((c) & 0x00FF0000) >> 16, \ + ((c) & 0x0000FF00) >> 8, \ + ((c) & 0x000000FF), \ + \ + ((d) & 0xFF000000) >> 24, \ + ((d) & 0x00FF0000) >> 16, \ + ((d) & 0x0000FF00) >> 8, \ + ((d) & 0x000000FF), \ } #endif // V3_COM_COMPAT diff --git a/distrho/src/travesty/bstream.h b/distrho/src/travesty/bstream.h index e00172ff..b997ff32 100644 --- a/distrho/src/travesty/bstream.h +++ b/distrho/src/travesty/bstream.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/component.h b/distrho/src/travesty/component.h index 39e63907..a354545b 100644 --- a/distrho/src/travesty/component.h +++ b/distrho/src/travesty/component.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index a516664a..bb52d964 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/events.h b/distrho/src/travesty/events.h index 00d28163..47d807d0 100644 --- a/distrho/src/travesty/events.h +++ b/distrho/src/travesty/events.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index 00a9c45f..b8c2f123 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index 3fe8b717..98bc9cc5 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -1,5 +1,5 @@ /* - * travesty, pure C interface to steinberg VST3 SDK + * travesty, pure C VST3-compatible interface * Copyright (C) 2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with @@ -68,7 +68,7 @@ struct v3_plugin_view { (void *self, v3_bool state); V3_API v3_result (*set_frame) - (void *self, struct v3_plug_frame *); + (void *self, struct v3_plugin_frame *); V3_API v3_result (*can_resize)(void *self); V3_API v3_result (*check_size_constraint) (void *self, struct v3_view_rect *); @@ -81,7 +81,7 @@ struct v3_plugin_frame { struct v3_funknown; V3_API v3_result (*resize_view) - (void *self, struct v3_plug_view *, struct v3_view_rect *); + (void *self, struct v3_plugin_view *, struct v3_view_rect *); }; static const v3_tuid v3_plugin_frame_iid = @@ -106,12 +106,12 @@ static const v3_tuid v3_plugin_view_content_scale_steinberg_iid = * support for querying the view to find what control is underneath the mouse */ -struct v3_plugin_view_param_finder { +struct v3_plugin_view_parameter_finder { struct v3_funknown; V3_API v3_result (*find_parameter) (void *self, int32_t x, int32_t y, v3_param_id *); }; -static const v3_tuid v3_plugin_view_param_finder_iid = +static const v3_tuid v3_plugin_view_parameter_finder_iid = V3_ID(0x0F618302, 0x215D4587, 0xA512073C, 0x77B9D383); From f462f60dfeb5ba6df1bcd99588e4009dcb9599c7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 22 Sep 2021 23:31:37 +0100 Subject: [PATCH 055/504] Add the needed bits for v3_plugin_factory_3 Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 86700742..a2fe1aad 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -798,16 +798,16 @@ struct v3_plugin_factory_cpp : v3_funknown { v3_plugin_factory_3 v3; }; -std::list components; - struct dpf_factory : v3_plugin_factory_cpp { + std::list components; + dpf_factory() { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, v3_plugin_factory_iid, - v3_plugin_factory_2_iid /*, - v3_plugin_factory_3_iid */ + v3_plugin_factory_2_iid, + v3_plugin_factory_3_iid }; // ------------------------------------------------------------------------------------------------------------ @@ -880,9 +880,10 @@ struct dpf_factory : v3_plugin_factory_cpp { DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); + dpf_factory* const factory = *(dpf_factory**)self; dpf_component* const component = new dpf_component(); - components.push_back(component); - *instance = &components.back(); + factory->components.push_back(component); + *instance = &factory->components.back(); return V3_OK; }; @@ -892,12 +893,11 @@ struct dpf_factory : v3_plugin_factory_cpp { v2.get_class_info_2 = []V3_API(void* self, int32_t idx, struct v3_class_info_2* info) -> v3_result { d_stdout("dpf_factory::get_class_info_2 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); - // get_class_info memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo->getName(), sizeof(info->name)); - // get_class_info_2 + info->class_flags = 0; DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); @@ -912,13 +912,23 @@ struct dpf_factory : v3_plugin_factory_cpp { v3.get_class_info_utf16 = []V3_API(void* self, int32_t idx, struct v3_class_info_3* info) -> v3_result { d_stdout("dpf_factory::get_class_info_utf16 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); - return V3_INTERNAL_ERR; + memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + info->cardinality = 0x7FFFFFFF; + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); + DISTRHO_NAMESPACE::strncpy_16from8(info->name, gPluginInfo->getName(), sizeof(info->name)); + + info->class_flags = 0; + DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO + DISTRHO_NAMESPACE::strncpy_16from8(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); + // DISTRHO_NAMESPACE::snprintf16(info->version, sizeof(info->version)/sizeof(info->version[0]), "%u", gPluginInfo->getVersion()); // TODO + DISTRHO_NAMESPACE::strncpy_16from8(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? + return V3_OK; }; v3.set_host_context = []V3_API (void* self, struct v3_funknown* host) -> v3_result { d_stdout("dpf_factory::set_host_context => %s | %p %p", __PRETTY_FUNCTION__ + 37, self, host); - return V3_INTERNAL_ERR; + return V3_NOT_IMPLEMENTED; }; } }; From bea070111cd3809d2a6f7d25fbfae25408e1367f Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Sep 2021 12:35:52 +0100 Subject: [PATCH 056/504] More VST3 tweaks Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 234 +++++++++++++++++-------- distrho/src/travesty/edit_controller.h | 12 +- 3 files changed, 169 insertions(+), 79 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 8494abc5..827a3055 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1066,7 +1066,7 @@ public: void vst_setParameter(const int32_t index, const float value) { - const uint32_t hints(fPlugin.getParameterHints(index)); + const uint32_t hints = fPlugin.getParameterHints(index); const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); // TODO figure out how to detect kVstParameterUsesIntegerMinMax host support, and skip normalization diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index a2fe1aad..ba10eb47 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -22,7 +22,8 @@ #include "travesty/edit_controller.h" #include "travesty/factory.h" -#include +#include +#include START_NAMESPACE_DISTRHO @@ -158,7 +159,7 @@ void strncpy_16from8(int16_t* const dst, const char* const src, const size_t siz } } -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- class PluginVst3 { @@ -168,6 +169,85 @@ public: { } + // ---------------------------------------------------------------------------------------------------------------- + // stuff for vst3 interfaces + + uint32_t getParameterCount() const noexcept + { + return fPlugin.getParameterCount(); + } + + void getParameterInfo(const uint32_t index, v3_param_info* const info) const noexcept + { + // set up flags + int32_t flags = 0; + + const auto desig = fPlugin.getParameterDesignation(index); + const auto hints = fPlugin.getParameterHints(index); + + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + + switch (desig) + { + case kParameterDesignationNull: + break; + case kParameterDesignationBypass: + flags |= V3_PARAM_IS_BYPASS; + break; + } + + if (hints & kParameterIsAutomable) + flags |= V3_PARAM_CAN_AUTOMATE; + if (hints & kParameterIsOutput) + flags |= V3_PARAM_READ_ONLY; + // TODO V3_PARAM_IS_LIST + + // set up step_count + int32_t step_count = 0; + + if (hints & kParameterIsBoolean) + step_count = 1; + if ((hints & kParameterIsInteger) && ranges.max - ranges.min > 1) + step_count = ranges.max - ranges.min - 1; + + std::memset(info, 0, sizeof(v3_param_info)); + info->param_id = index; + info->flags = flags; + info->step_count = step_count; + info->default_normalised_value = ranges.getNormalizedValue(ranges.def); + // int32_t unit_id; + strncpy_16from8(info->title, fPlugin.getParameterName(index), 128); + strncpy_16from8(info->short_title, fPlugin.getParameterShortName(index), 128); + strncpy_16from8(info->units, fPlugin.getParameterUnit(index), 128); + } + + double getNormalizedParameterValue(const uint32_t index) + { + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + return ranges.getNormalizedValue(fPlugin.getParameterValue(index)); + } + + void setNormalizedParameterValue(const uint32_t index, const double value) + { + const uint32_t hints = fPlugin.getParameterHints(index); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + + float realValue = ranges.getUnnormalizedValue(value); + + if (hints & kParameterIsBoolean) + { + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + realValue = realValue > midRange ? ranges.max : ranges.min; + } + + if (hints & kParameterIsInteger) + { + realValue = std::round(realValue); + } + + fPlugin.setParameterValue(index, realValue); + } + private: // Plugin PluginExporter fPlugin; @@ -343,7 +423,10 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { - dpf_edit_controller() + ScopedPointer& vst3; + + dpf_edit_controller(ScopedPointer& v) + : vst3(v) { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, @@ -423,66 +506,34 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_count = []V3_API(void* self) -> int32_t { d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 53, self); - return gPluginInfo->getParameterCount(); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); + + PluginVst3* const vst3 = controller->vst3.get(); + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + return vst3->getParameterCount(); }; controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result { d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 53, self, param_idx); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(param_idx >= 0, V3_INVALID_ARG); - // set up flags - int32_t flags = 0; - - const auto desig = gPluginInfo->getParameterDesignation(param_idx); - const auto hints = gPluginInfo->getParameterHints(param_idx); - - const ParameterRanges& ranges(gPluginInfo->getParameterRanges(param_idx)); - - switch (desig) - { - case kParameterDesignationNull: - break; - case kParameterDesignationBypass: - flags |= V3_PARAM_IS_BYPASS; - break; - } - - if (hints & kParameterIsAutomable) - flags |= V3_PARAM_CAN_AUTOMATE; - if (hints & kParameterIsOutput) - flags |= V3_PARAM_READ_ONLY; - // TODO V3_PARAM_IS_LIST - - // set up step_count - int32_t step_count = 0; - - if (hints & kParameterIsBoolean) - step_count = 1; - if ((hints & kParameterIsInteger) && ranges.max - ranges.min > 1) - step_count = ranges.max - ranges.min - 1; - - std::memset(param_info, 0, sizeof(v3_param_info)); - param_info->param_id = param_idx; - param_info->flags = flags; - param_info->step_count = step_count; - param_info->default_normalised_value = ranges.getNormalizedValue(ranges.def); - // int32_t unit_id; - strncpy_16from8(param_info->title, gPluginInfo->getParameterName(param_idx), 128); - strncpy_16from8(param_info->short_title, gPluginInfo->getParameterShortName(param_idx), 128); - strncpy_16from8(param_info->units, gPluginInfo->getParameterUnit(param_idx), 128); + PluginVst3* const vst3 = controller->vst3.get(); + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - /* - v3_str_128 title; - v3_str_128 short_title; - v3_str_128 units; - */ + const uint32_t uidx = static_cast(param_idx); + DISTRHO_SAFE_ASSERT_RETURN(uidx < vst3->getParameterCount(), V3_INVALID_ARG); + vst3->getParameterInfo(uidx, param_info); return V3_OK; }; - controller.get_param_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result + controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { - d_stdout("dpf_edit_controller::get_param_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::get_parameter_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); const ParameterRanges& ranges(gPluginInfo->getParameterRanges(index)); const float realvalue = ranges.getUnnormalizedValue(normalised); @@ -492,40 +543,55 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_OK; }; - controller.get_param_value_for_string = []V3_API(void* self, v3_param_id, int16_t* input, double* output) -> v3_result + controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id, int16_t* input, double* output) -> v3_result { - d_stdout("dpf_edit_controller::get_param_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 53, self, input, output); + d_stdout("dpf_edit_controller::get_parameter_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 53, self, input, output); return V3_NOT_IMPLEMENTED; }; - controller.normalised_param_to_plain = []V3_API(void* self, v3_param_id, double normalised) -> double + controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id, double normalised) -> double { - d_stdout("dpf_edit_controller::normalised_param_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); return normalised; }; - controller.plain_param_to_normalised = []V3_API(void* self, v3_param_id, double normalised) -> double + controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id, double normalised) -> double { - d_stdout("dpf_edit_controller::plain_param_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); return normalised; }; - controller.get_param_normalised = []V3_API(void* self, v3_param_id) -> double + controller.get_parameter_normalised = []V3_API(void* self, v3_param_id param_idx) -> double { - d_stdout("dpf_edit_controller::get_param_normalised => %s | %p", __PRETTY_FUNCTION__ + 53, self); - return 0.0; + d_stdout("dpf_edit_controller::get_parameter_normalised => %s | %p", __PRETTY_FUNCTION__ + 53, self); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); + + PluginVst3* const vst3 = controller->vst3.get(); + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0); + DISTRHO_SAFE_ASSERT_RETURN(param_idx < vst3->getParameterCount(), 0.0); + + return vst3->getNormalizedParameterValue(param_idx); }; - controller.set_param_normalised = []V3_API(void* self, v3_param_id, double normalised) -> v3_result + controller.set_parameter_normalised = []V3_API(void* self, v3_param_id param_idx, double normalised) -> v3_result { - d_stdout("dpf_edit_controller::set_param_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::set_parameter_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); + + PluginVst3* const vst3 = controller->vst3.get(); + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(param_idx < vst3->getParameterCount(), V3_INVALID_ARG); + + vst3->setNormalizedParameterValue(param_idx, normalised); return V3_OK; }; controller.set_component_handler = []V3_API(void* self, v3_component_handler**) -> v3_result { d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 53, self); - return V3_OK; + return V3_NOT_IMPLEMENTED; }; controller.create_view = []V3_API(void* self, const char* name) -> v3_plug_view** @@ -615,9 +681,13 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return 0; }; - processor.setup_processing = []V3_API(void* self, v3_process_setup*) -> v3_result + processor.setup_processing = []V3_API(void* self, v3_process_setup* setup) -> v3_result { d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 53, self); + + d_lastBufferSize = setup->max_block_size; + d_lastSampleRate = setup->sample_rate; + return V3_OK; }; @@ -650,10 +720,13 @@ struct v3_component_cpp : v3_funknown { }; struct dpf_component : v3_component_cpp { - dpf_audio_processor* processor = nullptr; - dpf_edit_controller* controller = nullptr; + std::atomic refcounter; + ScopedPointer processor; + ScopedPointer controller; + ScopedPointer vst3; dpf_component() + : refcounter(1) { static const uint8_t* kSupportedBaseFactories[] = { v3_funknown_iid, @@ -684,7 +757,7 @@ struct dpf_component : v3_component_cpp { dpf_component* const component = *(dpf_component**)self; if (component->processor == nullptr) component->processor = new dpf_audio_processor(); - *iface = (v3_funknown*)&component->processor; + *iface = &component->processor; return V3_OK; } @@ -692,8 +765,8 @@ struct dpf_component : v3_component_cpp { { dpf_component* const component = *(dpf_component**)self; if (component->controller == nullptr) - component->controller = new dpf_edit_controller(); - *iface = (v3_funknown*)&component->controller; + component->controller = new dpf_edit_controller(component->vst3); + *iface = &component->controller; return V3_OK; } @@ -704,27 +777,44 @@ struct dpf_component : v3_component_cpp { ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_component::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return 1; + dpf_component* const component = *(dpf_component**)self; + return ++component->refcounter; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_component::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + dpf_component* const component = *(dpf_component**)self; + if (const int refcounter = --component->refcounter) + return refcounter; + // delete component; + *(dpf_component**)self = nullptr; return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base - base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result + base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown* context) -> v3_result { d_stdout("dpf_component::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 41, self, context); + dpf_component* const component = *(dpf_component**)self; + + // default early values + if (d_lastBufferSize == 0) + d_lastBufferSize = 2048; + if (d_lastSampleRate <= 0.0) + d_lastSampleRate = 44100.0; + + component->vst3 = new PluginVst3(); return V3_OK; }; base.terminate = []V3_API(void* self) -> v3_result { d_stdout("dpf_component::terminate => %s | %p", __PRETTY_FUNCTION__ + 41, self); + dpf_component* const component = *(dpf_component**)self; + component->vst3 = nullptr; return V3_OK; }; @@ -799,7 +889,7 @@ struct v3_plugin_factory_cpp : v3_funknown { }; struct dpf_factory : v3_plugin_factory_cpp { - std::list components; + std::vector components; dpf_factory() { diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index bb52d964..23486db7 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -86,18 +86,18 @@ struct v3_edit_controller { V3_API v3_result (*get_parameter_info) (void *self, int32_t param_idx, struct v3_param_info *); - V3_API v3_result (*get_param_string_for_value) + V3_API v3_result (*get_parameter_string_for_value) (void *self, v3_param_id, double normalised, v3_str_128 output); - V3_API v3_result (*get_param_value_for_string) + V3_API v3_result (*get_parameter_value_for_string) (void *self, v3_param_id, int16_t *input, double *output); - V3_API double (*normalised_param_to_plain) + V3_API double (*normalised_parameter_to_plain) (void *self, v3_param_id, double normalised); - V3_API double (*plain_param_to_normalised) + V3_API double (*plain_parameter_to_normalised) (void *self, v3_param_id, double normalised); - V3_API double (*get_param_normalised)(void *self, v3_param_id); - V3_API v3_result (*set_param_normalised) + V3_API double (*get_parameter_normalised)(void *self, v3_param_id); + V3_API v3_result (*set_parameter_normalised) (void *self, v3_param_id, double normalised); V3_API v3_result (*set_component_handler) From e4c69d2d652c9402b997e785c0f1ad1123dac0cb Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Sep 2021 17:34:02 +0100 Subject: [PATCH 057/504] More work towards VST3, main class starts to take shape Signed-off-by: falkTX --- Makefile.plugins.mk | 2 +- distrho/DistrhoPlugin.hpp | 4 +- distrho/src/DistrhoPlugin.cpp | 4 +- distrho/src/DistrhoPluginInternal.hpp | 19 +- distrho/src/DistrhoPluginVST3.cpp | 542 +++++++++++++++++++++++++- 5 files changed, 554 insertions(+), 17 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index d4f69a5a..a5f11159 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -392,7 +392,7 @@ endif vst3: $(vst3) -$(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o +$(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) @echo "Creating VST3 plugin for $(NAME)" $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 048fb93d..34bfc857 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -40,7 +40,9 @@ START_NAMESPACE_DISTRHO static const uint32_t kAudioPortIsCV = 0x1; /** - Audio port should be used as sidechan (LV2 only). + Audio port should be used as sidechan (LV2 and VST3 only). + This hint should not be used with CV style ports. + @note non-sidechain audio ports must exist in the plugin if this flag is set. */ static const uint32_t kAudioPortIsSidechain = 0x2; diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index c517abfc..693c4913 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -29,7 +29,7 @@ bool d_lastCanRequestParameterValueChanges = false; * Static fallback data, see DistrhoPluginInternal.hpp */ const String PluginExporter::sFallbackString; -const AudioPort PluginExporter::sFallbackAudioPort; +/* */ AudioPortWithBusId PluginExporter::sFallbackAudioPort; const ParameterRanges PluginExporter::sFallbackRanges; const ParameterEnumerationValues PluginExporter::sFallbackEnumValues; const PortGroupWithId PluginExporter::sFallbackPortGroup; @@ -41,7 +41,7 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou : pData(new PrivateData()) { #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - pData->audioPorts = new AudioPort[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; + pData->audioPorts = new AudioPortWithBusId[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; #endif #ifdef DPF_ABORT_ON_ERROR diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index f46e5e8e..7d2a89d8 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -44,6 +44,14 @@ typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, floa // ----------------------------------------------------------------------- // Helpers +struct AudioPortWithBusId : AudioPort { + uint32_t busId; + + AudioPortWithBusId() + : AudioPort(), + busId(0) {} +}; + struct PortGroupWithId : PortGroup { uint32_t groupId; @@ -78,7 +86,7 @@ struct Plugin::PrivateData { bool isProcessing; #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - AudioPort* audioPorts; + AudioPortWithBusId* audioPorts; #endif uint32_t parameterCount; @@ -463,7 +471,7 @@ public: #endif #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - const AudioPort& getAudioPort(const bool input, const uint32_t index) const noexcept + AudioPortWithBusId& getAudioPort(const bool input, const uint32_t index) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackAudioPort); @@ -482,6 +490,11 @@ public: return fData->audioPorts[index + (input ? 0 : DISTRHO_PLUGIN_NUM_INPUTS)]; } + + uint32_t getAudioPortHints(const bool input, const uint32_t index) const noexcept + { + return getAudioPort(input, index).hints; + } #endif uint32_t getParameterCount() const noexcept @@ -882,7 +895,7 @@ private: // Static fallback data, see DistrhoPlugin.cpp static const String sFallbackString; - static const AudioPort sFallbackAudioPort; + static /* */ AudioPortWithBusId sFallbackAudioPort; static const ParameterRanges sFallbackRanges; static const ParameterEnumerationValues sFallbackEnumValues; static const PortGroupWithId sFallbackPortGroup; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index ba10eb47..884c71ac 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -25,6 +25,21 @@ #include #include +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI +# include "DistrhoUIInternal.hpp" +# include "../extra/RingBuffer.hpp" +#endif + START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- @@ -161,16 +176,449 @@ void strncpy_16from8(int16_t* const dst, const char* const src, const size_t siz // -------------------------------------------------------------------------------------------------------------------- +struct ParameterAndNotesHelper +{ + float* parameterValues; +#if DISTRHO_PLUGIN_HAS_UI + bool* parameterChecks; +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + SmallStackBuffer notesRingBuffer; +# endif +#endif + + ParameterAndNotesHelper() + : parameterValues(nullptr) +#if DISTRHO_PLUGIN_HAS_UI + , parameterChecks(nullptr) +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + , notesRingBuffer(StackBuffer_INIT) +# endif +#endif + { +#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) + std::memset(¬esRingBuffer, 0, sizeof(notesRingBuffer)); +#endif + } + + virtual ~ParameterAndNotesHelper() + { + if (parameterValues != nullptr) + { + delete[] parameterValues; + parameterValues = nullptr; + } +#if DISTRHO_PLUGIN_HAS_UI + if (parameterChecks != nullptr) + { + delete[] parameterChecks; + parameterChecks = nullptr; + } +#endif + } + +#if DISTRHO_PLUGIN_WANT_STATE + virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0; +#endif +}; + +// -------------------------------------------------------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_HAS_UI + +#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static const sendNoteFunc sendNoteCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_STATE +static const setStateFunc setStateCallback = nullptr; +#endif + +class UIVst3 +{ +public: + UIVst3(ParameterAndNotesHelper* const uiHelper, + PluginExporter* const plugin, + const intptr_t winId, const float scaleFactor) + : fUiHelper(uiHelper), + fPlugin(plugin), + fUI(this, winId, plugin->getSampleRate(), + editParameterCallback, + setParameterCallback, + setStateCallback, + sendNoteCallback, + setSizeCallback, + nullptr, // TODO file request + nullptr, + plugin->getInstancePointer(), + scaleFactor) +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + , fNotesRingBuffer() +# endif + { +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + fNotesRingBuffer.setRingBuffer(&uiHelper->notesRingBuffer, false); +# endif + } + + // ------------------------------------------------------------------- + + void idle() + { + for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) + { + if (fUiHelper->parameterChecks[i]) + { + fUiHelper->parameterChecks[i] = false; + fUI.parameterChanged(i, fUiHelper->parameterValues[i]); + } + } + + fUI.plugin_idle(); + } + + int16_t getWidth() const + { + return fUI.getWidth(); + } + + int16_t getHeight() const + { + return fUI.getHeight(); + } + + double getScaleFactor() const + { + return fUI.getScaleFactor(); + } + + // ------------------------------------------------------------------- + +protected: + void editParameter(const uint32_t /*index*/, const bool /*started*/) const + { + // hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index); + } + + void setParameterValue(const uint32_t index, const float realValue) + { + // const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); + // const float perValue(ranges.getNormalizedValue(realValue)); + + fPlugin->setParameterValue(index, realValue); + // hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); + } + + void setSize(uint /*width*/, uint /*height*/) + { +// # ifdef DISTRHO_OS_MAC +// const double scaleFactor = fUI.getScaleFactor(); +// width /= scaleFactor; +// height /= scaleFactor; +// # endif + // hostCallback(audioMasterSizeWindow, width, height); + } + +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + uint8_t midiData[3]; + midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; + midiData[1] = note; + midiData[2] = velocity; + fNotesRingBuffer.writeCustomData(midiData, 3); + fNotesRingBuffer.commitWrite(); + } +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* const key, const char* const value) + { + fUiHelper->setStateFromUI(key, value); + } +# endif + +private: + // Vst3 stuff + ParameterAndNotesHelper* const fUiHelper; + PluginExporter* const fPlugin; + + // Plugin UI + UIExporter fUI; +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + RingBufferControl fNotesRingBuffer; +# endif + + // ------------------------------------------------------------------- + // Callbacks + + #define handlePtr ((UIVst3*)ptr) + + static void editParameterCallback(void* ptr, uint32_t index, bool started) + { + handlePtr->editParameter(index, started); + } + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + handlePtr->setParameterValue(rindex, value); + } + + static void setSizeCallback(void* ptr, uint width, uint height) + { + handlePtr->setSize(width, height); + } + +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + handlePtr->sendNote(channel, note, velocity); + } +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + static void setStateCallback(void* ptr, const char* key, const char* value) + { + handlePtr->setState(key, value); + } +# endif + + #undef handlePtr +}; + +#endif // DISTRHO_PLUGIN_HAS_UI + +// -------------------------------------------------------------------------------------------------------------------- + class PluginVst3 { + /* buses: we provide 1 for the main audio (if there is any) plus 1 for each sidechain or cv port. + * Main audio comes first, if available. + * Then sidechain, also if available. + * And finally each CV port individually. + * + * MIDI will have a single bus, nothing special there. + */ + struct BusInfo { + uint8_t audio = 0; // either 0 or 1 + uint8_t sidechain = 0; // either 0 or 1 + uint32_t numMainAudio = 0; + uint32_t numSidechain = 0; + uint32_t numCV = 0; + } inputBuses, outputBuses; + public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback) { +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; i 0 + for (uint32_t i=0; i 0 + int32_t getAudioBusCount(const bool isInput) const noexcept + { + if (isInput) + return inputBuses.audio + inputBuses.sidechain + inputBuses.numCV; + else + return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV; + }; + + v3_result getBusAudioBusInfo(const bool isInput, const uint32_t index, v3_bus_info* info) const + { + int32_t channel_count; + v3_bus_types bus_type; + v3_bus_flags flags; + v3_str_128 bus_name; + + if (isInput) + { +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + switch (index) + { + case 0: + if (inputBuses.audio) + { + channel_count = inputBuses.numMainAudio; + bus_type = V3_MAIN; + flags = V3_DEFAULT_ACTIVE; + break; + } + // fall-through + case 1: + if (inputBuses.sidechain) + { + channel_count = inputBuses.numSidechain; + bus_type = V3_AUX; + flags = v3_bus_flags(0); + break; + } + // fall-through + default: + channel_count = 1; + bus_type = V3_AUX; + flags = V3_IS_CONTROL_VOLTAGE; + break; + } + + if (bus_type == V3_MAIN) + { + strncpy_16from8(info->bus_name, "Audio Input", 128); + } + else + { + for (uint32_t i=0; ibus_name, port.name, 128); + break; + } + } + } +#else + return V3_INVALID_ARG; +#endif + } + else + { +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + switch (index) + { + case 0: + if (outputBuses.audio) + { + channel_count = outputBuses.numMainAudio; + bus_type = V3_MAIN; + flags = V3_DEFAULT_ACTIVE; + break; + } + // fall-through + case 1: + if (outputBuses.sidechain) + { + channel_count = outputBuses.numSidechain; + bus_type = V3_AUX; + flags = v3_bus_flags(0); + break; + } + // fall-through + default: + channel_count = 1; + bus_type = V3_AUX; + flags = V3_IS_CONTROL_VOLTAGE; + break; + } + + if (bus_type == V3_MAIN) + { + strncpy_16from8(info->bus_name, "Audio Output", 128); + } + else + { + for (uint32_t i=0; ibus_name, port.name, 128); + break; + } + } + } +#else + return V3_INVALID_ARG; +#endif + } + + info->media_type = V3_AUDIO; + info->direction = isInput ? V3_INPUT : V3_OUTPUT; + info->channel_count = channel_count; + std::memcpy(info->bus_name, bus_name, sizeof(bus_name)); + info->bus_type = bus_type; + info->flags = flags; + return V3_OK; + } +#endif + + void setActive(const bool active) + { + if (active) + fPlugin.activate(); + else + fPlugin.deactivate(); } // ---------------------------------------------------------------------------------------------------------------- - // stuff for vst3 interfaces + // v3_edit_controller interface calls uint32_t getParameterCount() const noexcept { @@ -291,11 +739,14 @@ static ScopedPointer gPluginInfo; // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view +#if DISTRHO_PLUGIN_HAS_UI struct v3_plugin_view_cpp : v3_funknown { v3_plugin_view view; }; struct dpf_plugin_view : v3_plugin_view_cpp { + ScopedPointer vst3; + dpf_plugin_view() { static const uint8_t* kSupportedFactories[] = { @@ -413,6 +864,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { }; } }; +#endif // -------------------------------------------------------------------------------------------------------------------- // dpf_edit_controller @@ -830,51 +1282,121 @@ struct dpf_component : v3_component_cpp { comp.set_io_mode = []V3_API(void* self, int32_t io_mode) -> v3_result { d_stdout("dpf_component::set_io_mode => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, io_mode); - return V3_INTERNAL_ERR; + return V3_OK; }; comp.get_bus_count = []V3_API(void* self, int32_t media_type, int32_t bus_direction) -> int32_t { + // NOTE runs during RT d_stdout("dpf_component::get_bus_count => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction); + + switch (media_type) + { + case V3_AUDIO: +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + if (bus_direction == V3_INPUT || bus_direction == V3_OUTPUT) + { + dpf_component* const component = *(dpf_component**)self; + return component->vst3->getAudioBusCount(bus_direction == V3_INPUT); + } +#endif + break; + + case V3_EVENT: +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (bus_direction == V3_INPUT) + return 1; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (bus_direction == V3_OUTPUT) + return 1; +#endif + break; + } + return 0; }; comp.get_bus_info = []V3_API(void* self, int32_t media_type, int32_t bus_direction, - int32_t bus_idx, v3_bus_info* bus_info) -> v3_result + int32_t bus_idx, v3_bus_info* info) -> v3_result { - d_stdout("dpf_component::get_bus_info => %s | %p %i %i %i %p", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, bus_info); - return V3_INTERNAL_ERR; + d_stdout("dpf_component::get_bus_info => %s | %p %i %i %i %p", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, info); + DISTRHO_SAFE_ASSERT_INT_RETURN(media_type == V3_AUDIO || media_type == V3_EVENT, media_type, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_INT_RETURN(bus_direction == V3_INPUT || bus_direction == V3_OUTPUT, bus_direction, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_INT_RETURN(bus_idx >= 0, bus_idx, V3_INVALID_ARG); + + if (media_type == V3_AUDIO) + { +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + dpf_component* const component = *(dpf_component**)self; + return component->vst3->getBusAudioBusInfo(bus_direction == V3_INPUT, + static_cast(bus_idx), + info); +#else + return V3_INVALID_ARG; +#endif + } + else + { + if (bus_direction == V3_INPUT) + { +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG); +#else + return V3_INVALID_ARG; +#endif + } + else + { +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG); +#else + return V3_INVALID_ARG; +#endif + } + info->media_type = V3_EVENT; + info->direction = bus_direction; + info->channel_count = 1; + strncpy_16from8(info->bus_name, bus_direction == V3_INPUT ? "Event/MIDI Input" + : "Event/MIDI Output", 128); + info->bus_type = V3_MAIN; + info->flags = V3_DEFAULT_ACTIVE; + return V3_OK; + } }; comp.get_routing_info = []V3_API(void* self, v3_routing_info* input, v3_routing_info* output) -> v3_result { d_stdout("dpf_component::get_routing_info => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, input, output); - return V3_INTERNAL_ERR; + return V3_NOT_IMPLEMENTED; }; comp.activate_bus = []V3_API(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bool state) -> v3_result { d_stdout("dpf_component::activate_bus => %s | %p %i %i %i %u", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, state); - return V3_INTERNAL_ERR; + return V3_OK; }; comp.set_active = []V3_API(void* self, v3_bool state) -> v3_result { d_stdout("dpf_component::set_active => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); - return V3_INTERNAL_ERR; + dpf_component* const component = *(dpf_component**)self; + + component->vst3->setActive(state); + return V3_OK; }; comp.set_state = []V3_API(void* self, v3_bstream**) -> v3_result { d_stdout("dpf_component::set_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return V3_INTERNAL_ERR; + return V3_NOT_IMPLEMENTED; }; comp.get_state = []V3_API(void* self, v3_bstream**) -> v3_result { d_stdout("dpf_component::get_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return V3_INTERNAL_ERR; + return V3_NOT_IMPLEMENTED; }; } }; From 17dde100a09b2522aaa2cdf19ee90d713b97b77e Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Sep 2021 18:29:28 +0100 Subject: [PATCH 058/504] Hook up various v3_audio_processor calls Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 141 +++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 39 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 884c71ac..c9bcf323 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -599,6 +599,7 @@ public: #endif } + std::memset(info, 0, sizeof(v3_bus_info)); info->media_type = V3_AUDIO; info->direction = isInput ? V3_INPUT : V3_OUTPUT; info->channel_count = channel_count; @@ -614,7 +615,7 @@ public: if (active) fPlugin.activate(); else - fPlugin.deactivate(); + fPlugin.deactivateIfNeeded(); } // ---------------------------------------------------------------------------------------------------------------- @@ -696,6 +697,49 @@ public: fPlugin.setParameterValue(index, realValue); } + // ---------------------------------------------------------------------------------------------------------------- + // v3_audio_processor interface calls + +#if DISTRHO_PLUGIN_WANT_LATENCY + uint32_t getLatencySamples() const noexcept + { + return fPlugin.getLatency(); + } +#endif + + void setupProcessing(v3_process_setup* const setup) + { + DISTRHO_SAFE_ASSERT_RETURN(setup->symbolic_sample_size == V3_SAMPLE_32,); + + const bool active = fPlugin.isActive(); + fPlugin.deactivateIfNeeded(); + + // TODO process_mode can be V3_REALTIME, V3_PREFETCH, V3_OFFLINE + + fPlugin.setSampleRate(setup->sample_rate, true); + fPlugin.setBufferSize(setup->max_block_size, true); + + if (active) + fPlugin.activate(); + } + + void setProcessing(const bool processing) + { + if (processing) + fPlugin.activate(); + else + fPlugin.deactivate(); + } + + void process(v3_process_data* const data) + { + DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32,); + + // TODO full thing + } + + // ---------------------------------------------------------------------------------------------------------------- + private: // Plugin PluginExporter fPlugin; @@ -745,9 +789,10 @@ struct v3_plugin_view_cpp : v3_funknown { }; struct dpf_plugin_view : v3_plugin_view_cpp { - ScopedPointer vst3; + ScopedPointer& vst3; - dpf_plugin_view() + dpf_plugin_view(ScopedPointer& v) + : vst3(v) { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, @@ -890,7 +935,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_edit_controller::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 53, self, tuid2str(iid), iface); + d_stdout("dpf_edit_controller::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 97, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -909,13 +954,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 97, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 97, self); return 0; }; @@ -924,13 +969,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp { base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result { - d_stdout("dpf_edit_controller::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 53, self, context); + d_stdout("dpf_edit_controller::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 97, self, context); return V3_OK; }; base.terminate = []V3_API(void* self) -> v3_result { - d_stdout("dpf_edit_controller::terminate => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::terminate => %s | %p", __PRETTY_FUNCTION__ + 97, self); return V3_OK; }; @@ -939,25 +984,25 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_component_state = []V3_API(void* self, v3_bstream*) -> v3_result { - d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); return V3_OK; }; controller.set_state = []V3_API(void* self, v3_bstream*) -> v3_result { - d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); return V3_OK; }; controller.get_state = []V3_API(void* self, v3_bstream*) -> v3_result { - d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); return V3_OK; }; controller.get_parameter_count = []V3_API(void* self) -> int32_t { - d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 97, self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); @@ -968,7 +1013,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 53, self, param_idx); + d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 97, self, param_idx); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(param_idx >= 0, V3_INVALID_ARG); @@ -985,7 +1030,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::get_parameter_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); const ParameterRanges& ranges(gPluginInfo->getParameterRanges(index)); const float realvalue = ranges.getUnnormalizedValue(normalised); @@ -997,25 +1042,25 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id, int16_t* input, double* output) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 53, self, input, output); + d_stdout("dpf_edit_controller::get_parameter_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 97, self, input, output); return V3_NOT_IMPLEMENTED; }; controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id, double normalised) -> double { - d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); return normalised; }; controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id, double normalised) -> double { - d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); return normalised; }; controller.get_parameter_normalised = []V3_API(void* self, v3_param_id param_idx) -> double { - d_stdout("dpf_edit_controller::get_parameter_normalised => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::get_parameter_normalised => %s | %p", __PRETTY_FUNCTION__ + 97, self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); @@ -1028,7 +1073,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_parameter_normalised = []V3_API(void* self, v3_param_id param_idx, double normalised) -> v3_result { - d_stdout("dpf_edit_controller::set_parameter_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); + d_stdout("dpf_edit_controller::set_parameter_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); @@ -1042,13 +1087,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_component_handler = []V3_API(void* self, v3_component_handler**) -> v3_result { - d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 97, self); return V3_NOT_IMPLEMENTED; }; controller.create_view = []V3_API(void* self, const char* name) -> v3_plug_view** { - d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 53, self, name); + d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 97, self, name); return nullptr; }; } @@ -1062,7 +1107,10 @@ struct v3_audio_processor_cpp : v3_funknown { }; struct dpf_audio_processor : v3_audio_processor_cpp { - dpf_audio_processor() + ScopedPointer& vst3; + + dpf_audio_processor(ScopedPointer& v) + : vst3(v) { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, @@ -1074,7 +1122,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_audio_processor::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 53, self, tuid2str(iid), iface); + d_stdout("dpf_audio_processor::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 97, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -1093,13 +1141,13 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 97, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 97, self); return 0; }; @@ -1110,54 +1158,69 @@ struct dpf_audio_processor : v3_audio_processor_cpp { v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result { - d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 53, self, inputs, num_inputs, outputs, num_outputs); - return V3_OK; + d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 97, self, inputs, num_inputs, outputs, num_outputs); + return V3_NOT_IMPLEMENTED; }; processor.get_bus_arrangement = []V3_API(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*) -> v3_result { - d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i", __PRETTY_FUNCTION__ + 53, self, bus_direction, idx); - return V3_OK; + d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i", __PRETTY_FUNCTION__ + 97, self, bus_direction, idx); + return V3_NOT_IMPLEMENTED; }; processor.can_process_sample_size = []V3_API(void* self, int32_t symbolic_sample_size) -> v3_result { - d_stdout("dpf_audio_processor::can_process_sample_size => %s | %p %i", __PRETTY_FUNCTION__ + 53, self, symbolic_sample_size); - return V3_OK; + d_stdout("dpf_audio_processor::can_process_sample_size => %s | %p %i", __PRETTY_FUNCTION__ + 97, self, symbolic_sample_size); + return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED; }; processor.get_latency_samples = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 97, self); +#if DISTRHO_PLUGIN_WANT_LATENCY + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + return processor->vst3->getLatencySamples(); +#else return 0; +#endif }; processor.setup_processing = []V3_API(void* self, v3_process_setup* setup) -> v3_result { - d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 53, self); - + d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 97, self); d_lastBufferSize = setup->max_block_size; d_lastSampleRate = setup->sample_rate; + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(setup->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); + processor->vst3->setupProcessing(setup); return V3_OK; }; processor.set_processing = []V3_API(void* self, v3_bool state) -> v3_result { - d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 53, self, state); + d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 97, self, state); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + processor->vst3->setProcessing(state); return V3_OK; }; - processor.process = []V3_API(void* self, v3_process_data*) -> v3_result + processor.process = []V3_API(void* self, v3_process_data* data) -> v3_result { - d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 97, self); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + processor->vst3->process(data); return V3_OK; }; processor.get_tail_samples = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 53, self); + d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 97, self); return 0; }; } @@ -1208,7 +1271,7 @@ struct dpf_component : v3_component_cpp { { dpf_component* const component = *(dpf_component**)self; if (component->processor == nullptr) - component->processor = new dpf_audio_processor(); + component->processor = new dpf_audio_processor(component->vst3); *iface = &component->processor; return V3_OK; } From d704e0c82868ad29ca7fb226c96edb80ab6c764b Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Sep 2021 19:26:41 +0100 Subject: [PATCH 059/504] Continue VST3 testing, UI shows up Signed-off-by: falkTX --- Makefile.plugins.mk | 9 +- distrho/src/DistrhoPluginVST3.cpp | 519 ++++++++++++++++--------- distrho/src/DistrhoUIPrivateData.hpp | 2 +- distrho/src/travesty/edit_controller.h | 2 +- 4 files changed, 342 insertions(+), 190 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index a5f11159..38df652f 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -392,10 +392,15 @@ endif vst3: $(vst3) -$(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) +ifeq ($(HAVE_DGL),true) +$(vst3): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) +else +$(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o +endif -@mkdir -p $(shell dirname $@) @echo "Creating VST3 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ + # NOTE TESTING -lpthread is for testing, remove later + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -lpthread $(SYMBOLS_VST3) -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index c9bcf323..17a3aad4 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -25,6 +25,9 @@ #include #include +// TESTING awful idea dont reuse +#include "../extra/Thread.hpp" + #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI # undef DISTRHO_PLUGIN_HAS_UI # define DISTRHO_PLUGIN_HAS_UI 0 @@ -35,6 +38,9 @@ # define DISTRHO_PLUGIN_HAS_UI 0 #endif +#undef DISTRHO_PLUGIN_HAS_UI +#define DISTRHO_PLUGIN_HAS_UI 1 + #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" # include "../extra/RingBuffer.hpp" @@ -223,171 +229,6 @@ struct ParameterAndNotesHelper // -------------------------------------------------------------------------------------------------------------------- -#if DISTRHO_PLUGIN_HAS_UI - -#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT -static const sendNoteFunc sendNoteCallback = nullptr; -#endif -#if ! DISTRHO_PLUGIN_WANT_STATE -static const setStateFunc setStateCallback = nullptr; -#endif - -class UIVst3 -{ -public: - UIVst3(ParameterAndNotesHelper* const uiHelper, - PluginExporter* const plugin, - const intptr_t winId, const float scaleFactor) - : fUiHelper(uiHelper), - fPlugin(plugin), - fUI(this, winId, plugin->getSampleRate(), - editParameterCallback, - setParameterCallback, - setStateCallback, - sendNoteCallback, - setSizeCallback, - nullptr, // TODO file request - nullptr, - plugin->getInstancePointer(), - scaleFactor) -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - , fNotesRingBuffer() -# endif - { -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - fNotesRingBuffer.setRingBuffer(&uiHelper->notesRingBuffer, false); -# endif - } - - // ------------------------------------------------------------------- - - void idle() - { - for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) - { - if (fUiHelper->parameterChecks[i]) - { - fUiHelper->parameterChecks[i] = false; - fUI.parameterChanged(i, fUiHelper->parameterValues[i]); - } - } - - fUI.plugin_idle(); - } - - int16_t getWidth() const - { - return fUI.getWidth(); - } - - int16_t getHeight() const - { - return fUI.getHeight(); - } - - double getScaleFactor() const - { - return fUI.getScaleFactor(); - } - - // ------------------------------------------------------------------- - -protected: - void editParameter(const uint32_t /*index*/, const bool /*started*/) const - { - // hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index); - } - - void setParameterValue(const uint32_t index, const float realValue) - { - // const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); - // const float perValue(ranges.getNormalizedValue(realValue)); - - fPlugin->setParameterValue(index, realValue); - // hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); - } - - void setSize(uint /*width*/, uint /*height*/) - { -// # ifdef DISTRHO_OS_MAC -// const double scaleFactor = fUI.getScaleFactor(); -// width /= scaleFactor; -// height /= scaleFactor; -// # endif - // hostCallback(audioMasterSizeWindow, width, height); - } - -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) - { - uint8_t midiData[3]; - midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; - midiData[1] = note; - midiData[2] = velocity; - fNotesRingBuffer.writeCustomData(midiData, 3); - fNotesRingBuffer.commitWrite(); - } -# endif - -# if DISTRHO_PLUGIN_WANT_STATE - void setState(const char* const key, const char* const value) - { - fUiHelper->setStateFromUI(key, value); - } -# endif - -private: - // Vst3 stuff - ParameterAndNotesHelper* const fUiHelper; - PluginExporter* const fPlugin; - - // Plugin UI - UIExporter fUI; -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - RingBufferControl fNotesRingBuffer; -# endif - - // ------------------------------------------------------------------- - // Callbacks - - #define handlePtr ((UIVst3*)ptr) - - static void editParameterCallback(void* ptr, uint32_t index, bool started) - { - handlePtr->editParameter(index, started); - } - - static void setParameterCallback(void* ptr, uint32_t rindex, float value) - { - handlePtr->setParameterValue(rindex, value); - } - - static void setSizeCallback(void* ptr, uint width, uint height) - { - handlePtr->setSize(width, height); - } - -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) - { - handlePtr->sendNote(channel, note, velocity); - } -# endif - -# if DISTRHO_PLUGIN_WANT_STATE - static void setStateCallback(void* ptr, const char* key, const char* value) - { - handlePtr->setState(key, value); - } -# endif - - #undef handlePtr -}; - -#endif // DISTRHO_PLUGIN_HAS_UI - -// -------------------------------------------------------------------------------------------------------------------- - class PluginVst3 { /* buses: we provide 1 for the main audio (if there is any) plus 1 for each sidechain or cv port. @@ -475,6 +316,16 @@ public: #endif } + void* getInstancePointer() const noexcept + { + return fPlugin.getInstancePointer(); + } + + double getSampleRate() const noexcept + { + return fPlugin.getSampleRate(); + } + // ---------------------------------------------------------------------------------------------------------------- // v3_component interface calls @@ -726,9 +577,14 @@ public: void setProcessing(const bool processing) { if (processing) - fPlugin.activate(); + { + if (! fPlugin.isActive()) + fPlugin.activate(); + } else + { fPlugin.deactivate(); + } } void process(v3_process_data* const data) @@ -775,6 +631,202 @@ private: }; +// -------------------------------------------------------------------------------------------------------------------- + +#if DISTRHO_PLUGIN_HAS_UI + +#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static const sendNoteFunc sendNoteCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_STATE +static const setStateFunc setStateCallback = nullptr; +#endif + +class UIVst3 : public Thread +{ +public: + UIVst3(ScopedPointer& v, v3_plugin_frame* const f, const intptr_t winId, const float scaleFactor) + : vst3(v), + frame(f), + fUI(this, winId, v->getSampleRate(), + editParameterCallback, + setParameterCallback, + setStateCallback, + sendNoteCallback, + setSizeCallback, + nullptr, // TODO file request + nullptr, + v->getInstancePointer(), + scaleFactor) +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + , fNotesRingBuffer() +# endif + { +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + fNotesRingBuffer.setRingBuffer(&uiHelper->notesRingBuffer, false); +# endif + // TESTING awful idea dont reuse + startThread(); + } + + ~UIVst3() override + { + stopThread(5000); + } + + // ------------------------------------------------------------------- + + // TESTING awful idea dont reuse + void run() override + { + while (! shouldThreadExit()) + { + idle(); + d_msleep(50); + } + } + + void idle() + { + /* + for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) + { + if (fUiHelper->parameterChecks[i]) + { + fUiHelper->parameterChecks[i] = false; + fUI.parameterChanged(i, fUiHelper->parameterValues[i]); + } + } + */ + + fUI.plugin_idle(); + } + + int16_t getWidth() const + { + return fUI.getWidth(); + } + + int16_t getHeight() const + { + return fUI.getHeight(); + } + + double getScaleFactor() const + { + return fUI.getScaleFactor(); + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_view interface calls + + void setFrame(v3_plugin_frame* const f) noexcept + { + frame = f; + } + + // ---------------------------------------------------------------------------------------------------------------- + +protected: + void editParameter(const uint32_t /*index*/, const bool /*started*/) const + { + // hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index); + } + + void setParameterValue(const uint32_t index, const float realValue) + { + // const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); + // const float perValue(ranges.getNormalizedValue(realValue)); + + // fPlugin->setParameterValue(index, realValue); + // hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); + } + + void setSize(uint width, uint height) + { +# ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + width /= scaleFactor; + height /= scaleFactor; +# endif + if (frame == nullptr) + return; + + v3_view_rect rect = {}; + rect.right = width; + rect.bottom = height; + // frame->resize_view(nullptr, uivst3, &rect); + } + +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + uint8_t midiData[3]; + midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; + midiData[1] = note; + midiData[2] = velocity; + fNotesRingBuffer.writeCustomData(midiData, 3); + fNotesRingBuffer.commitWrite(); + } +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* const key, const char* const value) + { + // fUiHelper->setStateFromUI(key, value); + } +# endif + +private: + // VST3 stuff + ScopedPointer& vst3; + v3_plugin_frame* frame; + + // Plugin UI + UIExporter fUI; +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + RingBufferControl fNotesRingBuffer; +# endif + + // ------------------------------------------------------------------- + // Callbacks + + #define handlePtr ((UIVst3*)ptr) + + static void editParameterCallback(void* ptr, uint32_t index, bool started) + { + handlePtr->editParameter(index, started); + } + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + handlePtr->setParameterValue(rindex, value); + } + + static void setSizeCallback(void* ptr, uint width, uint height) + { + handlePtr->setSize(width, height); + } + +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + handlePtr->sendNote(channel, note, velocity); + } +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + static void setStateCallback(void* ptr, const char* key, const char* value) + { + handlePtr->setState(key, value); + } +# endif + + #undef handlePtr +}; + +#endif // DISTRHO_PLUGIN_HAS_UI + // -------------------------------------------------------------------------------------------------------------------- // Dummy plugin to get data from @@ -789,9 +841,12 @@ struct v3_plugin_view_cpp : v3_funknown { }; struct dpf_plugin_view : v3_plugin_view_cpp { - ScopedPointer& vst3; + ScopedPointer& vst3; + ScopedPointer uivst3; + double lastScaleFactor = 0.0; + v3_plugin_frame* hostframe = nullptr; - dpf_plugin_view(ScopedPointer& v) + dpf_plugin_view(ScopedPointer& v) : vst3(v) { static const uint8_t* kSupportedFactories[] = { @@ -839,73 +894,157 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result { d_stdout("dpf_plugin_view::is_platform_type_supported => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, platform_type); - return V3_OK; + const char* const supported[] = { +#ifdef _WIN32 + V3_VIEW_PLATFORM_TYPE_HWND, +#elif defined(__APPLE__) + V3_VIEW_PLATFORM_TYPE_NSVIEW, +#else + V3_VIEW_PLATFORM_TYPE_X11, +#endif + }; + + for (size_t i=0; i v3_result { d_stdout("dpf_plugin_view::attached => %s | %p %p %s", __PRETTY_FUNCTION__ + 41, self, parent, platform_type); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); + view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, view->lastScaleFactor); + + // TODO send parameter values + view->uivst3->idle(); return V3_OK; }; view.removed = []V3_API(void* self) -> v3_result { d_stdout("dpf_plugin_view::removed => %s | %p", __PRETTY_FUNCTION__ + 41, self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); + view->uivst3 = nullptr; return V3_OK; }; view.on_wheel = []V3_API(void* self, float distance) -> v3_result { d_stdout("dpf_plugin_view::on_wheel => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, distance); - return V3_OK; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; }; view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result { d_stdout("dpf_plugin_view::on_key_down => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); - return V3_OK; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; }; view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result { d_stdout("dpf_plugin_view::on_key_up => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); - return V3_OK; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; }; - view.get_size = []V3_API(void* self, v3_view_rect*) -> v3_result + view.get_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result { d_stdout("dpf_plugin_view::get_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return V3_OK; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + + std::memset(rect, 0, sizeof(v3_view_rect)); + + if (view->uivst3 != nullptr) + { + rect->right = view->uivst3->getWidth(); + rect->bottom = view->uivst3->getHeight(); +# ifdef DISTRHO_OS_MAC + const double scaleFactor = view->uivst3->getScaleFactor(); + rect->right /= scaleFactor; + rect->bottom /= scaleFactor; +# endif + } + else + { + UIExporter tmpUI(nullptr, 0, view->vst3->getSampleRate(), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + view->vst3->getInstancePointer(), view->lastScaleFactor); + rect->right = tmpUI.getWidth(); + rect->bottom = tmpUI.getHeight(); +# ifdef DISTRHO_OS_MAC + const double scaleFactor = tmpUI.getScaleFactor(); + rect->right /= scaleFactor; + rect->bottom /= scaleFactor; +# endif + tmpUI.quit(); + } + + return V3_NOT_IMPLEMENTED; }; view.set_size = []V3_API(void* self, v3_view_rect*) -> v3_result { d_stdout("dpf_plugin_view::set_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return V3_OK; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; }; view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result { d_stdout("dpf_plugin_view::on_focus => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); - return V3_OK; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; }; - view.set_frame = []V3_API(void* self, v3_plugin_frame*) -> v3_result + view.set_frame = []V3_API(void* self, v3_plugin_frame* frame) -> v3_result { d_stdout("dpf_plugin_view::set_frame => %s | %p", __PRETTY_FUNCTION__ + 41, self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + + view->hostframe = frame; + + if (view->uivst3 != nullptr) + view->uivst3->setFrame(frame); + return V3_OK; }; view.can_resize = []V3_API(void* self) -> v3_result { d_stdout("dpf_plugin_view::can_resize => %s | %p", __PRETTY_FUNCTION__ + 41, self); +#if DISTRHO_UI_USER_RESIZABLE return V3_OK; +#else + return V3_NOT_IMPLEMENTED; +#endif }; view.check_size_constraint = []V3_API(void* self, v3_view_rect*) -> v3_result { d_stdout("dpf_plugin_view::check_size_constraint => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return V3_OK; + return V3_NOT_IMPLEMENTED; }; } }; @@ -920,6 +1059,7 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { + ScopedPointer view; ScopedPointer& vst3; dpf_edit_controller(ScopedPointer& v) @@ -1075,7 +1215,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { d_stdout("dpf_edit_controller::set_parameter_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); PluginVst3* const vst3 = controller->vst3.get(); DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); @@ -1091,10 +1231,16 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NOT_IMPLEMENTED; }; - controller.create_view = []V3_API(void* self, const char* name) -> v3_plug_view** + controller.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** { d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 97, self, name); - return nullptr; + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); + + if (controller->view == nullptr) + controller->view = new dpf_plugin_view(controller->vst3); + + return (v3_plugin_view**)&controller->view; }; } }; @@ -1211,9 +1357,11 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.process = []V3_API(void* self, v3_process_data* data) -> v3_result { - d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 97, self); + // NOTE runs during RT + // d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 97, self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); processor->vst3->process(data); return V3_OK; }; @@ -1345,13 +1493,13 @@ struct dpf_component : v3_component_cpp { comp.set_io_mode = []V3_API(void* self, int32_t io_mode) -> v3_result { d_stdout("dpf_component::set_io_mode => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, io_mode); - return V3_OK; + return V3_NOT_IMPLEMENTED; }; comp.get_bus_count = []V3_API(void* self, int32_t media_type, int32_t bus_direction) -> int32_t { // NOTE runs during RT - d_stdout("dpf_component::get_bus_count => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction); + // d_stdout("dpf_component::get_bus_count => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction); switch (media_type) { @@ -1438,14 +1586,13 @@ struct dpf_component : v3_component_cpp { int32_t bus_idx, v3_bool state) -> v3_result { d_stdout("dpf_component::activate_bus => %s | %p %i %i %i %u", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, state); - return V3_OK; + return V3_NOT_IMPLEMENTED; }; comp.set_active = []V3_API(void* self, v3_bool state) -> v3_result { d_stdout("dpf_component::set_active => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); dpf_component* const component = *(dpf_component**)self; - component->vst3->setActive(state); return V3_OK; }; diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index bf50097b..47d5a54d 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -33,7 +33,7 @@ # define DISTRHO_UI_IS_STANDALONE 0 #endif -#if defined(DISTRHO_PLUGIN_TARGET_VST2) || defined(DISTRHO_PLUGIN_TARGET_VST3) +#if defined(DISTRHO_PLUGIN_TARGET_VST2) # undef DISTRHO_UI_USER_RESIZABLE # define DISTRHO_UI_USER_RESIZABLE 0 #endif diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 23486db7..9e77d408 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -103,7 +103,7 @@ struct v3_edit_controller { V3_API v3_result (*set_component_handler) (void *self, struct v3_component_handler **); - V3_API struct v3_plug_view **(*create_view) + V3_API struct v3_plugin_view **(*create_view) (void *self, const char *name); }; From dfb7e30dc171456d328c8d94fc7df67a14f5405e Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Sep 2021 19:36:17 +0100 Subject: [PATCH 060/504] Cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 17a3aad4..2a28a5e1 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -733,7 +733,7 @@ protected: // hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index); } - void setParameterValue(const uint32_t index, const float realValue) + void setParameterValue(const uint32_t /*index*/, const float /*realValue*/) { // const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); // const float perValue(ranges.getNormalizedValue(realValue)); @@ -755,6 +755,7 @@ protected: v3_view_rect rect = {}; rect.right = width; rect.bottom = height; + (void)rect; // frame->resize_view(nullptr, uivst3, &rect); } @@ -920,9 +921,6 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, view->lastScaleFactor); - - // TODO send parameter values - view->uivst3->idle(); return V3_OK; }; From a4203cd316406ac393c4eddbf90896a2b388ac48 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Sep 2021 23:07:44 +0100 Subject: [PATCH 061/504] Deal with most VST3 parameter stuff, start the refcounts Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 916 ++++++++++++++++--------- distrho/src/travesty/edit_controller.h | 2 +- 2 files changed, 604 insertions(+), 314 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 2a28a5e1..252b4e7c 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -329,156 +329,333 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_component interface calls -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - int32_t getAudioBusCount(const bool isInput) const noexcept + int32_t getBusCount(const int32_t mediaType, const int32_t busDirection) const noexcept { - if (isInput) - return inputBuses.audio + inputBuses.sidechain + inputBuses.numCV; - else - return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV; - }; + switch (mediaType) + { + case V3_AUDIO: + if (busDirection == V3_INPUT) + return inputBuses.audio + inputBuses.sidechain + inputBuses.numCV; + if (busDirection == V3_OUTPUT) + return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV; + break; + case V3_EVENT: +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (busDirection == V3_INPUT) + return 1; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (busDirection == V3_OUTPUT) + return 1; +#endif + break; + } - v3_result getBusAudioBusInfo(const bool isInput, const uint32_t index, v3_bus_info* info) const + return 0; + } + + v3_result getBusInfo(const int32_t mediaType, + const int32_t busDirection, + const int32_t busIndex, + v3_bus_info* const info) const { - int32_t channel_count; - v3_bus_types bus_type; - v3_bus_flags flags; - v3_str_128 bus_name; + DISTRHO_SAFE_ASSERT_INT_RETURN(mediaType == V3_AUDIO || mediaType == V3_EVENT, mediaType, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG); + +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 || DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + const uint32_t busId = static_cast(busIndex); +#endif - if (isInput) + if (mediaType == V3_AUDIO) { -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 - switch (index) + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + int32_t numChannels; + v3_bus_flags flags; + v3_bus_types busType; + v3_str_128 busName = {}; + + if (busDirection == V3_INPUT) { - case 0: - if (inputBuses.audio) + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + switch (busId) { - channel_count = inputBuses.numMainAudio; - bus_type = V3_MAIN; - flags = V3_DEFAULT_ACTIVE; + case 0: + if (inputBuses.audio) + { + numChannels = inputBuses.numMainAudio; + busType = V3_MAIN; + flags = V3_DEFAULT_ACTIVE; + break; + } + // fall-through + case 1: + if (inputBuses.sidechain) + { + numChannels = inputBuses.numSidechain; + busType = V3_AUX; + flags = v3_bus_flags(0); + break; + } + // fall-through + default: + numChannels = 1; + busType = V3_AUX; + flags = V3_IS_CONTROL_VOLTAGE; break; } - // fall-through - case 1: - if (inputBuses.sidechain) + + if (busType == V3_MAIN) { - channel_count = inputBuses.numSidechain; - bus_type = V3_AUX; - flags = v3_bus_flags(0); - break; + strncpy_16from8(busName, "Audio Input", 128); } - // fall-through - default: - channel_count = 1; - bus_type = V3_AUX; - flags = V3_IS_CONTROL_VOLTAGE; - break; - } - - if (bus_type == V3_MAIN) - { - strncpy_16from8(info->bus_name, "Audio Input", 128); + else + { + for (uint32_t i=0; i 0 + switch (busId) { - const AudioPortWithBusId& port(fPlugin.getAudioPort(true, i)); - - // TODO find port group name - if (port.busId == index) + case 0: + if (outputBuses.audio) { - strncpy_16from8(info->bus_name, port.name, 128); + numChannels = outputBuses.numMainAudio; + busType = V3_MAIN; + flags = V3_DEFAULT_ACTIVE; break; } + // fall-through + case 1: + if (outputBuses.sidechain) + { + numChannels = outputBuses.numSidechain; + busType = V3_AUX; + flags = v3_bus_flags(0); + break; + } + // fall-through + default: + numChannels = 1; + busType = V3_AUX; + flags = V3_IS_CONTROL_VOLTAGE; + break; } - } -#else - return V3_INVALID_ARG; -#endif - } - else - { -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - switch (index) - { - case 0: - if (outputBuses.audio) + + if (busType == V3_MAIN) { - channel_count = outputBuses.numMainAudio; - bus_type = V3_MAIN; - flags = V3_DEFAULT_ACTIVE; - break; + strncpy_16from8(busName, "Audio Output", 128); } - // fall-through - case 1: - if (outputBuses.sidechain) + else { - channel_count = outputBuses.numSidechain; - bus_type = V3_AUX; - flags = v3_bus_flags(0); - break; + for (uint32_t i=0; imedia_type = V3_AUDIO; + info->direction = busDirection; + info->channel_count = numChannels; + std::memcpy(info->bus_name, busName, sizeof(busName)); + info->bus_type = busType; + info->flags = flags; + return V3_OK; + #else + return V3_INVALID_ARG; + #endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS + } + else + { + if (busDirection == V3_INPUT) { - strncpy_16from8(info->bus_name, "Audio Output", 128); + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG); + #else + return V3_INVALID_ARG; + #endif } else { - for (uint32_t i=0; ibus_name, port.name, 128); - break; - } - } + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG); + #else + return V3_INVALID_ARG; + #endif } -#else - return V3_INVALID_ARG; -#endif + info->media_type = V3_EVENT; + info->direction = busDirection; + info->channel_count = 1; + strncpy_16from8(info->bus_name, busDirection == V3_INPUT ? "Event/MIDI Input" + : "Event/MIDI Output", 128); + info->bus_type = V3_MAIN; + info->flags = V3_DEFAULT_ACTIVE; + return V3_OK; } + } - std::memset(info, 0, sizeof(v3_bus_info)); - info->media_type = V3_AUDIO; - info->direction = isInput ? V3_INPUT : V3_OUTPUT; - info->channel_count = channel_count; - std::memcpy(info->bus_name, bus_name, sizeof(bus_name)); - info->bus_type = bus_type; - info->flags = flags; + v3_result activateBus(const int32_t /* mediaType */, + const int32_t /* busDirection */, + const int32_t /* busIndex */, + const bool /* state */) + { + // TODO, returning ok to make bitwig happy return V3_OK; } -#endif - void setActive(const bool active) + v3_result setActive(const bool active) { if (active) fPlugin.activate(); else fPlugin.deactivateIfNeeded(); + + return V3_OK; + } + + v3_result setState(v3_bstream**) + { + // TODO + return V3_NOT_IMPLEMENTED; + } + + v3_result getState(v3_bstream**) + { + // TODO + return V3_NOT_IMPLEMENTED; + }; + + // ---------------------------------------------------------------------------------------------------------------- + // v3_audio_processor interface calls + + v3_result setBusArrangements(v3_speaker_arrangement*, int32_t, v3_speaker_arrangement*, int32_t) + { + // TODO + return V3_NOT_IMPLEMENTED; + } + + v3_result getBusArrangement(int32_t, int32_t, v3_speaker_arrangement*) + { + // TODO + return V3_NOT_IMPLEMENTED; + }; + + uint32_t getLatencySamples() const noexcept + { +#if DISTRHO_PLUGIN_WANT_LATENCY + return fPlugin.getLatency(); +#else + return 0; +#endif + } + + v3_result setupProcessing(v3_process_setup* const setup) + { + DISTRHO_SAFE_ASSERT_RETURN(setup->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); + + const bool active = fPlugin.isActive(); + fPlugin.deactivateIfNeeded(); + + // TODO process_mode can be V3_REALTIME, V3_PREFETCH, V3_OFFLINE + + fPlugin.setSampleRate(setup->sample_rate, true); + fPlugin.setBufferSize(setup->max_block_size, true); + + if (active) + fPlugin.activate(); + + return V3_OK; + } + + v3_result setProcessing(const bool processing) + { + if (processing) + { + if (! fPlugin.isActive()) + fPlugin.activate(); + } + else + { + fPlugin.deactivate(); + } + + return V3_OK; + } + + v3_result process(v3_process_data* const data) + { + DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); + + // TODO full thing + + return V3_OK; + } + + uint32_t getTailSamples() const noexcept + { + return 0; } // ---------------------------------------------------------------------------------------------------------------- // v3_edit_controller interface calls - uint32_t getParameterCount() const noexcept + v3_result setComponentState(v3_bstream*) + { + // TODO + return V3_NOT_IMPLEMENTED; + }; + + v3_result setState(v3_bstream*) + { + // TODO + return V3_NOT_IMPLEMENTED; + }; + + v3_result getState(v3_bstream*) + { + // TODO + return V3_NOT_IMPLEMENTED; + }; + + int32_t getParameterCount() const noexcept { return fPlugin.getParameterCount(); } - void getParameterInfo(const uint32_t index, v3_param_info* const info) const noexcept + v3_result getParameterInfo(const int32_t index, v3_param_info* const info) const noexcept { + DISTRHO_SAFE_ASSERT_RETURN(index >= 0, V3_INVALID_ARG); + + const uint32_t uindex = static_cast(index); + DISTRHO_SAFE_ASSERT_RETURN(uindex < fPlugin.getParameterCount(), V3_INVALID_ARG); + // set up flags int32_t flags = 0; @@ -519,79 +696,73 @@ public: strncpy_16from8(info->title, fPlugin.getParameterName(index), 128); strncpy_16from8(info->short_title, fPlugin.getParameterShortName(index), 128); strncpy_16from8(info->units, fPlugin.getParameterUnit(index), 128); + + return V3_OK; } - double getNormalizedParameterValue(const uint32_t index) + v3_result getParameterStringForValue(const v3_param_id index, const double normalised, v3_str_128 output) { - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); - return ranges.getNormalizedValue(fPlugin.getParameterValue(index)); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); + + // TODO snprintf16 + char buf[24]; + sprintf(buf, "%f", fPlugin.getParameterRanges(index).getUnnormalizedValue(normalised)); + strncpy_16from8(output, buf, 128); + return V3_OK; } - void setNormalizedParameterValue(const uint32_t index, const double value) + v3_result getParameterValueForString(const v3_param_id index, int16_t*, double*) { - const uint32_t hints = fPlugin.getParameterHints(index); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); - float realValue = ranges.getUnnormalizedValue(value); + // TODO + return V3_NOT_IMPLEMENTED; + }; - if (hints & kParameterIsBoolean) - { - const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; - realValue = realValue > midRange ? ranges.max : ranges.min; - } + v3_result normalisedParameterToPlain(const v3_param_id index, const double normalised) + { + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); - if (hints & kParameterIsInteger) - { - realValue = std::round(realValue); - } + return fPlugin.getParameterRanges(index).getUnnormalizedValue(normalised); + }; - fPlugin.setParameterValue(index, realValue); - } + v3_result plainParameterToNormalised(const v3_param_id index, const double plain) + { + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); - // ---------------------------------------------------------------------------------------------------------------- - // v3_audio_processor interface calls + return fPlugin.getParameterRanges(index).getNormalizedValue(plain); + }; -#if DISTRHO_PLUGIN_WANT_LATENCY - uint32_t getLatencySamples() const noexcept + double getParameterNormalized(const v3_param_id index) { - return fPlugin.getLatency(); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), 0.0); + + const float value = fPlugin.getParameterValue(index); + return fPlugin.getParameterRanges(index).getNormalizedValue(value); } -#endif - void setupProcessing(v3_process_setup* const setup) + v3_result setParameterNormalized(const v3_param_id index, const double value) { - DISTRHO_SAFE_ASSERT_RETURN(setup->symbolic_sample_size == V3_SAMPLE_32,); - - const bool active = fPlugin.isActive(); - fPlugin.deactivateIfNeeded(); - - // TODO process_mode can be V3_REALTIME, V3_PREFETCH, V3_OFFLINE + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); - fPlugin.setSampleRate(setup->sample_rate, true); - fPlugin.setBufferSize(setup->max_block_size, true); + const uint32_t hints = fPlugin.getParameterHints(index); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); - if (active) - fPlugin.activate(); - } + float realValue = ranges.getUnnormalizedValue(value); - void setProcessing(const bool processing) - { - if (processing) + if (hints & kParameterIsBoolean) { - if (! fPlugin.isActive()) - fPlugin.activate(); + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + realValue = realValue > midRange ? ranges.max : ranges.min; } - else + + if (hints & kParameterIsInteger) { - fPlugin.deactivate(); + realValue = std::round(realValue); } - } - - void process(v3_process_data* const data) - { - DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32,); - // TODO full thing + fPlugin.setParameterValue(index, realValue); + return V3_OK; } // ---------------------------------------------------------------------------------------------------------------- @@ -725,6 +896,11 @@ public: frame = f; } + void setHandler(v3_component_handler** const h) noexcept + { + handler = h; + } + // ---------------------------------------------------------------------------------------------------------------- protected: @@ -782,6 +958,7 @@ private: // VST3 stuff ScopedPointer& vst3; v3_plugin_frame* frame; + v3_component_handler** handler = nullptr; // Plugin UI UIExporter fUI; @@ -845,6 +1022,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { ScopedPointer& vst3; ScopedPointer uivst3; double lastScaleFactor = 0.0; + v3_component_handler** handler = nullptr; v3_plugin_frame* hostframe = nullptr; dpf_plugin_view(ScopedPointer& v) @@ -921,6 +1099,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, view->lastScaleFactor); + view->uivst3->setHandler(view->handler); return V3_OK; }; @@ -1057,13 +1236,19 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { + std::atomic refcounter; + ScopedPointer* self; ScopedPointer view; ScopedPointer& vst3; + bool initialized; - dpf_edit_controller(ScopedPointer& v) - : vst3(v) + dpf_edit_controller(ScopedPointer* const s, ScopedPointer& v) + : refcounter(1), + self(s), + vst3(v), + initialized(false) { - static const uint8_t* kSupportedFactories[] = { + static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, v3_edit_controller_iid }; @@ -1077,9 +1262,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* factory_iid : kSupportedFactories) + for (const uint8_t* interface_iid : kSupportedInterfaces) { - if (v3_tuid_match(factory_iid, iid)) + if (v3_tuid_match(interface_iid, iid)) { *iface = self; return V3_OK; @@ -1089,16 +1274,26 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NO_INTERFACE; }; - // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 97, self); - return 1; + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0); + + return ++controller->refcounter; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 97, self); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0); + + if (const int refcounter = --controller->refcounter) + return refcounter; + + *(dpf_edit_controller**)self = nullptr; + *controller->self = nullptr; return 0; }; @@ -1108,44 +1303,77 @@ struct dpf_edit_controller : v3_edit_controller_cpp { base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result { d_stdout("dpf_edit_controller::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 97, self, context); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + const bool initialized = controller->initialized; + DISTRHO_SAFE_ASSERT_RETURN(! initialized, V3_INVALID_ARG); + + controller->initialized = true; return V3_OK; }; base.terminate = []V3_API(void* self) -> v3_result { d_stdout("dpf_edit_controller::terminate => %s | %p", __PRETTY_FUNCTION__ + 97, self); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + const bool initialized = controller->initialized; + DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); + + controller->initialized = false; return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ // v3_edit_controller - controller.set_component_state = []V3_API(void* self, v3_bstream*) -> v3_result + controller.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); - return V3_OK; + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->setComponentState(stream); }; - controller.set_state = []V3_API(void* self, v3_bstream*) -> v3_result + controller.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); - return V3_OK; + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->setState(stream); }; - controller.get_state = []V3_API(void* self, v3_bstream*) -> v3_result + controller.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); - return V3_OK; + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->getState(stream); }; controller.get_parameter_count = []V3_API(void* self) -> int32_t { d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 97, self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); - PluginVst3* const vst3 = controller->vst3.get(); + PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + return vst3->getParameterCount(); }; @@ -1153,80 +1381,100 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 97, self, param_idx); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); - DISTRHO_SAFE_ASSERT_RETURN(param_idx >= 0, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); - PluginVst3* const vst3 = controller->vst3.get(); + PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - const uint32_t uidx = static_cast(param_idx); - DISTRHO_SAFE_ASSERT_RETURN(uidx < vst3->getParameterCount(), V3_INVALID_ARG); - - vst3->getParameterInfo(uidx, param_info); - return V3_OK; + return vst3->getParameterInfo(param_idx, param_info); }; controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); + d_stdout("dpf_edit_controller::get_parameter_string_for_value => %s | %p %u %f %p", __PRETTY_FUNCTION__ + 97, self, index, normalised, output); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); - const ParameterRanges& ranges(gPluginInfo->getParameterRanges(index)); - const float realvalue = ranges.getUnnormalizedValue(normalised); - char buf[24]; - sprintf(buf, "%f", realvalue); - strncpy_16from8(output, buf, 128); - return V3_OK; + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->getParameterStringForValue(index, normalised, output); }; - controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id, int16_t* input, double* output) -> v3_result + controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id index, int16_t* input, double* output) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 97, self, input, output); - return V3_NOT_IMPLEMENTED; + d_stdout("dpf_edit_controller::get_parameter_value_for_string => %s | %p %u %p %p", __PRETTY_FUNCTION__ + 97, self, index, input, output); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->getParameterValueForString(index, input, output); }; - controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id, double normalised) -> double + controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id index, double normalised) -> double { - d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); - return normalised; + d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %s | %p %u %f", __PRETTY_FUNCTION__ + 97, self, index, normalised); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->normalisedParameterToPlain(index, normalised); }; - controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id, double normalised) -> double + controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double { - d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); - return normalised; + d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, plain); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->plainParameterToNormalised(index, plain); }; - controller.get_parameter_normalised = []V3_API(void* self, v3_param_id param_idx) -> double + controller.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double { d_stdout("dpf_edit_controller::get_parameter_normalised => %s | %p", __PRETTY_FUNCTION__ + 97, self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); - PluginVst3* const vst3 = controller->vst3.get(); + PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0); - DISTRHO_SAFE_ASSERT_RETURN(param_idx < vst3->getParameterCount(), 0.0); - return vst3->getNormalizedParameterValue(param_idx); + return vst3->getParameterNormalized(index); }; - controller.set_parameter_normalised = []V3_API(void* self, v3_param_id param_idx, double normalised) -> v3_result + controller.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result { d_stdout("dpf_edit_controller::set_parameter_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); - PluginVst3* const vst3 = controller->vst3.get(); + PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(param_idx < vst3->getParameterCount(), V3_INVALID_ARG); - vst3->setNormalizedParameterValue(param_idx, normalised); + vst3->setParameterNormalized(index, normalised); return V3_OK; }; - controller.set_component_handler = []V3_API(void* self, v3_component_handler**) -> v3_result + controller.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result { - d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 97, self); - return V3_NOT_IMPLEMENTED; + d_stdout("dpf_edit_controller::set_component_handler => %s | %p %p", __PRETTY_FUNCTION__ + 97, self, handler); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(controller->view != nullptr, V3_NOT_INITIALISED); + + controller->view->handler = handler; + + if (UIVst3* const uivst3 = controller->view->uivst3) + uivst3->setHandler(handler); + + return V3_OK; }; controller.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** @@ -1251,12 +1499,16 @@ struct v3_audio_processor_cpp : v3_funknown { }; struct dpf_audio_processor : v3_audio_processor_cpp { + std::atomic refcounter; + ScopedPointer* self; ScopedPointer& vst3; - dpf_audio_processor(ScopedPointer& v) - : vst3(v) + dpf_audio_processor(ScopedPointer* const s, ScopedPointer& v) + : refcounter(1), + self(s), + vst3(v) { - static const uint8_t* kSupportedFactories[] = { + static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, v3_audio_processor_iid }; @@ -1270,9 +1522,9 @@ struct dpf_audio_processor : v3_audio_processor_cpp { *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* factory_iid : kSupportedFactories) + for (const uint8_t* interface_iid : kSupportedInterfaces) { - if (v3_tuid_match(factory_iid, iid)) + if (v3_tuid_match(interface_iid, iid)) { *iface = self; return V3_OK; @@ -1282,16 +1534,26 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return V3_NO_INTERFACE; }; - // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 97, self); - return 1; + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + + return ++processor->refcounter; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 97, self); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + + if (const int refcounter = --processor->refcounter) + return refcounter; + + *(dpf_audio_processor**)self = nullptr; + *processor->self = nullptr; return 0; }; @@ -1303,14 +1565,26 @@ struct dpf_audio_processor : v3_audio_processor_cpp { v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result { d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 97, self, inputs, num_inputs, outputs, num_outputs); - return V3_NOT_IMPLEMENTED; + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return processor->vst3->setBusArrangements(inputs, num_inputs, outputs, num_outputs); }; processor.get_bus_arrangement = []V3_API(void* self, int32_t bus_direction, - int32_t idx, v3_speaker_arrangement*) -> v3_result + int32_t idx, v3_speaker_arrangement* arr) -> v3_result { - d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i", __PRETTY_FUNCTION__ + 97, self, bus_direction, idx); - return V3_NOT_IMPLEMENTED; + d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i %p", __PRETTY_FUNCTION__ + 97, self, bus_direction, idx, arr); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return processor->vst3->getBusArrangement(bus_direction, idx, arr); }; processor.can_process_sample_size = []V3_API(void* self, int32_t symbolic_sample_size) -> v3_result @@ -1322,26 +1596,27 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.get_latency_samples = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 97, self); -#if DISTRHO_PLUGIN_WANT_LATENCY dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); + return processor->vst3->getLatencySamples(); -#else - return 0; -#endif }; processor.setup_processing = []V3_API(void* self, v3_process_setup* setup) -> v3_result { d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 97, self); - d_lastBufferSize = setup->max_block_size; - d_lastSampleRate = setup->sample_rate; - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(setup->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); - processor->vst3->setupProcessing(setup); - return V3_OK; + + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + d_lastBufferSize = setup->max_block_size; + d_lastSampleRate = setup->sample_rate; + return processor->vst3->setupProcessing(setup); }; processor.set_processing = []V3_API(void* self, v3_bool state) -> v3_result @@ -1349,8 +1624,11 @@ struct dpf_audio_processor : v3_audio_processor_cpp { d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 97, self, state); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); - processor->vst3->setProcessing(state); - return V3_OK; + + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return processor->vst3->setProcessing(state); }; processor.process = []V3_API(void* self, v3_process_data* data) -> v3_result @@ -1359,15 +1637,23 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 97, self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); - processor->vst3->process(data); - return V3_OK; + + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return processor->vst3->process(data); }; processor.get_tail_samples = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 97, self); - return 0; + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); + + return processor->vst3->getTailSamples(); }; } }; @@ -1382,12 +1668,14 @@ struct v3_component_cpp : v3_funknown { struct dpf_component : v3_component_cpp { std::atomic refcounter; + ScopedPointer* self; ScopedPointer processor; ScopedPointer controller; ScopedPointer vst3; - dpf_component() - : refcounter(1) + dpf_component(ScopedPointer* const s) + : refcounter(1), + self(s) { static const uint8_t* kSupportedBaseFactories[] = { v3_funknown_iid, @@ -1402,7 +1690,9 @@ struct dpf_component : v3_component_cpp { { d_stdout("dpf_component::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 41, self, tuid2str(iid), iface); *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NO_INTERFACE); for (const uint8_t* factory_iid : kSupportedBaseFactories) { @@ -1415,18 +1705,16 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(v3_audio_processor_iid, iid)) { - dpf_component* const component = *(dpf_component**)self; if (component->processor == nullptr) - component->processor = new dpf_audio_processor(component->vst3); + component->processor = new dpf_audio_processor(&component->processor, component->vst3); *iface = &component->processor; return V3_OK; } if (v3_tuid_match(v3_edit_controller_iid, iid)) { - dpf_component* const component = *(dpf_component**)self; if (component->controller == nullptr) - component->controller = new dpf_edit_controller(component->vst3); + component->controller = new dpf_edit_controller(&component->controller, component->vst3); *iface = &component->controller; return V3_OK; } @@ -1434,11 +1722,12 @@ struct dpf_component : v3_component_cpp { return V3_NO_INTERFACE; }; - // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_component::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, 0); + return ++component->refcounter; }; @@ -1446,10 +1735,13 @@ struct dpf_component : v3_component_cpp { { d_stdout("dpf_component::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, 0); + if (const int refcounter = --component->refcounter) return refcounter; - // delete component; + *(dpf_component**)self = nullptr; + *component->self = nullptr; return 0; }; @@ -1460,6 +1752,10 @@ struct dpf_component : v3_component_cpp { { d_stdout("dpf_component::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 41, self, context); dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 == nullptr, V3_INVALID_ARG); // default early values if (d_lastBufferSize == 0) @@ -1475,6 +1771,11 @@ struct dpf_component : v3_component_cpp { { d_stdout("dpf_component::terminate => %s | %p", __PRETTY_FUNCTION__ + 41, self); dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_INVALID_ARG); + component->vst3 = nullptr; return V3_OK; }; @@ -1485,12 +1786,26 @@ struct dpf_component : v3_component_cpp { comp.get_controller_class_id = []V3_API(void* self, v3_tuid class_id) -> v3_result { d_stdout("dpf_component::get_controller_class_id => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, tuid2str(class_id)); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + // TODO return V3_NOT_IMPLEMENTED; }; comp.set_io_mode = []V3_API(void* self, int32_t io_mode) -> v3_result { d_stdout("dpf_component::set_io_mode => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, io_mode); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + // TODO return V3_NOT_IMPLEMENTED; }; @@ -1498,85 +1813,38 @@ struct dpf_component : v3_component_cpp { { // NOTE runs during RT // d_stdout("dpf_component::get_bus_count => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); - switch (media_type) - { - case V3_AUDIO: -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - if (bus_direction == V3_INPUT || bus_direction == V3_OUTPUT) - { - dpf_component* const component = *(dpf_component**)self; - return component->vst3->getAudioBusCount(bus_direction == V3_INPUT); - } -#endif - break; - - case V3_EVENT: -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (bus_direction == V3_INPUT) - return 1; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - if (bus_direction == V3_OUTPUT) - return 1; -#endif - break; - } + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - return 0; + return vst3->getBusCount(media_type, bus_direction); }; comp.get_bus_info = []V3_API(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bus_info* info) -> v3_result { d_stdout("dpf_component::get_bus_info => %s | %p %i %i %i %p", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, info); - DISTRHO_SAFE_ASSERT_INT_RETURN(media_type == V3_AUDIO || media_type == V3_EVENT, media_type, V3_INVALID_ARG); - DISTRHO_SAFE_ASSERT_INT_RETURN(bus_direction == V3_INPUT || bus_direction == V3_OUTPUT, bus_direction, V3_INVALID_ARG); - DISTRHO_SAFE_ASSERT_INT_RETURN(bus_idx >= 0, bus_idx, V3_INVALID_ARG); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); - if (media_type == V3_AUDIO) - { -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - dpf_component* const component = *(dpf_component**)self; - return component->vst3->getBusAudioBusInfo(bus_direction == V3_INPUT, - static_cast(bus_idx), - info); -#else - return V3_INVALID_ARG; -#endif - } - else - { - if (bus_direction == V3_INPUT) - { -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG); -#else - return V3_INVALID_ARG; -#endif - } - else - { -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG); -#else - return V3_INVALID_ARG; -#endif - } - info->media_type = V3_EVENT; - info->direction = bus_direction; - info->channel_count = 1; - strncpy_16from8(info->bus_name, bus_direction == V3_INPUT ? "Event/MIDI Input" - : "Event/MIDI Output", 128); - info->bus_type = V3_MAIN; - info->flags = V3_DEFAULT_ACTIVE; - return V3_OK; - } + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->getBusInfo(media_type, bus_direction, bus_idx, info); }; comp.get_routing_info = []V3_API(void* self, v3_routing_info* input, v3_routing_info* output) -> v3_result { d_stdout("dpf_component::get_routing_info => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, input, output); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + // TODO return V3_NOT_IMPLEMENTED; }; @@ -1584,27 +1852,49 @@ struct dpf_component : v3_component_cpp { int32_t bus_idx, v3_bool state) -> v3_result { d_stdout("dpf_component::activate_bus => %s | %p %i %i %i %u", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, state); - return V3_NOT_IMPLEMENTED; + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->activateBus(media_type, bus_direction, bus_idx, state); }; comp.set_active = []V3_API(void* self, v3_bool state) -> v3_result { d_stdout("dpf_component::set_active => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); dpf_component* const component = *(dpf_component**)self; - component->vst3->setActive(state); - return V3_OK; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return component->vst3->setActive(state); }; - comp.set_state = []V3_API(void* self, v3_bstream**) -> v3_result + comp.set_state = []V3_API(void* self, v3_bstream** stream) -> v3_result { d_stdout("dpf_component::set_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return V3_NOT_IMPLEMENTED; + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->setState(stream); }; - comp.get_state = []V3_API(void* self, v3_bstream**) -> v3_result + comp.get_state = []V3_API(void* self, v3_bstream** stream) -> v3_result { d_stdout("dpf_component::get_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); - return V3_NOT_IMPLEMENTED; + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->getState(stream); }; } }; @@ -1619,8 +1909,6 @@ struct v3_plugin_factory_cpp : v3_funknown { }; struct dpf_factory : v3_plugin_factory_cpp { - std::vector components; - dpf_factory() { static const uint8_t* kSupportedFactories[] = { @@ -1701,9 +1989,11 @@ struct dpf_factory : v3_plugin_factory_cpp { v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); dpf_factory* const factory = *(dpf_factory**)self; - dpf_component* const component = new dpf_component(); - factory->components.push_back(component); - *instance = &factory->components.back(); + DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALISED); + + ScopedPointer* const componentptr = new ScopedPointer; + *componentptr = new dpf_component(componentptr); + *instance = componentptr; return V3_OK; }; diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 9e77d408..7a50906c 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -94,7 +94,7 @@ struct v3_edit_controller { V3_API double (*normalised_parameter_to_plain) (void *self, v3_param_id, double normalised); V3_API double (*plain_parameter_to_normalised) - (void *self, v3_param_id, double normalised); + (void *self, v3_param_id, double plain); V3_API double (*get_parameter_normalised)(void *self, v3_param_id); V3_API v3_result (*set_parameter_normalised) From 63c52b3b38497bbfb9cd57bfcc8ab7e4a2a3a9e8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Sep 2021 23:32:38 +0100 Subject: [PATCH 062/504] VST3 audio processing, cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 190 ++++++++++++++++++------------ 1 file changed, 116 insertions(+), 74 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 252b4e7c..3b24f576 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -521,6 +521,12 @@ public: } } + v3_result getRoutingInfo(v3_routing_info*, v3_routing_info*) + { + // TODO + return V3_NOT_IMPLEMENTED; + } + v3_result activateBus(const int32_t /* mediaType */, const int32_t /* busDirection */, const int32_t /* busIndex */, @@ -591,6 +597,8 @@ public: if (active) fPlugin.activate(); + // TODO create dummy buffer of max_block_size length, to use for disabled buses + return V3_OK; } @@ -613,7 +621,42 @@ public: { DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); - // TODO full thing + if (! fPlugin.isActive()) + { + // host has not activated the plugin yet, nasty! + fPlugin.activate(); + } + +#if DISTRHO_PLUGIN_WANT_TIMEPOS + // TODO +#endif + + const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS]; + /* */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + + { + int32_t i = 0; + for (; i < data->inputs->num_channels; ++i) + inputs[i] = data->inputs->channel_buffers_32[i]; + for (; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) + inputs[i] = nullptr; // TODO use dummy buffer + } + + { + int32_t i = 0; + for (; i < data->outputs->num_channels; ++i) + outputs[i] = data->outputs->channel_buffers_32[i]; + for (; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + outputs[i] = nullptr; // TODO use dummy buffer + } + +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + fPlugin.run(inputs, outputs, data->nframes, nullptr, 0); +#else + fPlugin.run(inputs, outputs, data->nframes); +#endif + + // TODO updateParameterOutputsAndTriggers() return V3_OK; } @@ -630,19 +673,19 @@ public: { // TODO return V3_NOT_IMPLEMENTED; - }; + } v3_result setState(v3_bstream*) { // TODO return V3_NOT_IMPLEMENTED; - }; + } v3_result getState(v3_bstream*) { // TODO return V3_NOT_IMPLEMENTED; - }; + } int32_t getParameterCount() const noexcept { @@ -1038,7 +1081,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_plugin_view::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 41, self, tuid2str(iid), iface); + d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -1057,13 +1100,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_plugin_view::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::ref => %p", self); return 1; }; unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_plugin_view::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::unref => %p", self); return 0; }; @@ -1072,7 +1115,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result { - d_stdout("dpf_plugin_view::is_platform_type_supported => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, platform_type); + d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); const char* const supported[] = { #ifdef _WIN32 V3_VIEW_PLATFORM_TYPE_HWND, @@ -1094,7 +1137,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.attached = []V3_API(void* self, void* parent, const char* platform_type) -> v3_result { - d_stdout("dpf_plugin_view::attached => %s | %p %p %s", __PRETTY_FUNCTION__ + 41, self, parent, platform_type); + d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); @@ -1105,7 +1148,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.removed = []V3_API(void* self) -> v3_result { - d_stdout("dpf_plugin_view::removed => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::removed => %p", self); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); @@ -1115,7 +1158,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.on_wheel = []V3_API(void* self, float distance) -> v3_result { - d_stdout("dpf_plugin_view::on_wheel => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, distance); + d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); @@ -1124,7 +1167,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result { - d_stdout("dpf_plugin_view::on_key_down => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); + d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); @@ -1133,7 +1176,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result { - d_stdout("dpf_plugin_view::on_key_up => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); + d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); @@ -1142,7 +1185,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.get_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result { - d_stdout("dpf_plugin_view::get_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::get_size => %p", self); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); @@ -1178,7 +1221,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.set_size = []V3_API(void* self, v3_view_rect*) -> v3_result { - d_stdout("dpf_plugin_view::set_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::set_size => %p", self); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); @@ -1187,7 +1230,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result { - d_stdout("dpf_plugin_view::on_focus => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); + d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); @@ -1196,7 +1239,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.set_frame = []V3_API(void* self, v3_plugin_frame* frame) -> v3_result { - d_stdout("dpf_plugin_view::set_frame => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::set_frame => %p", self); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); @@ -1210,7 +1253,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.can_resize = []V3_API(void* self) -> v3_result { - d_stdout("dpf_plugin_view::can_resize => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::can_resize => %p", self); #if DISTRHO_UI_USER_RESIZABLE return V3_OK; #else @@ -1220,7 +1263,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.check_size_constraint = []V3_API(void* self, v3_view_rect*) -> v3_result { - d_stdout("dpf_plugin_view::check_size_constraint => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_plugin_view::check_size_constraint => %p", self); return V3_NOT_IMPLEMENTED; }; } @@ -1258,7 +1301,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_edit_controller::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 97, self, tuid2str(iid), iface); + d_stdout("dpf_edit_controller::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -1276,7 +1319,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::ref => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0); @@ -1285,7 +1328,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::unref => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0); @@ -1302,7 +1345,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result { - d_stdout("dpf_edit_controller::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 97, self, context); + d_stdout("dpf_edit_controller::initialise => %p %p", self, context); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1315,7 +1358,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { base.terminate = []V3_API(void* self) -> v3_result { - d_stdout("dpf_edit_controller::terminate => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::terminate => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1331,7 +1374,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { - d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::set_component_state => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1343,7 +1386,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { - d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::set_state => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1355,7 +1398,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { - d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::get_state => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1367,7 +1410,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_count = []V3_API(void* self) -> int32_t { - d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::get_parameter_count => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1379,7 +1422,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 97, self, param_idx); + d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1391,7 +1434,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_string_for_value => %s | %p %u %f %p", __PRETTY_FUNCTION__ + 97, self, index, normalised, output); + d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1403,7 +1446,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id index, int16_t* input, double* output) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_value_for_string => %s | %p %u %p %p", __PRETTY_FUNCTION__ + 97, self, index, input, output); + d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1415,7 +1458,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id index, double normalised) -> double { - d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %s | %p %u %f", __PRETTY_FUNCTION__ + 97, self, index, normalised); + d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1427,7 +1470,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double { - d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, plain); + d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %f", self, plain); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1439,7 +1482,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double { - d_stdout("dpf_edit_controller::get_parameter_normalised => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_edit_controller::get_parameter_normalised => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); @@ -1451,7 +1494,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result { - d_stdout("dpf_edit_controller::set_parameter_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 97, self, normalised); + d_stdout("dpf_edit_controller::set_parameter_normalised => %p %f", self, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1464,7 +1507,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result { - d_stdout("dpf_edit_controller::set_component_handler => %s | %p %p", __PRETTY_FUNCTION__ + 97, self, handler); + d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(controller->view != nullptr, V3_NOT_INITIALISED); @@ -1479,7 +1522,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** { - d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 97, self, name); + d_stdout("dpf_edit_controller::create_view => %p %s", self, name); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); @@ -1518,7 +1561,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_audio_processor::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 97, self, tuid2str(iid), iface); + d_stdout("dpf_audio_processor::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -1536,7 +1579,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_audio_processor::ref => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); @@ -1545,7 +1588,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_audio_processor::unref => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); @@ -1564,7 +1607,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result { - d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 97, self, inputs, num_inputs, outputs, num_outputs); + d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", self, inputs, num_inputs, outputs, num_outputs); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); @@ -1577,7 +1620,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.get_bus_arrangement = []V3_API(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement* arr) -> v3_result { - d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i %p", __PRETTY_FUNCTION__ + 97, self, bus_direction, idx, arr); + d_stdout("dpf_audio_processor::get_bus_arrangement => %p %i %i %p", self, bus_direction, idx, arr); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); @@ -1589,13 +1632,13 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.can_process_sample_size = []V3_API(void* self, int32_t symbolic_sample_size) -> v3_result { - d_stdout("dpf_audio_processor::can_process_sample_size => %s | %p %i", __PRETTY_FUNCTION__ + 97, self, symbolic_sample_size); + d_stdout("dpf_audio_processor::can_process_sample_size => %p %i", self, symbolic_sample_size); return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED; }; processor.get_latency_samples = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_audio_processor::get_latency_samples => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); @@ -1607,7 +1650,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.setup_processing = []V3_API(void* self, v3_process_setup* setup) -> v3_result { - d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_audio_processor::setup_processing => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); @@ -1621,7 +1664,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.set_processing = []V3_API(void* self, v3_bool state) -> v3_result { - d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 97, self, state); + d_stdout("dpf_audio_processor::set_processing => %p %u", self, state); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); @@ -1634,7 +1677,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.process = []V3_API(void* self, v3_process_data* data) -> v3_result { // NOTE runs during RT - // d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 97, self); + // d_stdout("dpf_audio_processor::process => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); @@ -1646,7 +1689,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { processor.get_tail_samples = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 97, self); + d_stdout("dpf_audio_processor::get_tail_samples => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); @@ -1688,7 +1731,7 @@ struct dpf_component : v3_component_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_component::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 41, self, tuid2str(iid), iface); + d_stdout("dpf_component::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; dpf_component* const component = *(dpf_component**)self; @@ -1724,7 +1767,7 @@ struct dpf_component : v3_component_cpp { ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_component::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_component::ref => %p", self); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, 0); @@ -1733,7 +1776,7 @@ struct dpf_component : v3_component_cpp { unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_component::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_component::unref => %p", self); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, 0); @@ -1750,7 +1793,7 @@ struct dpf_component : v3_component_cpp { base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown* context) -> v3_result { - d_stdout("dpf_component::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 41, self, context); + d_stdout("dpf_component::initialise => %p %p", self, context); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1769,7 +1812,7 @@ struct dpf_component : v3_component_cpp { base.terminate = []V3_API(void* self) -> v3_result { - d_stdout("dpf_component::terminate => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_component::terminate => %p", self); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1785,7 +1828,7 @@ struct dpf_component : v3_component_cpp { comp.get_controller_class_id = []V3_API(void* self, v3_tuid class_id) -> v3_result { - d_stdout("dpf_component::get_controller_class_id => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, tuid2str(class_id)); + d_stdout("dpf_component::get_controller_class_id => %p %s", self, tuid2str(class_id)); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1798,7 +1841,7 @@ struct dpf_component : v3_component_cpp { comp.set_io_mode = []V3_API(void* self, int32_t io_mode) -> v3_result { - d_stdout("dpf_component::set_io_mode => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, io_mode); + d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1812,7 +1855,7 @@ struct dpf_component : v3_component_cpp { comp.get_bus_count = []V3_API(void* self, int32_t media_type, int32_t bus_direction) -> int32_t { // NOTE runs during RT - // d_stdout("dpf_component::get_bus_count => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction); + // d_stdout("dpf_component::get_bus_count => %p %i %i", self, media_type, bus_direction); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1825,7 +1868,7 @@ struct dpf_component : v3_component_cpp { comp.get_bus_info = []V3_API(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bus_info* info) -> v3_result { - d_stdout("dpf_component::get_bus_info => %s | %p %i %i %i %p", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, info); + d_stdout("dpf_component::get_bus_info => %p %i %i %i %p", self, media_type, bus_direction, bus_idx, info); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1837,21 +1880,20 @@ struct dpf_component : v3_component_cpp { comp.get_routing_info = []V3_API(void* self, v3_routing_info* input, v3_routing_info* output) -> v3_result { - d_stdout("dpf_component::get_routing_info => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, input, output); + d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - // TODO - return V3_NOT_IMPLEMENTED; + return vst3->getRoutingInfo(input, output); }; comp.activate_bus = []V3_API(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bool state) -> v3_result { - d_stdout("dpf_component::activate_bus => %s | %p %i %i %i %u", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, state); + d_stdout("dpf_component::activate_bus => %p %i %i %i %u", self, media_type, bus_direction, bus_idx, state); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1863,7 +1905,7 @@ struct dpf_component : v3_component_cpp { comp.set_active = []V3_API(void* self, v3_bool state) -> v3_result { - d_stdout("dpf_component::set_active => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); + d_stdout("dpf_component::set_active => %p %u", self, state); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1875,7 +1917,7 @@ struct dpf_component : v3_component_cpp { comp.set_state = []V3_API(void* self, v3_bstream** stream) -> v3_result { - d_stdout("dpf_component::set_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_component::set_state => %p", self); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1887,7 +1929,7 @@ struct dpf_component : v3_component_cpp { comp.get_state = []V3_API(void* self, v3_bstream** stream) -> v3_result { - d_stdout("dpf_component::get_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); + d_stdout("dpf_component::get_state => %p", self); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1923,7 +1965,7 @@ struct dpf_factory : v3_plugin_factory_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_factory::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 37, self, tuid2str(iid), iface); + d_stdout("dpf_factory::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -1942,13 +1984,13 @@ struct dpf_factory : v3_plugin_factory_cpp { // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_factory::ref => %s | %p", __PRETTY_FUNCTION__ + 37, self); + d_stdout("dpf_factory::ref => %p", self); return 1; }; unref = []V3_API(void* self) -> uint32_t { - d_stdout("dpf_factory::unref => %s | %p", __PRETTY_FUNCTION__ + 37, self); + d_stdout("dpf_factory::unref => %p", self); return 0; }; @@ -1957,7 +1999,7 @@ struct dpf_factory : v3_plugin_factory_cpp { v1.get_factory_info = []V3_API(void* self, struct v3_factory_info* const info) -> v3_result { - d_stdout("dpf_factory::get_factory_info => %s | %p %p", __PRETTY_FUNCTION__ + 37, self, info); + d_stdout("dpf_factory::get_factory_info => %p %p", self, info); DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo->getHomePage(), sizeof(info->url)); DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO @@ -1966,13 +2008,13 @@ struct dpf_factory : v3_plugin_factory_cpp { v1.num_classes = []V3_API(void* self) -> int32_t { - d_stdout("dpf_factory::num_classes => %s | %p", __PRETTY_FUNCTION__ + 37, self); + d_stdout("dpf_factory::num_classes => %p", self); return 1; }; v1.get_class_info = []V3_API(void* self, int32_t idx, struct v3_class_info* const info) -> v3_result { - d_stdout("dpf_factory::get_class_info => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); + d_stdout("dpf_factory::get_class_info => %p %i %p", self, idx, info); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); @@ -1984,7 +2026,7 @@ struct dpf_factory : v3_plugin_factory_cpp { v1.create_instance = []V3_API(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) -> v3_result { - d_stdout("dpf_factory::create_instance => %s | %p %s %s %p", __PRETTY_FUNCTION__ + 37, self, tuid2str(class_id), tuid2str(iid), instance); + d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); @@ -2002,7 +2044,7 @@ struct dpf_factory : v3_plugin_factory_cpp { v2.get_class_info_2 = []V3_API(void* self, int32_t idx, struct v3_class_info_2* info) -> v3_result { - d_stdout("dpf_factory::get_class_info_2 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); + d_stdout("dpf_factory::get_class_info_2 => %p %i %p", self, idx, info); memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); @@ -2021,7 +2063,7 @@ struct dpf_factory : v3_plugin_factory_cpp { v3.get_class_info_utf16 = []V3_API(void* self, int32_t idx, struct v3_class_info_3* info) -> v3_result { - d_stdout("dpf_factory::get_class_info_utf16 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); + d_stdout("dpf_factory::get_class_info_utf16 => %p %i %p", self, idx, info); memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); @@ -2037,7 +2079,7 @@ struct dpf_factory : v3_plugin_factory_cpp { v3.set_host_context = []V3_API (void* self, struct v3_funknown* host) -> v3_result { - d_stdout("dpf_factory::set_host_context => %s | %p %p", __PRETTY_FUNCTION__ + 37, self, host); + d_stdout("dpf_factory::set_host_context => %p %p", self, host); return V3_NOT_IMPLEMENTED; }; } From c1684b1078a5f902fe5805689a1cacac621b6807 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 00:37:54 +0100 Subject: [PATCH 063/504] Setup/testing basic VST3 UI interaction Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 239 +++++++++++++++++++++++++----- 1 file changed, 205 insertions(+), 34 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 3b24f576..63689a32 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -762,14 +762,14 @@ public: return V3_NOT_IMPLEMENTED; }; - v3_result normalisedParameterToPlain(const v3_param_id index, const double normalised) + double normalisedParameterToPlain(const v3_param_id index, const double normalised) { DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); return fPlugin.getParameterRanges(index).getUnnormalizedValue(normalised); }; - v3_result plainParameterToNormalised(const v3_param_id index, const double plain) + double plainParameterToNormalised(const v3_param_id index, const double plain) { DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); @@ -856,6 +856,10 @@ static const sendNoteFunc sendNoteCallback = nullptr; static const setStateFunc setStateCallback = nullptr; #endif +struct v3_component_handler_cpp : v3_funknown { + v3_component_handler handler; +}; + class UIVst3 : public Thread { public: @@ -931,6 +935,17 @@ public: return fUI.getScaleFactor(); } + void notifyScaleFactorChanged(const double scaleFactor) + { + fUI.notifyScaleFactorChanged(scaleFactor); + } + + // TODO dont use this + void setParameterValueFromDSP(const uint32_t index, const float value) + { + fUI.parameterChanged(index, value); + } + // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view interface calls @@ -939,7 +954,7 @@ public: frame = f; } - void setHandler(v3_component_handler** const h) noexcept + void setHandler(v3_component_handler_cpp** const h) noexcept { handler = h; } @@ -947,18 +962,30 @@ public: // ---------------------------------------------------------------------------------------------------------------- protected: - void editParameter(const uint32_t /*index*/, const bool /*started*/) const + void editParameter(const uint32_t index, const bool started) const { - // hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index); + DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); + + v3_component_handler_cpp* const chandler = *handler; + DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); + + if (started) + chandler->handler.begin_edit(handler, index); + else + chandler->handler.end_edit(handler, index); } - void setParameterValue(const uint32_t /*index*/, const float /*realValue*/) + void setParameterValue(const uint32_t index, const float realValue) { - // const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); - // const float perValue(ranges.getNormalizedValue(realValue)); + DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); + + v3_component_handler_cpp* const chandler = *handler; + DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); - // fPlugin->setParameterValue(index, realValue); - // hostCallback(audioMasterAutomate, index, 0, nullptr, perValue); + const double value = vst3->plainParameterToNormalised(index, realValue); + chandler->handler.perform_edit(handler, index, value); + + // TODO send change to DSP side? } void setSize(uint width, uint height) @@ -1001,7 +1028,7 @@ private: // VST3 stuff ScopedPointer& vst3; v3_plugin_frame* frame; - v3_component_handler** handler = nullptr; + v3_component_handler_cpp** handler = nullptr; // Plugin UI UIExporter fUI; @@ -1053,25 +1080,116 @@ private: static ScopedPointer gPluginInfo; +#if DISTRHO_PLUGIN_HAS_UI +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view_content_scale + +struct v3_plugin_view_content_scale_steinberg_cpp : v3_funknown { + v3_plugin_view_content_scale_steinberg scale; +}; + +struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { + std::atomic refcounter; + ScopedPointer* self; + ScopedPointer& uivst3; + float lastScaleFactor = 0.0f; + + dpf_plugin_view_scale(ScopedPointer* s, ScopedPointer& v) + : refcounter(1), + self(s), + uivst3(v) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_plugin_view_content_scale_steinberg_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_plugin_view_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view_scale::ref => %p", self); + dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); + + return ++scale->refcounter; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view_scale::unref => %p", self); + dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); + + if (const int refcounter = --scale->refcounter) + return refcounter; + + *(dpf_plugin_view_scale**)self = nullptr; + *scale->self = nullptr; + return 0; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_view_content_scale_steinberg + + scale.set_content_scale_factor = []V3_API(void* self, float factor) -> v3_result + { + d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); + dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALISED); + + if (UIVst3* const uivst3 = scale->uivst3) + if (d_isNotZero(scale->lastScaleFactor) && d_isNotEqual(scale->lastScaleFactor, factor)) + uivst3->notifyScaleFactorChanged(factor); + + scale->lastScaleFactor = factor; + return V3_OK; + }; + } +}; + // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view -#if DISTRHO_PLUGIN_HAS_UI struct v3_plugin_view_cpp : v3_funknown { v3_plugin_view view; }; struct dpf_plugin_view : v3_plugin_view_cpp { + std::atomic refcounter; + ScopedPointer* self; + ScopedPointer scale; ScopedPointer& vst3; ScopedPointer uivst3; - double lastScaleFactor = 0.0; - v3_component_handler** handler = nullptr; + // cached values + v3_component_handler_cpp** handler = nullptr; v3_plugin_frame* hostframe = nullptr; - dpf_plugin_view(ScopedPointer& v) - : vst3(v) + dpf_plugin_view(ScopedPointer* s, ScopedPointer& v) + : refcounter(1), + self(s), + vst3(v) { - static const uint8_t* kSupportedFactories[] = { + static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, v3_plugin_view_iid }; @@ -1085,28 +1203,49 @@ struct dpf_plugin_view : v3_plugin_view_cpp { *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* factory_iid : kSupportedFactories) + for (const uint8_t* interface_iid : kSupportedInterfacesBase) { - if (v3_tuid_match(factory_iid, iid)) + if (v3_tuid_match(interface_iid, iid)) { *iface = self; return V3_OK; } } + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); + + if (v3_tuid_match(v3_plugin_view_content_scale_steinberg_iid, iid)) + { + if (view->scale == nullptr) + view->scale = new dpf_plugin_view_scale(&view->scale, view->uivst3); + *iface = &view->scale; + return V3_OK; + } + return V3_NO_INTERFACE; }; - // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_plugin_view::ref => %p", self); - return 1; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); + + return ++view->refcounter; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_plugin_view::unref => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); + + if (const int refcounter = --view->refcounter) + return refcounter; + + *(dpf_plugin_view**)self = nullptr; + *view->self = nullptr; return 0; }; @@ -1141,7 +1280,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); - view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, view->lastScaleFactor); + + const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; + view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, scaleFactor); view->uivst3->setHandler(view->handler); return V3_OK; }; @@ -1203,9 +1344,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { } else { + const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; UIExporter tmpUI(nullptr, 0, view->vst3->getSampleRate(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->vst3->getInstancePointer(), view->lastScaleFactor); + view->vst3->getInstancePointer(), scaleFactor); rect->right = tmpUI.getWidth(); rect->bottom = tmpUI.getHeight(); # ifdef DISTRHO_OS_MAC @@ -1284,6 +1426,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ScopedPointer view; ScopedPointer& vst3; bool initialized; + // cached values + v3_component_handler_cpp** handler = nullptr; dpf_edit_controller(ScopedPointer* const s, ScopedPointer& v) : refcounter(1), @@ -1502,6 +1646,15 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); vst3->setParameterNormalized(index, normalised); + + if (dpf_plugin_view* const view = controller->view) + { + if (UIVst3* const uivst3 = view->uivst3) + { + uivst3->setParameterValueFromDSP(index, vst3->normalisedParameterToPlain(index, normalised)); + } + } + return V3_OK; }; @@ -1510,12 +1663,18 @@ struct dpf_edit_controller : v3_edit_controller_cpp { d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(controller->view != nullptr, V3_NOT_INITIALISED); - controller->view->handler = handler; + v3_component_handler_cpp** const cpphandler = (v3_component_handler_cpp**)handler; - if (UIVst3* const uivst3 = controller->view->uivst3) - uivst3->setHandler(handler); + controller->handler = cpphandler; + + if (controller->view != nullptr) + { + controller->view->handler = cpphandler; + + if (UIVst3* const uivst3 = controller->view->uivst3) + uivst3->setHandler(cpphandler); + } return V3_OK; }; @@ -1527,7 +1686,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); if (controller->view == nullptr) - controller->view = new dpf_plugin_view(controller->vst3); + { + controller->view = new dpf_plugin_view(&controller->view, controller->vst3); + controller->view->handler = controller->handler; + } return (v3_plugin_view**)&controller->view; }; @@ -1714,13 +1876,14 @@ struct dpf_component : v3_component_cpp { ScopedPointer* self; ScopedPointer processor; ScopedPointer controller; + ScopedPointer view; ScopedPointer vst3; dpf_component(ScopedPointer* const s) : refcounter(1), self(s) { - static const uint8_t* kSupportedBaseFactories[] = { + static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, v3_plugin_base_iid, v3_component_iid @@ -1734,18 +1897,18 @@ struct dpf_component : v3_component_cpp { d_stdout("dpf_component::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* factory_iid : kSupportedBaseFactories) + for (const uint8_t* interface_iid : kSupportedInterfacesBase) { - if (v3_tuid_match(factory_iid, iid)) + if (v3_tuid_match(interface_iid, iid)) { *iface = self; return V3_OK; } } + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NO_INTERFACE); + if (v3_tuid_match(v3_audio_processor_iid, iid)) { if (component->processor == nullptr) @@ -1762,6 +1925,14 @@ struct dpf_component : v3_component_cpp { return V3_OK; } + if (v3_tuid_match(v3_plugin_view_iid, iid)) + { + if (component->view == nullptr) + component->view = new dpf_plugin_view(&component->view, component->vst3); + *iface = &component->view; + return V3_OK; + } + return V3_NO_INTERFACE; }; From 60cf219e34c0d14e1b0fae460b66b31296e614cd Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 10:48:34 +0100 Subject: [PATCH 064/504] Do not set exported symbols list if building under DEBUG Signed-off-by: falkTX --- Makefile.plugins.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 38df652f..d6a24653 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -127,7 +127,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 -else +else ifneq ($(DEBUG),true) 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 From 3343e7d770c57ba8a5d3a737dfe0ae271c8219ea Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 10:49:06 +0100 Subject: [PATCH 065/504] Move VST3 UI code into a separate file, non-working for now Signed-off-by: falkTX --- distrho/src/DISTRHOUIVST3.cpp | 666 ++++++++++++++++++++++++++++ distrho/src/DistrhoPluginVST3.cpp | 702 ++---------------------------- 2 files changed, 700 insertions(+), 668 deletions(-) create mode 100644 distrho/src/DISTRHOUIVST3.cpp diff --git a/distrho/src/DISTRHOUIVST3.cpp b/distrho/src/DISTRHOUIVST3.cpp new file mode 100644 index 00000000..cbd9b6e5 --- /dev/null +++ b/distrho/src/DISTRHOUIVST3.cpp @@ -0,0 +1,666 @@ +/* + * 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 "DistrhoUIInternal.hpp" + +#include "travesty/audio_processor.h" +#include "travesty/component.h" +#include "travesty/edit_controller.h" +#include "travesty/factory.h" +#include "travesty/view.h" + +#include + +// TESTING awful idea dont reuse +#include "../extra/Thread.hpp" + +// #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI +// # undef DISTRHO_PLUGIN_HAS_UI +// # define DISTRHO_PLUGIN_HAS_UI 0 +// #endif +// +// #if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +// # undef DISTRHO_PLUGIN_HAS_UI +// # define DISTRHO_PLUGIN_HAS_UI 0 +// #endif +// +// #undef DISTRHO_PLUGIN_HAS_UI +// #define DISTRHO_PLUGIN_HAS_UI 1 + +// #if DISTRHO_PLUGIN_HAS_UI +# include "DistrhoUIInternal.hpp" +// #endif + +// -------------------------------------------------------------------------------------------------------------------- + +struct v3_component_handler_cpp : v3_funknown { + v3_component_handler handler; +}; + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static const sendNoteFunc sendNoteCallback = nullptr; +#endif +#if ! DISTRHO_PLUGIN_WANT_STATE +static const setStateFunc setStateCallback = nullptr; +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// custom v3_tuid compatible type + +typedef uint32_t dpf_tuid[4]; +static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch"); + +// -------------------------------------------------------------------------------------------------------------------- +// Utility functions + +const char* tuid2str(const v3_tuid iid) +{ + if (v3_tuid_match(iid, v3_funknown_iid)) + return "{v3_funknown}"; + if (v3_tuid_match(iid, v3_plugin_base_iid)) + return "{v3_plugin_base}"; + if (v3_tuid_match(iid, v3_plugin_factory_iid)) + return "{v3_plugin_factory}"; + if (v3_tuid_match(iid, v3_plugin_factory_2_iid)) + return "{v3_plugin_factory_2}"; + if (v3_tuid_match(iid, v3_plugin_factory_3_iid)) + return "{v3_plugin_factory_3}"; + if (v3_tuid_match(iid, v3_component_iid)) + return "{v3_component}"; + if (v3_tuid_match(iid, v3_bstream_iid)) + return "{v3_bstream}"; + if (v3_tuid_match(iid, v3_event_list_iid)) + return "{v3_event_list}"; + if (v3_tuid_match(iid, v3_param_value_queue_iid)) + return "{v3_param_value_queue}"; + if (v3_tuid_match(iid, v3_param_changes_iid)) + return "{v3_param_changes}"; + if (v3_tuid_match(iid, v3_process_context_requirements_iid)) + return "{v3_process_context_requirements}"; + if (v3_tuid_match(iid, v3_audio_processor_iid)) + return "{v3_audio_processor}"; + if (v3_tuid_match(iid, v3_component_handler_iid)) + return "{v3_component_handler}"; + if (v3_tuid_match(iid, v3_edit_controller_iid)) + return "{v3_edit_controller}"; + if (v3_tuid_match(iid, v3_plugin_view_iid)) + return "{v3_plugin_view}"; + if (v3_tuid_match(iid, v3_plugin_frame_iid)) + return "{v3_plugin_frame}"; + if (v3_tuid_match(iid, v3_plugin_view_content_scale_steinberg_iid)) + return "{v3_plugin_view_content_scale_steinberg}"; + if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid)) + return "{v3_plugin_view_parameter_finder}"; + /* + if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_class}"; + if (std::memcmp(iid, dpf_tuid_component, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_component}"; + if (std::memcmp(iid, dpf_tuid_controller, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_controller}"; + if (std::memcmp(iid, dpf_tuid_processor, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_processor}"; + if (std::memcmp(iid, dpf_tuid_view, sizeof(dpf_tuid)) == 0) + return "{dpf_tuid_view}"; + */ + + static char buf[46]; + std::snprintf(buf, sizeof(buf), "{0x%08X,0x%08X,0x%08X,0x%08X}", + (uint32_t)d_cconst(iid[ 0], iid[ 1], iid[ 2], iid[ 3]), + (uint32_t)d_cconst(iid[ 4], iid[ 5], iid[ 6], iid[ 7]), + (uint32_t)d_cconst(iid[ 8], iid[ 9], iid[10], iid[11]), + (uint32_t)d_cconst(iid[12], iid[13], iid[14], iid[15])); + return buf; +} + +// -------------------------------------------------------------------------------------------------------------------- + +class UIVst3 : public Thread +{ +public: + UIVst3(const intptr_t winId, const float scaleFactor, const double sampleRate, void* const instancePointer) + : fUI(this, winId, sampleRate, + editParameterCallback, + setParameterCallback, + setStateCallback, + sendNoteCallback, + setSizeCallback, + nullptr, // TODO file request + nullptr, + instancePointer, + scaleFactor) + { + // TESTING awful idea dont reuse + startThread(); + } + + ~UIVst3() override + { + stopThread(5000); + } + + // ------------------------------------------------------------------- + + // TESTING awful idea dont reuse + void run() override + { + while (! shouldThreadExit()) + { + idle(); + d_msleep(50); + } + } + + void idle() + { + /* + for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) + { + if (fUiHelper->parameterChecks[i]) + { + fUiHelper->parameterChecks[i] = false; + fUI.parameterChanged(i, fUiHelper->parameterValues[i]); + } + } + */ + + fUI.plugin_idle(); + } + + int16_t getWidth() const + { + return fUI.getWidth(); + } + + int16_t getHeight() const + { + return fUI.getHeight(); + } + + double getScaleFactor() const + { + return fUI.getScaleFactor(); + } + + void notifyScaleFactorChanged(const double scaleFactor) + { + fUI.notifyScaleFactorChanged(scaleFactor); + } + + // TODO dont use this + void setParameterValueFromDSP(const uint32_t index, const float value) + { + fUI.parameterChanged(index, value); + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_view interface calls + + void setFrame(v3_plugin_frame* const f) noexcept + { + frame = f; + } + + void setHandler(v3_component_handler_cpp** const h) noexcept + { + handler = h; + } + + // ---------------------------------------------------------------------------------------------------------------- + +protected: + void editParameter(const uint32_t index, const bool started) const + { + DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); + + v3_component_handler_cpp* const chandler = *handler; + DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); + + if (started) + chandler->handler.begin_edit(handler, index); + else + chandler->handler.end_edit(handler, index); + } + + void setParameterValue(const uint32_t index, const float realValue) + { + DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); + + v3_component_handler_cpp* const chandler = *handler; + DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); + + const double value = vst3->plainParameterToNormalised(index, realValue); + chandler->handler.perform_edit(handler, index, value); + + // TODO send change to DSP side? + } + + void setSize(uint width, uint height) + { +# ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + width /= scaleFactor; + height /= scaleFactor; +# endif + if (frame == nullptr) + return; + + v3_view_rect rect = {}; + rect.right = width; + rect.bottom = height; + (void)rect; + // frame->resize_view(nullptr, uivst3, &rect); + } + +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + uint8_t midiData[3]; + midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; + midiData[1] = note; + midiData[2] = velocity; + fNotesRingBuffer.writeCustomData(midiData, 3); + fNotesRingBuffer.commitWrite(); + } +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* const key, const char* const value) + { + // fUiHelper->setStateFromUI(key, value); + } +# endif + +private: + // VST3 stuff + v3_plugin_frame* frame; + v3_component_handler_cpp** handler = nullptr; + + // Plugin UI + UIExporter fUI; + + // ------------------------------------------------------------------- + // Callbacks + + #define handlePtr ((UIVst3*)ptr) + + static void editParameterCallback(void* ptr, uint32_t index, bool started) + { + handlePtr->editParameter(index, started); + } + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + handlePtr->setParameterValue(rindex, value); + } + + static void setSizeCallback(void* ptr, uint width, uint height) + { + handlePtr->setSize(width, height); + } + +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + handlePtr->sendNote(channel, note, velocity); + } +# endif + +# if DISTRHO_PLUGIN_WANT_STATE + static void setStateCallback(void* ptr, const char* key, const char* value) + { + handlePtr->setState(key, value); + } +# endif + + #undef handlePtr +}; + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view_content_scale + +struct v3_plugin_view_content_scale_steinberg_cpp : v3_funknown { + v3_plugin_view_content_scale_steinberg scale; +}; + +struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { + std::atomic refcounter; + ScopedPointer* self; + ScopedPointer& uivst3; + float lastScaleFactor = 0.0f; + + dpf_plugin_view_scale(ScopedPointer* s, ScopedPointer& v) + : refcounter(1), + self(s), + uivst3(v) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_plugin_view_content_scale_steinberg_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_plugin_view_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view_scale::ref => %p", self); + dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); + + return ++scale->refcounter; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view_scale::unref => %p", self); + dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); + + if (const int refcounter = --scale->refcounter) + return refcounter; + + *(dpf_plugin_view_scale**)self = nullptr; + *scale->self = nullptr; + return 0; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_view_content_scale_steinberg + + scale.set_content_scale_factor = []V3_API(void* self, float factor) -> v3_result + { + d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); + dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALISED); + + if (UIVst3* const uivst3 = scale->uivst3) + if (d_isNotZero(scale->lastScaleFactor) && d_isNotEqual(scale->lastScaleFactor, factor)) + uivst3->notifyScaleFactorChanged(factor); + + scale->lastScaleFactor = factor; + return V3_OK; + }; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view + +struct v3_plugin_view_cpp : v3_funknown { + v3_plugin_view view; +}; + +struct dpf_plugin_view : v3_plugin_view_cpp { + std::atomic refcounter; + ScopedPointer* self; + ScopedPointer scale; + ScopedPointer uivst3; + // cached values + v3_component_handler_cpp** handler = nullptr; + v3_plugin_frame* hostframe = nullptr; + + dpf_plugin_view(ScopedPointer* s) + : refcounter(1), + self(s) + { + static const uint8_t* kSupportedInterfacesBase[] = { + v3_funknown_iid, + v3_plugin_view_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfacesBase) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); + + if (v3_tuid_match(v3_plugin_view_content_scale_steinberg_iid, iid)) + { + if (view->scale == nullptr) + view->scale = new dpf_plugin_view_scale(&view->scale, view->uivst3); + *iface = &view->scale; + return V3_OK; + } + + return V3_NO_INTERFACE; + }; + + ref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view::ref => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); + + return ++view->refcounter; + }; + + unref = []V3_API(void* self) -> uint32_t + { + d_stdout("dpf_plugin_view::unref => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); + + if (const int refcounter = --view->refcounter) + return refcounter; + + *(dpf_plugin_view**)self = nullptr; + *view->self = nullptr; + return 0; + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_view + + view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result + { + d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); + const char* const supported[] = { +#ifdef _WIN32 + V3_VIEW_PLATFORM_TYPE_HWND, +#elif defined(__APPLE__) + V3_VIEW_PLATFORM_TYPE_NSVIEW, +#else + V3_VIEW_PLATFORM_TYPE_X11, +#endif + }; + + for (size_t i=0; i v3_result + { + d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); + + const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; + view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, scaleFactor); + view->uivst3->setHandler(view->handler); + return V3_OK; + }; + + view.removed = []V3_API(void* self) -> v3_result + { + d_stdout("dpf_plugin_view::removed => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); + view->uivst3 = nullptr; + return V3_OK; + }; + + view.on_wheel = []V3_API(void* self, float distance) -> v3_result + { + d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; + }; + + view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result + { + d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; + }; + + view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result + { + d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; + }; + + view.get_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result + { + d_stdout("dpf_plugin_view::get_size => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + + std::memset(rect, 0, sizeof(v3_view_rect)); + + if (view->uivst3 != nullptr) + { + rect->right = view->uivst3->getWidth(); + rect->bottom = view->uivst3->getHeight(); +# ifdef DISTRHO_OS_MAC + const double scaleFactor = view->uivst3->getScaleFactor(); + rect->right /= scaleFactor; + rect->bottom /= scaleFactor; +# endif + } + else + { + const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; + UIExporter tmpUI(nullptr, 0, view->vst3->getSampleRate(), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + view->vst3->getInstancePointer(), scaleFactor); + rect->right = tmpUI.getWidth(); + rect->bottom = tmpUI.getHeight(); +# ifdef DISTRHO_OS_MAC + const double scaleFactor = tmpUI.getScaleFactor(); + rect->right /= scaleFactor; + rect->bottom /= scaleFactor; +# endif + tmpUI.quit(); + } + + return V3_NOT_IMPLEMENTED; + }; + + view.set_size = []V3_API(void* self, v3_view_rect*) -> v3_result + { + d_stdout("dpf_plugin_view::set_size => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; + }; + + view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result + { + d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); + return V3_NOT_IMPLEMENTED; + }; + + view.set_frame = []V3_API(void* self, v3_plugin_frame* frame) -> v3_result + { + d_stdout("dpf_plugin_view::set_frame => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + + view->hostframe = frame; + + if (view->uivst3 != nullptr) + view->uivst3->setFrame(frame); + + return V3_OK; + }; + + view.can_resize = []V3_API(void* self) -> v3_result + { + d_stdout("dpf_plugin_view::can_resize => %p", self); +#if DISTRHO_UI_USER_RESIZABLE + return V3_OK; +#else + return V3_NOT_IMPLEMENTED; +#endif + }; + + view.check_size_constraint = []V3_API(void* self, v3_view_rect*) -> v3_result + { + d_stdout("dpf_plugin_view::check_size_constraint => %p", self); + return V3_NOT_IMPLEMENTED; + }; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 63689a32..d9ce97d3 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -23,29 +23,10 @@ #include "travesty/factory.h" #include -#include // TESTING awful idea dont reuse #include "../extra/Thread.hpp" -#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#undef DISTRHO_PLUGIN_HAS_UI -#define DISTRHO_PLUGIN_HAS_UI 1 - -#if DISTRHO_PLUGIN_HAS_UI -# include "DistrhoUIInternal.hpp" -# include "../extra/RingBuffer.hpp" -#endif - START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- @@ -182,53 +163,6 @@ void strncpy_16from8(int16_t* const dst, const char* const src, const size_t siz // -------------------------------------------------------------------------------------------------------------------- -struct ParameterAndNotesHelper -{ - float* parameterValues; -#if DISTRHO_PLUGIN_HAS_UI - bool* parameterChecks; -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - SmallStackBuffer notesRingBuffer; -# endif -#endif - - ParameterAndNotesHelper() - : parameterValues(nullptr) -#if DISTRHO_PLUGIN_HAS_UI - , parameterChecks(nullptr) -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - , notesRingBuffer(StackBuffer_INIT) -# endif -#endif - { -#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) - std::memset(¬esRingBuffer, 0, sizeof(notesRingBuffer)); -#endif - } - - virtual ~ParameterAndNotesHelper() - { - if (parameterValues != nullptr) - { - delete[] parameterValues; - parameterValues = nullptr; - } -#if DISTRHO_PLUGIN_HAS_UI - if (parameterChecks != nullptr) - { - delete[] parameterChecks; - parameterChecks = nullptr; - } -#endif - } - -#if DISTRHO_PLUGIN_WANT_STATE - virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0; -#endif -}; - -// -------------------------------------------------------------------------------------------------------------------- - class PluginVst3 { /* buses: we provide 1 for the main audio (if there is any) plus 1 for each sidechain or cv port. @@ -845,573 +779,6 @@ private: }; -// -------------------------------------------------------------------------------------------------------------------- - -#if DISTRHO_PLUGIN_HAS_UI - -#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT -static const sendNoteFunc sendNoteCallback = nullptr; -#endif -#if ! DISTRHO_PLUGIN_WANT_STATE -static const setStateFunc setStateCallback = nullptr; -#endif - -struct v3_component_handler_cpp : v3_funknown { - v3_component_handler handler; -}; - -class UIVst3 : public Thread -{ -public: - UIVst3(ScopedPointer& v, v3_plugin_frame* const f, const intptr_t winId, const float scaleFactor) - : vst3(v), - frame(f), - fUI(this, winId, v->getSampleRate(), - editParameterCallback, - setParameterCallback, - setStateCallback, - sendNoteCallback, - setSizeCallback, - nullptr, // TODO file request - nullptr, - v->getInstancePointer(), - scaleFactor) -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - , fNotesRingBuffer() -# endif - { -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - fNotesRingBuffer.setRingBuffer(&uiHelper->notesRingBuffer, false); -# endif - // TESTING awful idea dont reuse - startThread(); - } - - ~UIVst3() override - { - stopThread(5000); - } - - // ------------------------------------------------------------------- - - // TESTING awful idea dont reuse - void run() override - { - while (! shouldThreadExit()) - { - idle(); - d_msleep(50); - } - } - - void idle() - { - /* - for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) - { - if (fUiHelper->parameterChecks[i]) - { - fUiHelper->parameterChecks[i] = false; - fUI.parameterChanged(i, fUiHelper->parameterValues[i]); - } - } - */ - - fUI.plugin_idle(); - } - - int16_t getWidth() const - { - return fUI.getWidth(); - } - - int16_t getHeight() const - { - return fUI.getHeight(); - } - - double getScaleFactor() const - { - return fUI.getScaleFactor(); - } - - void notifyScaleFactorChanged(const double scaleFactor) - { - fUI.notifyScaleFactorChanged(scaleFactor); - } - - // TODO dont use this - void setParameterValueFromDSP(const uint32_t index, const float value) - { - fUI.parameterChanged(index, value); - } - - // ---------------------------------------------------------------------------------------------------------------- - // v3_plugin_view interface calls - - void setFrame(v3_plugin_frame* const f) noexcept - { - frame = f; - } - - void setHandler(v3_component_handler_cpp** const h) noexcept - { - handler = h; - } - - // ---------------------------------------------------------------------------------------------------------------- - -protected: - void editParameter(const uint32_t index, const bool started) const - { - DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); - - v3_component_handler_cpp* const chandler = *handler; - DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); - - if (started) - chandler->handler.begin_edit(handler, index); - else - chandler->handler.end_edit(handler, index); - } - - void setParameterValue(const uint32_t index, const float realValue) - { - DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); - - v3_component_handler_cpp* const chandler = *handler; - DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); - - const double value = vst3->plainParameterToNormalised(index, realValue); - chandler->handler.perform_edit(handler, index, value); - - // TODO send change to DSP side? - } - - void setSize(uint width, uint height) - { -# ifdef DISTRHO_OS_MAC - const double scaleFactor = fUI.getScaleFactor(); - width /= scaleFactor; - height /= scaleFactor; -# endif - if (frame == nullptr) - return; - - v3_view_rect rect = {}; - rect.right = width; - rect.bottom = height; - (void)rect; - // frame->resize_view(nullptr, uivst3, &rect); - } - -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) - { - uint8_t midiData[3]; - midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; - midiData[1] = note; - midiData[2] = velocity; - fNotesRingBuffer.writeCustomData(midiData, 3); - fNotesRingBuffer.commitWrite(); - } -# endif - -# if DISTRHO_PLUGIN_WANT_STATE - void setState(const char* const key, const char* const value) - { - // fUiHelper->setStateFromUI(key, value); - } -# endif - -private: - // VST3 stuff - ScopedPointer& vst3; - v3_plugin_frame* frame; - v3_component_handler_cpp** handler = nullptr; - - // Plugin UI - UIExporter fUI; -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - RingBufferControl fNotesRingBuffer; -# endif - - // ------------------------------------------------------------------- - // Callbacks - - #define handlePtr ((UIVst3*)ptr) - - static void editParameterCallback(void* ptr, uint32_t index, bool started) - { - handlePtr->editParameter(index, started); - } - - static void setParameterCallback(void* ptr, uint32_t rindex, float value) - { - handlePtr->setParameterValue(rindex, value); - } - - static void setSizeCallback(void* ptr, uint width, uint height) - { - handlePtr->setSize(width, height); - } - -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) - { - handlePtr->sendNote(channel, note, velocity); - } -# endif - -# if DISTRHO_PLUGIN_WANT_STATE - static void setStateCallback(void* ptr, const char* key, const char* value) - { - handlePtr->setState(key, value); - } -# endif - - #undef handlePtr -}; - -#endif // DISTRHO_PLUGIN_HAS_UI - -// -------------------------------------------------------------------------------------------------------------------- -// Dummy plugin to get data from - -static ScopedPointer gPluginInfo; - -#if DISTRHO_PLUGIN_HAS_UI -// -------------------------------------------------------------------------------------------------------------------- -// dpf_plugin_view_content_scale - -struct v3_plugin_view_content_scale_steinberg_cpp : v3_funknown { - v3_plugin_view_content_scale_steinberg scale; -}; - -struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { - std::atomic refcounter; - ScopedPointer* self; - ScopedPointer& uivst3; - float lastScaleFactor = 0.0f; - - dpf_plugin_view_scale(ScopedPointer* s, ScopedPointer& v) - : refcounter(1), - self(s), - uivst3(v) - { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_plugin_view_content_scale_steinberg_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_plugin_view_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - ref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_plugin_view_scale::ref => %p", self); - dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); - - return ++scale->refcounter; - }; - - unref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_plugin_view_scale::unref => %p", self); - dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); - - if (const int refcounter = --scale->refcounter) - return refcounter; - - *(dpf_plugin_view_scale**)self = nullptr; - *scale->self = nullptr; - return 0; - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_view_content_scale_steinberg - - scale.set_content_scale_factor = []V3_API(void* self, float factor) -> v3_result - { - d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); - dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALISED); - - if (UIVst3* const uivst3 = scale->uivst3) - if (d_isNotZero(scale->lastScaleFactor) && d_isNotEqual(scale->lastScaleFactor, factor)) - uivst3->notifyScaleFactorChanged(factor); - - scale->lastScaleFactor = factor; - return V3_OK; - }; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- -// dpf_plugin_view - -struct v3_plugin_view_cpp : v3_funknown { - v3_plugin_view view; -}; - -struct dpf_plugin_view : v3_plugin_view_cpp { - std::atomic refcounter; - ScopedPointer* self; - ScopedPointer scale; - ScopedPointer& vst3; - ScopedPointer uivst3; - // cached values - v3_component_handler_cpp** handler = nullptr; - v3_plugin_frame* hostframe = nullptr; - - dpf_plugin_view(ScopedPointer* s, ScopedPointer& v) - : refcounter(1), - self(s), - vst3(v) - { - static const uint8_t* kSupportedInterfacesBase[] = { - v3_funknown_iid, - v3_plugin_view_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfacesBase) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); - - if (v3_tuid_match(v3_plugin_view_content_scale_steinberg_iid, iid)) - { - if (view->scale == nullptr) - view->scale = new dpf_plugin_view_scale(&view->scale, view->uivst3); - *iface = &view->scale; - return V3_OK; - } - - return V3_NO_INTERFACE; - }; - - ref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_plugin_view::ref => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); - - return ++view->refcounter; - }; - - unref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_plugin_view::unref => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); - - if (const int refcounter = --view->refcounter) - return refcounter; - - *(dpf_plugin_view**)self = nullptr; - *view->self = nullptr; - return 0; - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_view - - view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result - { - d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); - const char* const supported[] = { -#ifdef _WIN32 - V3_VIEW_PLATFORM_TYPE_HWND, -#elif defined(__APPLE__) - V3_VIEW_PLATFORM_TYPE_NSVIEW, -#else - V3_VIEW_PLATFORM_TYPE_X11, -#endif - }; - - for (size_t i=0; i v3_result - { - d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); - - const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; - view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, scaleFactor); - view->uivst3->setHandler(view->handler); - return V3_OK; - }; - - view.removed = []V3_API(void* self) -> v3_result - { - d_stdout("dpf_plugin_view::removed => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); - view->uivst3 = nullptr; - return V3_OK; - }; - - view.on_wheel = []V3_API(void* self, float distance) -> v3_result - { - d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; - }; - - view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result - { - d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; - }; - - view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result - { - d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; - }; - - view.get_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result - { - d_stdout("dpf_plugin_view::get_size => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - - std::memset(rect, 0, sizeof(v3_view_rect)); - - if (view->uivst3 != nullptr) - { - rect->right = view->uivst3->getWidth(); - rect->bottom = view->uivst3->getHeight(); -# ifdef DISTRHO_OS_MAC - const double scaleFactor = view->uivst3->getScaleFactor(); - rect->right /= scaleFactor; - rect->bottom /= scaleFactor; -# endif - } - else - { - const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; - UIExporter tmpUI(nullptr, 0, view->vst3->getSampleRate(), - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->vst3->getInstancePointer(), scaleFactor); - rect->right = tmpUI.getWidth(); - rect->bottom = tmpUI.getHeight(); -# ifdef DISTRHO_OS_MAC - const double scaleFactor = tmpUI.getScaleFactor(); - rect->right /= scaleFactor; - rect->bottom /= scaleFactor; -# endif - tmpUI.quit(); - } - - return V3_NOT_IMPLEMENTED; - }; - - view.set_size = []V3_API(void* self, v3_view_rect*) -> v3_result - { - d_stdout("dpf_plugin_view::set_size => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; - }; - - view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result - { - d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; - }; - - view.set_frame = []V3_API(void* self, v3_plugin_frame* frame) -> v3_result - { - d_stdout("dpf_plugin_view::set_frame => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - - view->hostframe = frame; - - if (view->uivst3 != nullptr) - view->uivst3->setFrame(frame); - - return V3_OK; - }; - - view.can_resize = []V3_API(void* self) -> v3_result - { - d_stdout("dpf_plugin_view::can_resize => %p", self); -#if DISTRHO_UI_USER_RESIZABLE - return V3_OK; -#else - return V3_NOT_IMPLEMENTED; -#endif - }; - - view.check_size_constraint = []V3_API(void* self, v3_view_rect*) -> v3_result - { - d_stdout("dpf_plugin_view::check_size_constraint => %p", self); - return V3_NOT_IMPLEMENTED; - }; - } -}; -#endif - // -------------------------------------------------------------------------------------------------------------------- // dpf_edit_controller @@ -1423,11 +790,11 @@ struct v3_edit_controller_cpp : v3_funknown { struct dpf_edit_controller : v3_edit_controller_cpp { std::atomic refcounter; ScopedPointer* self; - ScopedPointer view; + // ScopedPointer view; ScopedPointer& vst3; bool initialized; // cached values - v3_component_handler_cpp** handler = nullptr; + // v3_component_handler_cpp** handler = nullptr; dpf_edit_controller(ScopedPointer* const s, ScopedPointer& v) : refcounter(1), @@ -1647,13 +1014,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp { vst3->setParameterNormalized(index, normalised); - if (dpf_plugin_view* const view = controller->view) - { - if (UIVst3* const uivst3 = view->uivst3) - { - uivst3->setParameterValueFromDSP(index, vst3->normalisedParameterToPlain(index, normalised)); - } - } +// if (dpf_plugin_view* const view = controller->view) +// { +// if (UIVst3* const uivst3 = view->uivst3) +// { +// uivst3->setParameterValueFromDSP(index, vst3->normalisedParameterToPlain(index, normalised)); +// } +// } return V3_OK; }; @@ -1664,17 +1031,17 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); - v3_component_handler_cpp** const cpphandler = (v3_component_handler_cpp**)handler; - - controller->handler = cpphandler; - - if (controller->view != nullptr) - { - controller->view->handler = cpphandler; - - if (UIVst3* const uivst3 = controller->view->uivst3) - uivst3->setHandler(cpphandler); - } +// v3_component_handler_cpp** const cpphandler = (v3_component_handler_cpp**)handler; +// +// controller->handler = cpphandler; +// +// if (controller->view != nullptr) +// { +// controller->view->handler = cpphandler; +// +// if (UIVst3* const uivst3 = controller->view->uivst3) +// uivst3->setHandler(cpphandler); +// } return V3_OK; }; @@ -1685,13 +1052,15 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); - if (controller->view == nullptr) - { - controller->view = new dpf_plugin_view(&controller->view, controller->vst3); - controller->view->handler = controller->handler; - } + return nullptr; - return (v3_plugin_view**)&controller->view; +// if (controller->view == nullptr) +// { +// controller->view = new dpf_plugin_view(&controller->view, controller->vst3); +// controller->view->handler = controller->handler; +// } + +// return (v3_plugin_view**)&controller->view; }; } }; @@ -1876,7 +1245,7 @@ struct dpf_component : v3_component_cpp { ScopedPointer* self; ScopedPointer processor; ScopedPointer controller; - ScopedPointer view; +// ScopedPointer view; ScopedPointer vst3; dpf_component(ScopedPointer* const s) @@ -1925,14 +1294,6 @@ struct dpf_component : v3_component_cpp { return V3_OK; } - if (v3_tuid_match(v3_plugin_view_iid, iid)) - { - if (component->view == nullptr) - component->view = new dpf_plugin_view(&component->view, component->vst3); - *iface = &component->view; - return V3_OK; - } - return V3_NO_INTERFACE; }; @@ -2112,6 +1473,11 @@ struct dpf_component : v3_component_cpp { } }; +// -------------------------------------------------------------------------------------------------------------------- +// Dummy plugin to get data from + +static ScopedPointer gPluginInfo; + // -------------------------------------------------------------------------------------------------------------------- // dpf_factory From a6f3835cab87df29651640cf54d86b20dbecf060 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 11:10:14 +0100 Subject: [PATCH 066/504] VST3: Use local static plugin for factory info, not global Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 51 +++++++++++++------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index d9ce97d3..e502de1d 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1473,11 +1473,6 @@ struct dpf_component : v3_component_cpp { } }; -// -------------------------------------------------------------------------------------------------------------------- -// Dummy plugin to get data from - -static ScopedPointer gPluginInfo; - // -------------------------------------------------------------------------------------------------------------------- // dpf_factory @@ -1497,6 +1492,18 @@ struct dpf_factory : v3_plugin_factory_cpp { v3_plugin_factory_3_iid }; + // ------------------------------------------------------------------------------------------------------------ + // Dummy plugin to get data from + + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr); + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + + dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] + = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo.getUniqueId(); + // ------------------------------------------------------------------------------------------------------------ // v3_funknown @@ -1537,8 +1544,8 @@ struct dpf_factory : v3_plugin_factory_cpp { v1.get_factory_info = []V3_API(void* self, struct v3_factory_info* const info) -> v3_result { d_stdout("dpf_factory::get_factory_info => %p %p", self, info); - DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); - DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo->getHomePage(), sizeof(info->url)); + DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); + DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo.getHomePage(), sizeof(info->url)); DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO return V3_OK; }; @@ -1557,7 +1564,7 @@ struct dpf_factory : v3_plugin_factory_cpp { memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); - DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo->getName(), sizeof(info->name)); + DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo.getName(), sizeof(info->name)); return V3_OK; }; @@ -1585,12 +1592,12 @@ struct dpf_factory : v3_plugin_factory_cpp { memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); - DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo->getName(), sizeof(info->name)); + DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo.getName(), sizeof(info->name)); info->class_flags = 0; DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO - DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); - std::snprintf(info->version, sizeof(info->version), "%u", gPluginInfo->getVersion()); // TODO + DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); + std::snprintf(info->version, sizeof(info->version), "%u", gPluginInfo.getVersion()); // TODO DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; @@ -1604,12 +1611,12 @@ struct dpf_factory : v3_plugin_factory_cpp { memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); - DISTRHO_NAMESPACE::strncpy_16from8(info->name, gPluginInfo->getName(), sizeof(info->name)); + DISTRHO_NAMESPACE::strncpy_16from8(info->name, gPluginInfo.getName(), sizeof(info->name)); info->class_flags = 0; DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO - DISTRHO_NAMESPACE::strncpy_16from8(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); - // DISTRHO_NAMESPACE::snprintf16(info->version, sizeof(info->version)/sizeof(info->version[0]), "%u", gPluginInfo->getVersion()); // TODO + DISTRHO_NAMESPACE::strncpy_16from8(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); + // DISTRHO_NAMESPACE::snprintf16(info->version, sizeof(info->version)/sizeof(info->version[0]), "%u", gPluginInfo.getVersion()); // TODO DISTRHO_NAMESPACE::strncpy_16from8(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; @@ -1657,20 +1664,6 @@ bool ENTRYFNNAME(void*); bool ENTRYFNNAME(void*) { - USE_NAMESPACE_DISTRHO; - - if (gPluginInfo == nullptr) - { - d_lastBufferSize = 512; - d_lastSampleRate = 44100.0; - gPluginInfo = new PluginExporter(nullptr, nullptr, nullptr); - d_lastBufferSize = 0; - d_lastSampleRate = 0.0; - - dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] - = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo->getUniqueId(); - } - return true; } @@ -1679,8 +1672,6 @@ bool EXITFNNAME(void); bool EXITFNNAME(void) { - USE_NAMESPACE_DISTRHO; - gPluginInfo = nullptr; return true; } From 601212430b2026a32aeb610f834532f4eb9beb36 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 12:45:24 +0100 Subject: [PATCH 067/504] Add ARRAY_SIZE macro Signed-off-by: falkTX --- distrho/src/DistrhoDefines.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index afff900e..ffd407f1 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -199,6 +199,9 @@ private: \ # pragma warning(disable:4244) /* possible loss of data */ #endif +/* Useful macros */ +#define ARRAY_SIZE(ARRAY) sizeof(ARRAY)/sizeof(ARRAY[0]) + /* Useful typedefs */ typedef unsigned char uchar; typedef unsigned short int ushort; From 3b91a816a3971bc6d55c2b3456eb06a53eec1ff2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 12:45:50 +0100 Subject: [PATCH 068/504] Add VST3 string utility functions, cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginChecks.h | 1 - distrho/src/DistrhoPluginVST3.cpp | 201 ++++++++++++++++++------------ 2 files changed, 122 insertions(+), 80 deletions(-) diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index b885dd74..dfcda68b 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -173,7 +173,6 @@ # error DISTRHO_UI_IS_STANDALONE must not be defined #endif - // ----------------------------------------------------------------------- #endif // DISTRHO_PLUGIN_CHECKS_H_INCLUDED diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index e502de1d..9c00c8d3 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -66,7 +66,7 @@ static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; // -------------------------------------------------------------------------------------------------------------------- // Utility functions -const char* tuid2str(const v3_tuid iid) +static const char* tuid2str(const v3_tuid iid) { if (v3_tuid_match(iid, v3_funknown_iid)) return "{v3_funknown}"; @@ -124,7 +124,9 @@ const char* tuid2str(const v3_tuid iid) return buf; } -void strncpy(char* const dst, const char* const src, const size_t size) +// -------------------------------------------------------------------------------------------------------------------- + +static void strncpy(char* const dst, const char* const src, const size_t size) { DISTRHO_SAFE_ASSERT_RETURN(size > 0,); @@ -139,7 +141,7 @@ void strncpy(char* const dst, const char* const src, const size_t size) } } -void strncpy_16from8(int16_t* const dst, const char* const src, const size_t size) +static void strncpy_utf16(int16_t* const dst, const char* const src, const size_t size) { DISTRHO_SAFE_ASSERT_RETURN(size > 0,); @@ -163,6 +165,39 @@ void strncpy_16from8(int16_t* const dst, const char* const src, const size_t siz // -------------------------------------------------------------------------------------------------------------------- +template +static void snprintf_t(char* const dst, const T value, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + std::snprintf(dst, size-1, format, value); + dst[size-1] = '\0'; +} + +template +static void snprintf_utf16_t(int16_t* const dst, const T value, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + + char* const tmpbuf = (char*)std::malloc(size); + DISTRHO_SAFE_ASSERT_RETURN(tmpbuf != nullptr,); + + std::snprintf(tmpbuf, size-1, format, value); + tmpbuf[size-1] = '\0'; + + strncpy_utf16(dst, tmpbuf, size); + std::free(tmpbuf); +} + +static constexpr const char format_i32[] = "%d"; +static constexpr const char format_f32[] = "%f"; +static constexpr const char format_u32[] = "%u"; + +static constexpr void (*const snprintf_u32)(char*, uint32_t, size_t) = snprintf_t; +static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snprintf_utf16_t; +static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t; + +// -------------------------------------------------------------------------------------------------------------------- + class PluginVst3 { /* buses: we provide 1 for the main audio (if there is any) plus 1 for each sidechain or cv port. @@ -182,7 +217,9 @@ class PluginVst3 public: PluginVst3() - : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback) + : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), + fComponentHandler(nullptr), + fComponentHandlerArg(nullptr) { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 for (uint32_t i=0; imedia_type = V3_EVENT; info->direction = busDirection; info->channel_count = 1; - strncpy_16from8(info->bus_name, busDirection == V3_INPUT ? "Event/MIDI Input" - : "Event/MIDI Output", 128); + strncpy_utf16(info->bus_name, busDirection == V3_INPUT ? "Event/MIDI Input" + : "Event/MIDI Output", 128); info->bus_type = V3_MAIN; info->flags = V3_DEFAULT_ACTIVE; return V3_OK; @@ -670,10 +697,9 @@ public: info->step_count = step_count; info->default_normalised_value = ranges.getNormalizedValue(ranges.def); // int32_t unit_id; - strncpy_16from8(info->title, fPlugin.getParameterName(index), 128); - strncpy_16from8(info->short_title, fPlugin.getParameterShortName(index), 128); - strncpy_16from8(info->units, fPlugin.getParameterUnit(index), 128); - + strncpy_utf16(info->title, fPlugin.getParameterName(index), 128); + strncpy_utf16(info->short_title, fPlugin.getParameterShortName(index), 128); + strncpy_utf16(info->units, fPlugin.getParameterUnit(index), 128); return V3_OK; } @@ -681,10 +707,7 @@ public: { DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); - // TODO snprintf16 - char buf[24]; - sprintf(buf, "%f", fPlugin.getParameterRanges(index).getUnnormalizedValue(normalised)); - strncpy_16from8(output, buf, 128); + snprintf_f32_utf16(output, fPlugin.getParameterRanges(index).getUnnormalizedValue(normalised), 128); return V3_OK; } @@ -742,6 +765,13 @@ public: return V3_OK; } + v3_result setComponentHandler(v3_component_handler* const handler, void* const arg) noexcept + { + fComponentHandler = handler; + fComponentHandlerArg = arg; + return V3_OK; + } + // ---------------------------------------------------------------------------------------------------------------- private: @@ -749,15 +779,25 @@ private: PluginExporter fPlugin; // VST3 stuff - // TODO + v3_component_handler* fComponentHandler; + void* fComponentHandlerArg; -#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST - bool requestParameterValueChange(uint32_t, float) + bool requestParameterValueChange(const uint32_t index, const float value) { - // TODO - return true; + DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); + DISTRHO_SAFE_ASSERT_RETURN(fComponentHandlerArg != nullptr, false); + + if (fComponentHandler->begin_edit(fComponentHandlerArg, index) != V3_OK) + return false; + + const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value); + const bool ret = fComponentHandler->perform_edit(fComponentHandlerArg, index, normalized) == V3_OK; + + fComponentHandler->end_edit(fComponentHandlerArg, index); + return ret; } +#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) { return ((PluginVst3*)ptr)->requestParameterValueChange(index, value); @@ -790,11 +830,8 @@ struct v3_edit_controller_cpp : v3_funknown { struct dpf_edit_controller : v3_edit_controller_cpp { std::atomic refcounter; ScopedPointer* self; - // ScopedPointer view; ScopedPointer& vst3; bool initialized; - // cached values - // v3_component_handler_cpp** handler = nullptr; dpf_edit_controller(ScopedPointer* const s, ScopedPointer& v) : refcounter(1), @@ -1012,8 +1049,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - vst3->setParameterNormalized(index, normalised); - // if (dpf_plugin_view* const view = controller->view) // { // if (UIVst3* const uivst3 = view->uivst3) @@ -1022,7 +1057,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // } // } - return V3_OK; + return vst3->setParameterNormalized(index, normalised); }; controller.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result @@ -1031,6 +1066,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + // v3_component_handler_cpp** const cpphandler = (v3_component_handler_cpp**)handler; // // controller->handler = cpphandler; @@ -1043,7 +1081,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // uivst3->setHandler(cpphandler); // } - return V3_OK; + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_component_handler* const handlerptr + = handler != nullptr ? (v3_component_handler*)((uint8_t*)*(handler)+sizeof(v3_funknown)) + : nullptr; + + return vst3->setComponentHandler(handlerptr, handler); }; controller.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** @@ -1138,7 +1181,8 @@ struct dpf_audio_processor : v3_audio_processor_cpp { v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result { - d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", self, inputs, num_inputs, outputs, num_outputs); + // NOTE this is called a bunch of times + // d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", self, inputs, num_inputs, outputs, num_outputs); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); @@ -1332,6 +1376,8 @@ struct dpf_component : v3_component_cpp { PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 == nullptr, V3_INVALID_ARG); + d_lastCanRequestParameterValueChanges = true; + // default early values if (d_lastBufferSize == 0) d_lastBufferSize = 2048; @@ -1425,7 +1471,8 @@ struct dpf_component : v3_component_cpp { comp.activate_bus = []V3_API(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bool state) -> v3_result { - d_stdout("dpf_component::activate_bus => %p %i %i %i %u", self, media_type, bus_direction, bus_idx, state); + // NOTE this is called a bunch of times + // d_stdout("dpf_component::activate_bus => %p %i %i %i %u", self, media_type, bus_direction, bus_idx, state); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1497,9 +1544,11 @@ struct dpf_factory : v3_plugin_factory_cpp { d_lastBufferSize = 512; d_lastSampleRate = 44100.0; + d_lastCanRequestParameterValueChanges = true; static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr); d_lastBufferSize = 0; d_lastSampleRate = 0.0; + d_lastCanRequestParameterValueChanges = false; dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo.getUniqueId(); @@ -1526,42 +1575,32 @@ struct dpf_factory : v3_plugin_factory_cpp { }; // we only support 1 plugin per binary, so don't have to care here - ref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_factory::ref => %p", self); - return 1; - }; - - unref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_factory::unref => %p", self); - return 0; - }; + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory - v1.get_factory_info = []V3_API(void* self, struct v3_factory_info* const info) -> v3_result + v1.get_factory_info = []V3_API(void*, struct v3_factory_info* const info) -> v3_result { - d_stdout("dpf_factory::get_factory_info => %p %p", self, info); + std::memset(info, 0, sizeof(*info)); DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo.getHomePage(), sizeof(info->url)); - DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO + // DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO return V3_OK; }; - v1.num_classes = []V3_API(void* self) -> int32_t + v1.num_classes = []V3_API(void*) -> int32_t { - d_stdout("dpf_factory::num_classes => %p", self); return 1; }; - v1.get_class_info = []V3_API(void* self, int32_t idx, struct v3_class_info* const info) -> v3_result + v1.get_class_info = []V3_API(void*, int32_t idx, struct v3_class_info* const info) -> v3_result { - d_stdout("dpf_factory::get_class_info => %p %i %p", self, idx, info); + std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); - memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo.getName(), sizeof(info->name)); @@ -1586,38 +1625,42 @@ struct dpf_factory : v3_plugin_factory_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_2 - v2.get_class_info_2 = []V3_API(void* self, int32_t idx, struct v3_class_info_2* info) -> v3_result + v2.get_class_info_2 = []V3_API(void*, int32_t idx, struct v3_class_info_2* info) -> v3_result { - d_stdout("dpf_factory::get_class_info_2 => %p %i %p", self, idx, info); - memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + std::memset(info, 0, sizeof(*info)); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); - DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo.getName(), sizeof(info->name)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo.getName(), ARRAY_SIZE(info->name)); info->class_flags = 0; - DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO - DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); - std::snprintf(info->version, sizeof(info->version), "%u", gPluginInfo.getVersion()); // TODO - DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? + // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO + DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), ARRAY_SIZE(info->vendor)); + DISTRHO_NAMESPACE::snprintf_u32(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); + DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_3 - v3.get_class_info_utf16 = []V3_API(void* self, int32_t idx, struct v3_class_info_3* info) -> v3_result + v3.get_class_info_utf16 = []V3_API(void*, int32_t idx, struct v3_class_info_3* info) -> v3_result { - d_stdout("dpf_factory::get_class_info_utf16 => %p %i %p", self, idx, info); - memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + std::memset(info, 0, sizeof(*info)); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); - DISTRHO_NAMESPACE::strncpy_16from8(info->name, gPluginInfo.getName(), sizeof(info->name)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + DISTRHO_NAMESPACE::strncpy_utf16(info->name, gPluginInfo.getName(), ARRAY_SIZE(info->name)); info->class_flags = 0; - DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO - DISTRHO_NAMESPACE::strncpy_16from8(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); - // DISTRHO_NAMESPACE::snprintf16(info->version, sizeof(info->version)/sizeof(info->version[0]), "%u", gPluginInfo.getVersion()); // TODO - DISTRHO_NAMESPACE::strncpy_16from8(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? + // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", ARRAY_SIZE(info->sub_categories)); // TODO + DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); + DISTRHO_NAMESPACE::snprintf_u32_utf16(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); + DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; From 6cbf08d58809b9c70e5283eb40975cf9efd0825b Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 13:07:33 +0100 Subject: [PATCH 069/504] Correctly offset VST3 streams Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 60 ++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 9c00c8d3..24a51045 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -507,17 +507,17 @@ public: return V3_OK; } - v3_result setState(v3_bstream**) + v3_result setState(v3_bstream*, void*) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result getState(v3_bstream**) + v3_result getState(v3_bstream*, void*) { // TODO return V3_NOT_IMPLEMENTED; - }; + } // ---------------------------------------------------------------------------------------------------------------- // v3_audio_processor interface calls @@ -630,23 +630,25 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_edit_controller interface calls - v3_result setComponentState(v3_bstream*) +#if 0 + v3_result setComponentState(v3_bstream*, void*) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result setState(v3_bstream*) + v3_result setState(v3_bstream*, void*) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result getState(v3_bstream*) + v3_result getState(v3_bstream*, void*) { // TODO return V3_NOT_IMPLEMENTED; } +#endif int32_t getParameterCount() const noexcept { @@ -922,38 +924,62 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { - d_stdout("dpf_edit_controller::set_component_state => %p", self); + d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - return vst3->setComponentState(stream); +#if 0 + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_bstream* const streamptr + = stream != nullptr ? (v3_bstream*)((uint8_t*)stream+sizeof(v3_funknown)) + : nullptr; + + return vst3->setComponentState(streamptr, stream); +#endif + return V3_NOT_IMPLEMENTED; }; controller.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { - d_stdout("dpf_edit_controller::set_state => %p", self); + d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); +#if 0 + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_bstream* const streamptr + = stream != nullptr ? (v3_bstream*)((uint8_t*)stream+sizeof(v3_funknown)) + : nullptr; + return vst3->setState(stream); +#endif + return V3_NOT_IMPLEMENTED; }; controller.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { - d_stdout("dpf_edit_controller::get_state => %p", self); + d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); +#if 0 + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_bstream* const streamptr + = stream != nullptr ? (v3_bstream*)((uint8_t*)stream+sizeof(v3_funknown)) + : nullptr; + return vst3->getState(stream); +#endif + return V3_NOT_IMPLEMENTED; }; controller.get_parameter_count = []V3_API(void* self) -> int32_t @@ -1503,7 +1529,12 @@ struct dpf_component : v3_component_cpp { PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - return vst3->setState(stream); + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_bstream* const streamptr + = stream != nullptr ? (v3_bstream*)((uint8_t*)*(stream)+sizeof(v3_funknown)) + : nullptr; + + return vst3->setState(streamptr, stream); }; comp.get_state = []V3_API(void* self, v3_bstream** stream) -> v3_result @@ -1515,7 +1546,12 @@ struct dpf_component : v3_component_cpp { PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - return vst3->getState(stream); + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_bstream* const streamptr + = stream != nullptr ? (v3_bstream*)((uint8_t*)*(stream)+sizeof(v3_funknown)) + : nullptr; + + return vst3->getState(streamptr, stream); }; } }; From 1db760336079f0bf9968d21f9497f905acbed346 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 19:15:51 +0100 Subject: [PATCH 070/504] VST3: Implement parameter state save/restore Signed-off-by: falkTX --- distrho/extra/String.hpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 288 +++++++++++++++++++++++++++++- 2 files changed, 285 insertions(+), 5 deletions(-) diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index 04a959be..88a7501d 100644 --- a/distrho/extra/String.hpp +++ b/distrho/extra/String.hpp @@ -516,7 +516,7 @@ public: */ String& replace(const char before, const char after) noexcept { - DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0', *this); + DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this); for (std::size_t i=0; i < fBufferLen; ++i) { diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 24a51045..a3a50762 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -507,13 +507,195 @@ public: return V3_OK; } - v3_result setState(v3_bstream*, void*) + /* state: we pack pairs of key-value strings each separated by a null/zero byte. + * states come first, and then parameters. parameters are simply converted to/from strings and floats. + * the parameter symbol is used as the "key", so it is possible to reorder them or even remove and add safely. + * the number of states must remain constant though. + */ + v3_result setState(v3_bstream* const stream, void* arg) + { +#if DISTRHO_PLUGIN_WANT_STATE + // TODO +#endif + + if (const uint32_t paramCount = fPlugin.getParameterCount()) + { + char buffer[32], orig; + String key, value; + v3_result res; + bool fillingKey = true; + + // temporarily set locale to "C" while converting floats + const ScopedSafeLocale ssl; + + for (int32_t pos = 0, read;; pos += read) + { + std::memset(buffer, '\xff', sizeof(buffer)); + res = stream->read(arg, buffer, sizeof(buffer)-1, &read); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); + + for (int32_t i = 0; i < read; ++i) + { + if (buffer[i] == '\0' && pos == 0 && i == 0) + continue; + + orig = buffer[read]; + buffer[read] = '\0'; + + if (fillingKey) + key += buffer + i; + else + value += buffer + i; + + i += std::strlen(buffer + i); + buffer[read] = orig; + + if (buffer[i] == '\0') + { + fillingKey = !fillingKey; + + if (value.isNotEmpty()) + { + // find parameter with this symbol, and set its value + for (uint32_t j=0; jwrite(arg, &buffer, 1, &ignored); + } + + String state; + +#if DISTRHO_PLUGIN_WANT_FULL_STATE + /* + // Update current state + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) + { + const String& key = cit->first; + fStateMap[key] = fPlugin.getState(key); + } + */ +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + /* + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) + { + const String& key = cit->first; + const String& value = cit->second; + + // join key and value + String tmpStr; + tmpStr = key; + tmpStr += "\xff"; + tmpStr += value; + tmpStr += "\xff"; + + state += tmpStr; + } + */ +#endif + + if (paramCount != 0) + { + // add another separator + state += "\xff"; + + for (uint32_t i=0; i(state.length())+1; + v3_result res; + + for (int32_t wrtntotal = 0, wrtn; wrtntotal < size; wrtntotal += wrtn) + { + wrtn = 0; + res = stream->write(arg, const_cast(buffer), size - wrtntotal, &wrtn); + + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(wrtn > 0, wrtn, V3_INTERNAL_ERR); + } + + return V3_OK; + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_bstream interface calls (for state support) + + v3_result read(void* buffer, int32_t num_bytes, int32_t* bytes_read) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result getState(v3_bstream*, void*) + v3_result write(void* buffer, int32_t num_bytes, int32_t* bytes_written) + { + // TODO + return V3_NOT_IMPLEMENTED; + } + + v3_result seek(int64_t pos, int32_t seek_mode, int64_t* result) + { + // TODO + return V3_NOT_IMPLEMENTED; + } + + v3_result tell(int64_t* pos) { // TODO return V3_NOT_IMPLEMENTED; @@ -1302,6 +1484,102 @@ struct dpf_audio_processor : v3_audio_processor_cpp { } }; +// -------------------------------------------------------------------------------------------------------------------- +// dpf_state_stream + +struct v3_bstream_cpp : v3_funknown { + v3_bstream stream; +}; + +struct dpf_state_stream : v3_bstream_cpp { + ScopedPointer& vst3; + + dpf_state_stream(ScopedPointer& v) + : vst3(v) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_bstream_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_factory::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // TODO + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_bstream + + stream.read = []V3_API(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_read) -> v3_result + { + d_stdout("dpf_state_stream::read => %p %p %i %p", self, buffer, num_bytes, bytes_read); + dpf_state_stream* const stream = *(dpf_state_stream**)self; + DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = stream->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return V3_NOT_IMPLEMENTED; + }; + + stream.write = []V3_API(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_written) -> v3_result + { + d_stdout("dpf_state_stream::write => %p %p %i %p", self, buffer, num_bytes, bytes_written); + dpf_state_stream* const stream = *(dpf_state_stream**)self; + DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = stream->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return V3_NOT_IMPLEMENTED; + }; + + stream.seek = []V3_API(void* self, int64_t pos, int32_t seek_mode, int64_t* result) -> v3_result + { + d_stdout("dpf_state_stream::seek => %p %lu %i %p", self, pos, seek_mode, result); + dpf_state_stream* const stream = *(dpf_state_stream**)self; + DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = stream->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return V3_NOT_IMPLEMENTED; + }; + + stream.tell = []V3_API(void* self, int64_t* pos) -> v3_result + { + d_stdout("dpf_state_stream::tell => %p %p", self, pos); + dpf_state_stream* const stream = *(dpf_state_stream**)self; + DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = stream->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return V3_NOT_IMPLEMENTED; + }; + } +}; + // -------------------------------------------------------------------------------------------------------------------- // dpf_component @@ -1315,7 +1593,7 @@ struct dpf_component : v3_component_cpp { ScopedPointer* self; ScopedPointer processor; ScopedPointer controller; -// ScopedPointer view; + ScopedPointer stream; ScopedPointer vst3; dpf_component(ScopedPointer* const s) @@ -1539,7 +1817,7 @@ struct dpf_component : v3_component_cpp { comp.get_state = []V3_API(void* self, v3_bstream** stream) -> v3_result { - d_stdout("dpf_component::get_state => %p", self); + d_stdout("dpf_component::get_state => %p %p", self, stream); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -1706,6 +1984,8 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_NOT_IMPLEMENTED; }; } + + DISTRHO_PREVENT_HEAP_ALLOCATION }; END_NAMESPACE_DISTRHO From 9b49fd363e67d4e7efbc54852cf0cf24312571ce Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 20:07:25 +0100 Subject: [PATCH 071/504] VST3: implement time position; enable metronome vst3 build Signed-off-by: falkTX --- distrho/src/DistrhoDefines.h | 4 +- distrho/src/DistrhoPluginVST2.cpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 97 ++++++++++++++++++++++---- distrho/src/travesty/audio_processor.h | 2 +- examples/Metronome/Makefile | 3 +- 5 files changed, 90 insertions(+), 18 deletions(-) diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index ffd407f1..281031c2 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -109,7 +109,7 @@ #define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_CONTINUE(msg, cond) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } continue; } #define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN(msg, cond, ret) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } return ret; } -#define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value); break; } +#define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); break; } #define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); continue; } #define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); return ret; } @@ -117,7 +117,7 @@ #define DISTRHO_SAFE_ASSERT_INT2_CONTINUE(cond, v1, v2) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); continue; } #define DISTRHO_SAFE_ASSERT_INT2_RETURN(cond, v1, v2, ret) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); return ret; } -#define DISTRHO_SAFE_ASSERT_UINT_BREAK(cond, value) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value); break; } +#define DISTRHO_SAFE_ASSERT_UINT_BREAK(cond, value) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); break; } #define DISTRHO_SAFE_ASSERT_UINT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); continue; } #define DISTRHO_SAFE_ASSERT_UINT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); return ret; } diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 827a3055..824327a9 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1114,7 +1114,7 @@ public: fTimePosition.playing = (vstTimeInfo->flags & kVstTransportPlaying); fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0); - // ticksPerBeat is not possible with VST + // ticksPerBeat is not possible with VST2 fTimePosition.bbt.ticksPerBeat = 1920.0; if (vstTimeInfo->flags & kVstTempoValid) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index a3a50762..2a6e4153 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -677,25 +677,25 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_bstream interface calls (for state support) - v3_result read(void* buffer, int32_t num_bytes, int32_t* bytes_read) + v3_result read(void* /*buffer*/, int32_t /*num_bytes*/, int32_t* /*bytes_read*/) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result write(void* buffer, int32_t num_bytes, int32_t* bytes_written) + v3_result write(void* /*buffer*/, int32_t /*num_bytes*/, int32_t* /*bytes_written*/) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result seek(int64_t pos, int32_t seek_mode, int64_t* result) + v3_result seek(int64_t /*pos*/, int32_t /*seek_mode*/, int64_t* /*result*/) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result tell(int64_t* pos) + v3_result tell(int64_t* /*pos*/) { // TODO return V3_NOT_IMPLEMENTED; @@ -771,25 +771,91 @@ public: } #if DISTRHO_PLUGIN_WANT_TIMEPOS - // TODO + // TODO implement v3_process_context_requirements as query_interface of something.. + if (v3_process_context* const ctx = data->ctx) + { + fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING; + fTimePosition.bbt.valid = ctx->state & (V3_PROCESS_CTX_TEMPO_VALID|V3_PROCESS_CTX_TIME_SIG_VALID); + + // ticksPerBeat is not possible with VST2 + fTimePosition.bbt.ticksPerBeat = 1920.0; + + if (ctx->state & V3_PROCESS_CTX_CONT_TIME_VALID) + fTimePosition.frame = ctx->continuous_time_in_samples; + else + fTimePosition.frame = ctx->project_time_in_samples; + + if (ctx->state & V3_PROCESS_CTX_TEMPO_VALID) + fTimePosition.bbt.beatsPerMinute = ctx->bpm; + else + fTimePosition.bbt.beatsPerMinute = 120.0; + + if (ctx->state & (V3_PROCESS_CTX_PROJECT_TIME_VALID|V3_PROCESS_CTX_TIME_SIG_VALID)) + { + const double ppqPos = std::abs(ctx->project_time_quarters); + const int ppqPerBar = ctx->time_sig_numerator * 4 / ctx->time_sig_denom; + const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * ctx->time_sig_numerator; + const double rest = std::fmod(barBeats, 1.0); + + fTimePosition.bbt.bar = static_cast(ppqPos) / ppqPerBar + 1; + fTimePosition.bbt.beat = static_cast(barBeats - rest + 0.5) + 1; + fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; + fTimePosition.bbt.beatsPerBar = ctx->time_sig_numerator; + fTimePosition.bbt.beatType = ctx->time_sig_denom; + + if (ctx->project_time_quarters < 0.0) + { + --fTimePosition.bbt.bar; + fTimePosition.bbt.beat = ctx->time_sig_numerator - fTimePosition.bbt.beat + 1; + fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1; + } + } + else + { + fTimePosition.bbt.bar = 1; + fTimePosition.bbt.beat = 1; + fTimePosition.bbt.tick = 0.0; + fTimePosition.bbt.beatsPerBar = 4.0f; + fTimePosition.bbt.beatType = 4.0f; + } + + fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* + fTimePosition.bbt.beatsPerBar* + (fTimePosition.bbt.bar-1); + + fPlugin.setTimePosition(fTimePosition); + + } #endif - const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS]; - /* */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS != 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1]; + /* */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1]; { int32_t i = 0; - for (; i < data->inputs->num_channels; ++i) - inputs[i] = data->inputs->channel_buffers_32[i]; - for (; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) + if (data->inputs != nullptr) + { + for (; i < data->inputs->num_channels; ++i) + { + DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_INPUTS, i); + inputs[i] = data->inputs->channel_buffers_32[i]; + } + } + for (; i < std::max(1, DISTRHO_PLUGIN_NUM_INPUTS); ++i) inputs[i] = nullptr; // TODO use dummy buffer } { int32_t i = 0; - for (; i < data->outputs->num_channels; ++i) - outputs[i] = data->outputs->channel_buffers_32[i]; - for (; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + if (data->outputs != nullptr) + { + for (; i < data->outputs->num_channels; ++i) + { + DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_OUTPUTS, i); + outputs[i] = data->outputs->channel_buffers_32[i]; + } + } + for (; i < std::max(1, DISTRHO_PLUGIN_NUM_OUTPUTS); ++i) outputs[i] = nullptr; // TODO use dummy buffer } @@ -966,6 +1032,11 @@ private: v3_component_handler* fComponentHandler; void* fComponentHandlerArg; + // Temporary data +#if DISTRHO_PLUGIN_WANT_TIMEPOS + TimePosition fTimePosition; +#endif + bool requestParameterValueChange(const uint32_t index, const float value) { DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h index 2386e637..d1ed5bf3 100644 --- a/distrho/src/travesty/audio_processor.h +++ b/distrho/src/travesty/audio_processor.h @@ -153,7 +153,7 @@ struct v3_process_context { int64_t project_time_in_samples; // with loop int64_t system_time_ns; - int64_t continuous_time_in_samples; // without loop? unclear + int64_t continuous_time_in_samples; // without loop double project_time_quarters; double bar_position_quarters; diff --git a/examples/Metronome/Makefile b/examples/Metronome/Makefile index 72316ea8..c5860dfb 100644 --- a/examples/Metronome/Makefile +++ b/examples/Metronome/Makefile @@ -25,7 +25,8 @@ include ../../Makefile.plugins.mk TARGETS += jack TARGETS += lv2_dsp -TARGETS += vst +TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) From ef64e046b060c6fdd2fa17d3cfcb74518946bc6e Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 20:26:11 +0100 Subject: [PATCH 072/504] Implement v3_process_context_requirements Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 70 +++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 2a6e4153..7849072b 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -824,7 +824,6 @@ public: (fTimePosition.bbt.bar-1); fPlugin.setTimePosition(fTimePosition); - } #endif @@ -1387,6 +1386,63 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } }; +// -------------------------------------------------------------------------------------------------------------------- +// dpf_process_context_requirements + +struct v3_process_context_requirements_cpp : v3_funknown { + v3_process_context_requirements req; +}; + +struct dpf_process_context_requirements : v3_process_context_requirements_cpp { + dpf_process_context_requirements() + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_process_context_requirements_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_process_context_requirements::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // this is used statically, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_process_context_requirements + + req.get_process_context_requirements = []V3_API(void*) -> uint32_t + { + return 0x0 + |V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID + |V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID + |V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID + |V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID + |V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING + }; + } + + DISTRHO_PREVENT_HEAP_ALLOCATION +}; + // -------------------------------------------------------------------------------------------------------------------- // dpf_audio_processor @@ -1404,7 +1460,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { self(s), vst3(v) { - static const uint8_t* kSupportedInterfaces[] = { + static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, v3_audio_processor_iid }; @@ -1418,7 +1474,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* interface_iid : kSupportedInterfaces) + for (const uint8_t* interface_iid : kSupportedInterfacesBase) { if (v3_tuid_match(interface_iid, iid)) { @@ -1427,6 +1483,14 @@ struct dpf_audio_processor : v3_audio_processor_cpp { } } + if (v3_tuid_match(v3_process_context_requirements_iid, iid)) + { + static dpf_process_context_requirements context_req; + static dpf_process_context_requirements* context_req_ptr = &context_req;; + *iface = &context_req_ptr; + return V3_OK; + } + return V3_NO_INTERFACE; }; From da528328bf4e13004bfddf9b71e214a5bea97e95 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 21:22:31 +0100 Subject: [PATCH 073/504] VST3: Initial MIDI support, enable midi-through vst3 build Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 154 ++++++++++++++++++++++++++++-- examples/MidiThrough/Makefile | 3 +- 2 files changed, 150 insertions(+), 7 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 7849072b..f3632d63 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -220,6 +220,9 @@ public: : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), fComponentHandlerArg(nullptr) +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + , fHostEventOutputHandle(nullptr) +#endif { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 for (uint32_t i=0; ictx) { fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING; @@ -858,12 +860,95 @@ public: outputs[i] = nullptr; // TODO use dummy buffer } +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fHostEventOutputHandle = data->output_events; +#endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - fPlugin.run(inputs, outputs, data->nframes, nullptr, 0); + uint32_t midiEventCount = 0; + + if (v3_event_list** const eventarg = data->input_events) + { + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_event_list* const eventptr = (v3_event_list*)((uint8_t*)(*eventarg)+sizeof(v3_funknown)); + + v3_event event; + for (uint32_t i = 0, count = eventptr->get_event_count(eventarg); i < count; ++i) + { + if (eventptr->get_event(eventarg, i, &event) != V3_OK) + break; + + // check if event can be encoded as MIDI + switch (event.type) + { + case V3_EVENT_NOTE_ON: + case V3_EVENT_NOTE_OFF: + // case V3_EVENT_DATA: + case V3_EVENT_POLY_PRESSURE: + // case V3_EVENT_NOTE_EXP_VALUE: + // case V3_EVENT_NOTE_EXP_TEXT: + // case V3_EVENT_CHORD: + // case V3_EVENT_SCALE: + case V3_EVENT_LEGACY_MIDI_CC_OUT: + break; + default: + continue; + } + + MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); + midiEvent.frame = event.sample_offset; + + // encode event as MIDI + switch (event.type) + { + case V3_EVENT_NOTE_ON: + midiEvent.size = 3; + midiEvent.data[0] = 0x90 | (event.note_on.channel & 0xf); + midiEvent.data[1] = event.note_on.pitch; + midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_on.velocity * 127))); + midiEvent.data[3] = 0; + break; + case V3_EVENT_NOTE_OFF: + midiEvent.size = 3; + midiEvent.data[0] = 0x80 | (event.note_off.channel & 0xf); + midiEvent.data[1] = event.note_off.pitch; + midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_off.velocity * 127))); + midiEvent.data[3] = 0; + break; + case V3_EVENT_POLY_PRESSURE: + midiEvent.size = 3; + midiEvent.data[0] = 0xA0 | (event.poly_pressure.channel & 0xf); + midiEvent.data[1] = event.poly_pressure.pitch; + midiEvent.data[2] = std::max(0, std::min(127, (int)(event.poly_pressure.pressure * 127))); + midiEvent.data[3] = 0; + break; + case V3_EVENT_LEGACY_MIDI_CC_OUT: + midiEvent.size = 3; + midiEvent.data[0] = 0xB0 | (event.midi_cc_out.channel & 0xf); + midiEvent.data[1] = event.midi_cc_out.cc_number; + midiEvent.data[2] = event.midi_cc_out.value; + midiEvent.data[3] = 0; + // midiEvent.data[3] = event.midi_cc_out.value2; // TODO check when size should be 4 + break; + default: + midiEvent.size = 0; + break; + } + + if (midiEventCount == kMaxMidiEvents) + break; + } + } + + fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount); #else fPlugin.run(inputs, outputs, data->nframes); #endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fHostEventOutputHandle = nullptr; +#endif + // TODO updateParameterOutputsAndTriggers() return V3_OK; @@ -1032,6 +1117,12 @@ private: void* fComponentHandlerArg; // Temporary data +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + MidiEvent fMidiEvents[kMaxMidiEvents]; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + v3_event_list** fHostEventOutputHandle; +#endif #if DISTRHO_PLUGIN_WANT_TIMEPOS TimePosition fTimePosition; #endif @@ -1061,8 +1152,55 @@ private: #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT bool writeMidi(const MidiEvent& midiEvent) { - // TODO - return true; + DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fHostEventOutputHandle != nullptr, false); + + v3_event event; + std::memset(&event, 0, sizeof(event)); + event.sample_offset = midiEvent.frame; + + const uint8_t* const data = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data; + + switch (data[0] & 0xf0) + { + case 0x80: + event.type = V3_EVENT_NOTE_OFF; + event.note_off.channel = data[0] & 0xf; + event.note_off.pitch = data[1]; + event.note_off.velocity = (float)data[2] / 127.0f; + // int32_t note_id; + // float tuning; + break; + case 0x90: + event.type = V3_EVENT_NOTE_ON; + event.note_on.channel = data[0] & 0xf; + event.note_on.pitch = data[1]; + // float tuning; + event.note_on.velocity = (float)data[2] / 127.0f; + // int32_t length; + // int32_t note_id; + break; + case 0xA0: + event.type = V3_EVENT_POLY_PRESSURE; + event.poly_pressure.channel = data[0] & 0xf; + event.poly_pressure.pitch = data[1]; + event.poly_pressure.pressure = (float)data[2] / 127.0f; + // int32_t note_id; + break; + case 0xB0: + event.type = V3_EVENT_LEGACY_MIDI_CC_OUT; + event.midi_cc_out.channel = data[0] & 0xf; + event.midi_cc_out.cc_number = data[1]; + event.midi_cc_out.value = data[2]; + event.midi_cc_out.value2 = midiEvent.size == 4 ? data[3] : 0; + break; + default: + return true; + } + + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_event_list* const hostptr = (v3_event_list*)((uint8_t*)(*fHostEventOutputHandle)+sizeof(v3_funknown)); + + return hostptr->add_event(fHostEventOutputHandle, &event) == V3_OK; } static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) @@ -1431,12 +1569,16 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { req.get_process_context_requirements = []V3_API(void*) -> uint32_t { +#if DISTRHO_PLUGIN_WANT_TIMEPOS return 0x0 |V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID |V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID |V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID |V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID |V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING +#else + return 0x0; +#endif }; } diff --git a/examples/MidiThrough/Makefile b/examples/MidiThrough/Makefile index 7cec0047..39751b8d 100644 --- a/examples/MidiThrough/Makefile +++ b/examples/MidiThrough/Makefile @@ -25,7 +25,8 @@ include ../../Makefile.plugins.mk TARGETS += jack TARGETS += lv2_dsp -TARGETS += vst +TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) From ba550f42fd9f366e633bf0b979a99825791f44ee Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 24 Sep 2021 23:22:42 +0100 Subject: [PATCH 074/504] VST3: Cleanup UI side, make it appear again Signed-off-by: falkTX --- distrho/DistrhoUIMain.cpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 37 +- .../{DISTRHOUIVST3.cpp => DistrhoUIVST3.cpp} | 410 ++++++++++-------- 3 files changed, 260 insertions(+), 189 deletions(-) rename distrho/src/{DISTRHOUIVST3.cpp => DistrhoUIVST3.cpp} (67%) diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index 0be2e2a2..ef3aba28 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -27,7 +27,7 @@ #elif defined(DISTRHO_PLUGIN_TARGET_VST2) // nothing #elif defined(DISTRHO_PLUGIN_TARGET_VST3) -// nothing +# include "src/DistrhoUIVST3.cpp" #else # error unsupported format #endif diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index f3632d63..fff9fe40 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -290,6 +290,19 @@ public: #endif } + // ---------------------------------------------------------------------------------------------------------------- + // stuff called for UI creation + + void* getInstancePointer() const noexcept + { + return fPlugin.getInstancePointer(); + } + + double getSampleRate() const noexcept + { + return fPlugin.getSampleRate(); + } + // ---------------------------------------------------------------------------------------------------------------- // v3_component interface calls @@ -1127,6 +1140,7 @@ private: TimePosition fTimePosition; #endif +#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST bool requestParameterValueChange(const uint32_t index, const float value) { DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); @@ -1142,7 +1156,6 @@ private: return ret; } -#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) { return ((PluginVst3*)ptr)->requestParameterValueChange(index, value); @@ -1211,6 +1224,13 @@ private: }; +#if DISTRHO_PLUGIN_HAS_UI +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view_create (called from DSP side) + +v3_funknown** dpf_plugin_view_create(void* instancePointer, double sampleRate); +#endif + // -------------------------------------------------------------------------------------------------------------------- // dpf_edit_controller @@ -1511,15 +1531,14 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); - return nullptr; - -// if (controller->view == nullptr) -// { -// controller->view = new dpf_plugin_view(&controller->view, controller->vst3); -// controller->view->handler = controller->handler; -// } + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); -// return (v3_plugin_view**)&controller->view; +#if DISTRHO_PLUGIN_HAS_UI + return (v3_plugin_view**)dpf_plugin_view_create(vst3->getInstancePointer(), vst3->getSampleRate()); +#else + return nullptr; +#endif }; } }; diff --git a/distrho/src/DISTRHOUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp similarity index 67% rename from distrho/src/DISTRHOUIVST3.cpp rename to distrho/src/DistrhoUIVST3.cpp index cbd9b6e5..9ad40519 100644 --- a/distrho/src/DISTRHOUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -145,7 +145,9 @@ public: nullptr, // TODO file request nullptr, instancePointer, - scaleFactor) + scaleFactor), + fFrame(nullptr), + fScaleFactor(scaleFactor) { // TESTING awful idea dont reuse startThread(); @@ -156,179 +158,199 @@ public: stopThread(5000); } - // ------------------------------------------------------------------- - // TESTING awful idea dont reuse void run() override { while (! shouldThreadExit()) { - idle(); + fUI.plugin_idle(); d_msleep(50); } } - void idle() - { - /* - for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) - { - if (fUiHelper->parameterChecks[i]) - { - fUiHelper->parameterChecks[i] = false; - fUI.parameterChanged(i, fUiHelper->parameterValues[i]); - } - } - */ +// // TODO dont use this +// void setParameterValueFromDSP(const uint32_t index, const float value) +// { +// fUI.parameterChanged(index, value); +// } - fUI.plugin_idle(); - } +// void setHandler(v3_component_handler_cpp** const h) noexcept +// { +// handler = h; +// } - int16_t getWidth() const + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_view interface calls + + v3_result onWheel(float /*distance*/) { - return fUI.getWidth(); - } + // TODO + return V3_NOT_IMPLEMENTED; + }; - int16_t getHeight() const + v3_result onKeyDown(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/) { - return fUI.getHeight(); - } + // TODO + return V3_NOT_IMPLEMENTED; + }; - double getScaleFactor() const + v3_result onKeyUp(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/) { - return fUI.getScaleFactor(); - } + // TODO + return V3_NOT_IMPLEMENTED; + }; - void notifyScaleFactorChanged(const double scaleFactor) + v3_result getSize(v3_view_rect* const rect) const noexcept { - fUI.notifyScaleFactorChanged(scaleFactor); + std::memset(rect, 0, sizeof(v3_view_rect)); + + rect->right = fUI.getWidth(); + rect->bottom = fUI.getHeight(); +#ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + rect->right /= scaleFactor; + rect->bottom /= scaleFactor; +#endif + + return V3_OK; } - // TODO dont use this - void setParameterValueFromDSP(const uint32_t index, const float value) + v3_result setSize(v3_view_rect* const /*rect*/) { - fUI.parameterChanged(index, value); + // TODO + return V3_NOT_IMPLEMENTED; } - // ---------------------------------------------------------------------------------------------------------------- - // v3_plugin_view interface calls + v3_result onFocus(const bool /*state*/) + { + // TODO + return V3_NOT_IMPLEMENTED; + } - void setFrame(v3_plugin_frame* const f) noexcept + v3_result setFrame(v3_plugin_frame* const frame) noexcept { - frame = f; + fFrame = frame; + return V3_OK; } - void setHandler(v3_component_handler_cpp** const h) noexcept + v3_result checkSizeConstraint(v3_view_rect* const /*rect*/) { - handler = h; + // TODO + return V3_NOT_IMPLEMENTED; } // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_view_content_scale_steinberg interface calls -protected: - void editParameter(const uint32_t index, const bool started) const + v3_result setContentScaleFactor(const float factor) { - DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); - - v3_component_handler_cpp* const chandler = *handler; - DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); + if (d_isEqual(fScaleFactor, factor)) + return V3_OK; - if (started) - chandler->handler.begin_edit(handler, index); - else - chandler->handler.end_edit(handler, index); + fScaleFactor = factor; + fUI.notifyScaleFactorChanged(factor); + return V3_OK; } - void setParameterValue(const uint32_t index, const float realValue) - { - DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); + // ---------------------------------------------------------------------------------------------------------------- - v3_component_handler_cpp* const chandler = *handler; - DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); +private: + // Plugin UI + UIExporter fUI; - const double value = vst3->plainParameterToNormalised(index, realValue); - chandler->handler.perform_edit(handler, index, value); + // VST3 stuff + v3_plugin_frame* fFrame; + // v3_component_handler_cpp** handler = nullptr; - // TODO send change to DSP side? - } + // Temporary data + float fScaleFactor; - void setSize(uint width, uint height) + void editParameter(const uint32_t /*index*/, const bool /*started*/) const { -# ifdef DISTRHO_OS_MAC - const double scaleFactor = fUI.getScaleFactor(); - width /= scaleFactor; - height /= scaleFactor; -# endif - if (frame == nullptr) - return; - - v3_view_rect rect = {}; - rect.right = width; - rect.bottom = height; - (void)rect; - // frame->resize_view(nullptr, uivst3, &rect); +// DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); +// +// v3_component_handler_cpp* const chandler = *handler; +// DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); +// +// if (started) +// chandler->handler.begin_edit(handler, index); +// else +// chandler->handler.end_edit(handler, index); } -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + static void editParameterCallback(void* ptr, uint32_t index, bool started) { - uint8_t midiData[3]; - midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; - midiData[1] = note; - midiData[2] = velocity; - fNotesRingBuffer.writeCustomData(midiData, 3); - fNotesRingBuffer.commitWrite(); + ((UIVst3*)ptr)->editParameter(index, started); } -# endif -# if DISTRHO_PLUGIN_WANT_STATE - void setState(const char* const key, const char* const value) + void setParameterValue(const uint32_t /*index*/, const float /*realValue*/) { - // fUiHelper->setStateFromUI(key, value); - } -# endif - -private: - // VST3 stuff - v3_plugin_frame* frame; - v3_component_handler_cpp** handler = nullptr; - - // Plugin UI - UIExporter fUI; +// DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); +// +// v3_component_handler_cpp* const chandler = *handler; +// DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); - // ------------------------------------------------------------------- - // Callbacks +// const double value = vst3->plainParameterToNormalised(index, realValue); +// chandler->handler.perform_edit(handler, index, value); - #define handlePtr ((UIVst3*)ptr) + // TODO send change to DSP side? + } - static void editParameterCallback(void* ptr, uint32_t index, bool started) + static void setParameterCallback(void* ptr, uint32_t rindex, float value) { - handlePtr->editParameter(index, started); + ((UIVst3*)ptr)->setParameterValue(rindex, value); } - static void setParameterCallback(void* ptr, uint32_t rindex, float value) + void setSize(uint /*width*/, uint /*height*/) { - handlePtr->setParameterValue(rindex, value); +// #ifdef DISTRHO_OS_MAC +// const double scaleFactor = fUI.getScaleFactor(); +// width /= scaleFactor; +// height /= scaleFactor; +// #endif +// if (frame == nullptr) +// return; +// +// v3_view_rect rect = {}; +// rect.right = width; +// rect.bottom = height; +// (void)rect; +// // frame->resize_view(nullptr, uivst3, &rect); } static void setSizeCallback(void* ptr, uint width, uint height) { - handlePtr->setSize(width, height); + ((UIVst3*)ptr)->setSize(width, height); + } + +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t /*channel*/, const uint8_t /*note*/, const uint8_t /*velocity*/) + { +// uint8_t midiData[3]; +// midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; +// midiData[1] = note; +// midiData[2] = velocity; +// fNotesRingBuffer.writeCustomData(midiData, 3); +// fNotesRingBuffer.commitWrite(); } -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) { - handlePtr->sendNote(channel, note, velocity); + ((UIVst3*)ptr)->sendNote(channel, note, velocity); + } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + void setState(const char* const /*key*/, const char* const /*value*/) + { + // fUiHelper->setStateFromUI(key, value); } -# endif -# if DISTRHO_PLUGIN_WANT_STATE static void setStateCallback(void* ptr, const char* key, const char* value) { - handlePtr->setState(key, value); + ((UIVst3*)ptr)->setState(key, value); } -# endif +#endif #undef handlePtr }; @@ -344,12 +366,14 @@ struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { std::atomic refcounter; ScopedPointer* self; ScopedPointer& uivst3; - float lastScaleFactor = 0.0f; + // cached values + float scaleFactor; dpf_plugin_view_scale(ScopedPointer* s, ScopedPointer& v) : refcounter(1), self(s), - uivst3(v) + uivst3(v), + scaleFactor(0.0f) { static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, @@ -409,12 +433,12 @@ struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALISED); + scale->scaleFactor = factor; + if (UIVst3* const uivst3 = scale->uivst3) - if (d_isNotZero(scale->lastScaleFactor) && d_isNotEqual(scale->lastScaleFactor, factor)) - uivst3->notifyScaleFactorChanged(factor); + return uivst3->setContentScaleFactor(factor); - scale->lastScaleFactor = factor; - return V3_OK; + return V3_NOT_INITIALISED; }; } }; @@ -432,18 +456,32 @@ struct dpf_plugin_view : v3_plugin_view_cpp { ScopedPointer scale; ScopedPointer uivst3; // cached values - v3_component_handler_cpp** handler = nullptr; - v3_plugin_frame* hostframe = nullptr; + void* const instancePointer; + double sampleRate; +// v3_component_handler_cpp** handler = nullptr; + v3_plugin_frame* frame = nullptr; - dpf_plugin_view(ScopedPointer* s) + dpf_plugin_view(ScopedPointer* s, void* const instance, const double sr) : refcounter(1), - self(s) + self(s), + instancePointer(instance), + sampleRate(sr) { static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, v3_plugin_view_iid }; + static const char* const kSupportedPlatforms[] = { +#ifdef _WIN32 + V3_VIEW_PLATFORM_TYPE_HWND, +#elif defined(__APPLE__) + V3_VIEW_PLATFORM_TYPE_NSVIEW, +#else + V3_VIEW_PLATFORM_TYPE_X11, +#endif + }; + // ------------------------------------------------------------------------------------------------------------ // v3_funknown @@ -505,19 +543,12 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result { d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); - const char* const supported[] = { -#ifdef _WIN32 - V3_VIEW_PLATFORM_TYPE_HWND, -#elif defined(__APPLE__) - V3_VIEW_PLATFORM_TYPE_NSVIEW, -#else - V3_VIEW_PLATFORM_TYPE_X11, -#endif - }; + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - for (size_t i=0; iuivst3 == nullptr, V3_INVALID_ARG); - const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; - view->uivst3 = new UIVst3(view->vst3, view->hostframe, (uintptr_t)parent, scaleFactor); - view->uivst3->setHandler(view->handler); - return V3_OK; + for (size_t i=0; iscale != nullptr ? view->scale->scaleFactor : 0.0f; + view->uivst3 = new UIVst3((uintptr_t)parent, scaleFactor, view->sampleRate, view->instancePointer); + view->uivst3->setFrame(view->frame); + // view->uivst3->setHandler(view->handler); + return V3_OK; + } + } + + return V3_NOT_IMPLEMENTED; }; view.removed = []V3_API(void* self) -> v3_result @@ -552,8 +592,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; + + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + + return uivst3->onWheel(distance); }; view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result @@ -561,8 +604,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; + + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + + return uivst3->onKeyDown(key_char, key_code, modifiers); }; view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result @@ -570,8 +616,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; + + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + + return uivst3->onKeyUp(key_char, key_code, modifiers); }; view.get_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result @@ -580,44 +629,26 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - std::memset(rect, 0, sizeof(v3_view_rect)); + // special case: allow UI to not be attached yet, as a way to get size before window creation - if (view->uivst3 != nullptr) - { - rect->right = view->uivst3->getWidth(); - rect->bottom = view->uivst3->getHeight(); -# ifdef DISTRHO_OS_MAC - const double scaleFactor = view->uivst3->getScaleFactor(); - rect->right /= scaleFactor; - rect->bottom /= scaleFactor; -# endif - } - else - { - const double scaleFactor = view->scale != nullptr ? view->scale->lastScaleFactor : 0.0; - UIExporter tmpUI(nullptr, 0, view->vst3->getSampleRate(), - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->vst3->getInstancePointer(), scaleFactor); - rect->right = tmpUI.getWidth(); - rect->bottom = tmpUI.getHeight(); -# ifdef DISTRHO_OS_MAC - const double scaleFactor = tmpUI.getScaleFactor(); - rect->right /= scaleFactor; - rect->bottom /= scaleFactor; -# endif - tmpUI.quit(); - } + if (UIVst3* const uivst3 = view->uivst3) + return uivst3->getSize(rect); - return V3_NOT_IMPLEMENTED; + const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; + const UIVst3 uivst3(0, scaleFactor, view->sampleRate, view->instancePointer); + return uivst3.getSize(rect); }; - view.set_size = []V3_API(void* self, v3_view_rect*) -> v3_result + view.set_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result { - d_stdout("dpf_plugin_view::set_size => %p", self); + d_stdout("dpf_plugin_view::set_size => %p %p", self, rect); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; + + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + + return uivst3->setSize(rect); }; view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result @@ -625,22 +656,25 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_NOT_INITIALISED); - return V3_NOT_IMPLEMENTED; + + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + + return uivst3->onFocus(state); }; view.set_frame = []V3_API(void* self, v3_plugin_frame* frame) -> v3_result { - d_stdout("dpf_plugin_view::set_frame => %p", self); + d_stdout("dpf_plugin_view::set_frame => %p %o", self, frame); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - view->hostframe = frame; + view->frame = frame; - if (view->uivst3 != nullptr) - view->uivst3->setFrame(frame); + if (UIVst3* const uivst3 = view->uivst3) + return uivst3->setFrame(frame); - return V3_OK; + return V3_NOT_INITIALISED; }; view.can_resize = []V3_API(void* self) -> v3_result @@ -653,14 +687,32 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif }; - view.check_size_constraint = []V3_API(void* self, v3_view_rect*) -> v3_result + view.check_size_constraint = []V3_API(void* self, v3_view_rect* rect) -> v3_result { - d_stdout("dpf_plugin_view::check_size_constraint => %p", self); - return V3_NOT_IMPLEMENTED; + d_stdout("dpf_plugin_view::check_size_constraint => %p %p", self, rect); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + + return uivst3->checkSizeConstraint(rect); }; } }; +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view_create (called from DSP side) + +v3_funknown** dpf_plugin_view_create(void* instancePointer, double sampleRate); + +v3_funknown** dpf_plugin_view_create(void* const instancePointer, const double sampleRate) +{ + ScopedPointer* const viewptr = new ScopedPointer; + *viewptr = new dpf_plugin_view(viewptr, instancePointer, sampleRate); + return (v3_funknown**)viewptr; +} + // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO From d81467ecc04ceba5bf853ba2906b002b31985412 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 00:48:37 +0100 Subject: [PATCH 075/504] Fix some v3_plugin_view arguments Signed-off-by: falkTX --- distrho/src/travesty/view.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index 98bc9cc5..54e6e34e 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -61,14 +61,14 @@ struct v3_plugin_view { V3_API v3_result (*get_size) (void *self, struct v3_view_rect *); - V3_API v3_result (*set_size) + V3_API v3_result (*on_size) (void *self, struct v3_view_rect *); V3_API v3_result (*on_focus) (void *self, v3_bool state); V3_API v3_result (*set_frame) - (void *self, struct v3_plugin_frame *); + (void *self, struct v3_plugin_frame **); V3_API v3_result (*can_resize)(void *self); V3_API v3_result (*check_size_constraint) (void *self, struct v3_view_rect *); @@ -81,7 +81,7 @@ struct v3_plugin_frame { struct v3_funknown; V3_API v3_result (*resize_view) - (void *self, struct v3_plugin_view *, struct v3_view_rect *); + (void *self, struct v3_plugin_view **, struct v3_view_rect *); }; static const v3_tuid v3_plugin_frame_iid = From 842bb786ff67d7d039c55353af0dc65c8a10f2b6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 00:49:03 +0100 Subject: [PATCH 076/504] VST3: Some attention to UI, plugin->host resizing works Signed-off-by: falkTX --- distrho/src/DistrhoUIPrivateData.hpp | 6 ++ distrho/src/DistrhoUIVST3.cpp | 112 +++++++++++++++++---------- examples/Info/Makefile | 7 +- 3 files changed, 80 insertions(+), 45 deletions(-) diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 47d5a54d..ac6bd838 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -38,6 +38,12 @@ # define DISTRHO_UI_USER_RESIZABLE 0 #endif +// TODO figure out how to detect host support +#if defined(DISTRHO_PLUGIN_TARGET_VST3) +# undef DISTRHO_UI_USER_RESIZABLE +# define DISTRHO_UI_USER_RESIZABLE 0 +#endif + // ----------------------------------------------------------------------- #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 9ad40519..43140357 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -50,6 +50,10 @@ struct v3_component_handler_cpp : v3_funknown { v3_component_handler handler; }; +struct v3_plugin_frame_cpp : v3_funknown { + v3_plugin_frame frame; +}; + START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- @@ -132,10 +136,21 @@ const char* tuid2str(const v3_tuid iid) // -------------------------------------------------------------------------------------------------------------------- +// TESTING +struct v3_plugin_view_cpp_virtual { + virtual V3_API v3_result query_interface(const v3_tuid iid, void **obj) = 0; + virtual V3_API uint32_t ref() = 0; + virtual V3_API uint32_t unref() = 0; + virtual V3_API v3_result resize_view(void*, void*) = 0; +}; + +// -------------------------------------------------------------------------------------------------------------------- + class UIVst3 : public Thread { public: - UIVst3(const intptr_t winId, const float scaleFactor, const double sampleRate, void* const instancePointer) + UIVst3(const intptr_t winId, const float scaleFactor, const double sampleRate, + void* const instancePointer, v3_plugin_view** const view) : fUI(this, winId, sampleRate, editParameterCallback, setParameterCallback, @@ -146,7 +161,9 @@ public: nullptr, instancePointer, scaleFactor), + fView(view), fFrame(nullptr), + fFrameArg(nullptr), fScaleFactor(scaleFactor) { // TESTING awful idea dont reuse @@ -168,17 +185,6 @@ public: } } -// // TODO dont use this -// void setParameterValueFromDSP(const uint32_t index, const float value) -// { -// fUI.parameterChanged(index, value); -// } - -// void setHandler(v3_component_handler_cpp** const h) noexcept -// { -// handler = h; -// } - // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view interface calls @@ -215,7 +221,7 @@ public: return V3_OK; } - v3_result setSize(v3_view_rect* const /*rect*/) + v3_result onSize(v3_view_rect* const /*rect*/) { // TODO return V3_NOT_IMPLEMENTED; @@ -227,9 +233,12 @@ public: return V3_NOT_IMPLEMENTED; } - v3_result setFrame(v3_plugin_frame* const frame) noexcept + v3_result setFrame(v3_plugin_frame* const frame, void* const arg) noexcept { + d_stdout("setFrame %p %p", frame, arg); fFrame = frame; + fFrameArg = arg; + return V3_OK; } @@ -259,7 +268,9 @@ private: UIExporter fUI; // VST3 stuff + v3_plugin_view** const fView; v3_plugin_frame* fFrame; + void* fFrameArg; // v3_component_handler_cpp** handler = nullptr; // Temporary data @@ -301,21 +312,24 @@ private: ((UIVst3*)ptr)->setParameterValue(rindex, value); } - void setSize(uint /*width*/, uint /*height*/) + void setSize(uint width, uint height) { -// #ifdef DISTRHO_OS_MAC -// const double scaleFactor = fUI.getScaleFactor(); -// width /= scaleFactor; -// height /= scaleFactor; -// #endif -// if (frame == nullptr) -// return; -// -// v3_view_rect rect = {}; -// rect.right = width; -// rect.bottom = height; -// (void)rect; -// // frame->resize_view(nullptr, uivst3, &rect); + DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fFrameArg != nullptr,); + d_stdout("from UI setSize %u %u | %p %p %p", width, height, fView, fFrame, fFrameArg); + +#ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + width /= scaleFactor; + height /= scaleFactor; +#endif + + v3_view_rect rect; + std::memset(&rect, 0, sizeof(rect)); + rect.right = width; + rect.bottom = height; + fFrame->resize_view(fFrameArg, fView, &rect); } static void setSizeCallback(void* ptr, uint width, uint height) @@ -351,8 +365,6 @@ private: ((UIVst3*)ptr)->setState(key, value); } #endif - - #undef handlePtr }; // -------------------------------------------------------------------------------------------------------------------- @@ -459,7 +471,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { void* const instancePointer; double sampleRate; // v3_component_handler_cpp** handler = nullptr; - v3_plugin_frame* frame = nullptr; + v3_plugin_frame** frame = nullptr; dpf_plugin_view(ScopedPointer* s, void* const instance, const double sr) : refcounter(1), @@ -567,8 +579,15 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - view->uivst3 = new UIVst3((uintptr_t)parent, scaleFactor, view->sampleRate, view->instancePointer); - view->uivst3->setFrame(view->frame); + view->uivst3 = new UIVst3((uintptr_t)parent, scaleFactor, view->sampleRate, + view->instancePointer, (v3_plugin_view**)self); + + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_plugin_frame* const frameptr + = view->frame != nullptr ? (v3_plugin_frame*)((uint8_t*)*(view->frame)+sizeof(v3_funknown)) + : nullptr; + + view->uivst3->setFrame(frameptr, view->frame); // view->uivst3->setHandler(view->handler); return V3_OK; } @@ -635,20 +654,24 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->getSize(rect); const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - const UIVst3 uivst3(0, scaleFactor, view->sampleRate, view->instancePointer); - return uivst3.getSize(rect); + UIExporter tmpUI(nullptr, 0, view->sampleRate, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + view->instancePointer, scaleFactor); + rect->right = tmpUI.getWidth(); + rect->bottom = tmpUI.getHeight(); + return V3_OK; }; - view.set_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result + view.on_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result { - d_stdout("dpf_plugin_view::set_size => %p %p", self, rect); + d_stdout("dpf_plugin_view::on_size => %p %p", self, rect); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); UIVst3* const uivst3 = view->uivst3; DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); - return uivst3->setSize(rect); + return uivst3->onSize(rect); }; view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result @@ -663,16 +686,23 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->onFocus(state); }; - view.set_frame = []V3_API(void* self, v3_plugin_frame* frame) -> v3_result + view.set_frame = []V3_API(void* self, v3_plugin_frame** frame) -> v3_result { - d_stdout("dpf_plugin_view::set_frame => %p %o", self, frame); + d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame); dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); view->frame = frame; if (UIVst3* const uivst3 = view->uivst3) - return uivst3->setFrame(frame); + { + // offset struct by sizeof(v3_funknown), because of differences between C and C++ + v3_plugin_frame* const frameptr + = frame != nullptr ? (v3_plugin_frame*)((uint8_t*)*(frame)+sizeof(v3_funknown)) + : nullptr; + + return uivst3->setFrame(frameptr, frame); + } return V3_NOT_INITIALISED; }; diff --git a/examples/Info/Makefile b/examples/Info/Makefile index e728dab2..84d5b84a 100644 --- a/examples/Info/Makefile +++ b/examples/Info/Makefile @@ -28,12 +28,11 @@ include ../../Makefile.plugins.mk ifeq ($(HAVE_OPENGL),true) TARGETS += jack -TARGETS += lv2_sep -else -TARGETS += lv2_dsp endif # HAVE_OPENGL -TARGETS += vst +TARGETS += lv2_sep +TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) From 7cfaef75503fde6300cd6431d083c0e57d521580 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 00:51:21 +0100 Subject: [PATCH 077/504] Cleanup Signed-off-by: falkTX --- distrho/src/DistrhoUIVST3.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 43140357..c80fe154 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -46,14 +46,6 @@ // -------------------------------------------------------------------------------------------------------------------- -struct v3_component_handler_cpp : v3_funknown { - v3_component_handler handler; -}; - -struct v3_plugin_frame_cpp : v3_funknown { - v3_plugin_frame frame; -}; - START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- @@ -136,16 +128,6 @@ const char* tuid2str(const v3_tuid iid) // -------------------------------------------------------------------------------------------------------------------- -// TESTING -struct v3_plugin_view_cpp_virtual { - virtual V3_API v3_result query_interface(const v3_tuid iid, void **obj) = 0; - virtual V3_API uint32_t ref() = 0; - virtual V3_API uint32_t unref() = 0; - virtual V3_API v3_result resize_view(void*, void*) = 0; -}; - -// -------------------------------------------------------------------------------------------------------------------- - class UIVst3 : public Thread { public: From 090ee032746321c5557d4559d2d44d3899720a41 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 11:29:36 +0100 Subject: [PATCH 078/504] VST3: implement parameter outputs and triggers, UI focus Signed-off-by: falkTX --- dgl/src/Window.cpp | 16 ------ distrho/src/DistrhoPluginInternal.hpp | 8 +++ distrho/src/DistrhoPluginVST2.cpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 79 +++++++++++++++++++++++++-- distrho/src/DistrhoUIInternal.hpp | 11 +++- distrho/src/DistrhoUIVST3.cpp | 30 ++++++---- 6 files changed, 110 insertions(+), 36 deletions(-) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 79538e07..6f2a7dad 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -407,22 +407,6 @@ void Window::setTransientWinId(const uintptr_t winId) { puglSetTransientFor(pData->view, winId); } - -// ----------------------------------------------------------------------- - -bool Window::handlePluginKeyboard(const bool press, const uint key) -{ - // TODO - return false; - // return pData->handlePluginKeyboard(press, key); -} - -bool Window::handlePluginSpecial(const bool press, const Key key) -{ - // TODO - return false; - // return pData->handlePluginSpecial(press, key); -} #endif // ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 7d2a89d8..202887ce 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -610,6 +610,14 @@ public: return fData->parameters[index].groupId; } + float getParameterDefault(const uint32_t index) const + { + DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f); + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0.0f); + + return fData->parameters[index].ranges.def; + } + float getParameterValue(const uint32_t index) const { DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f); diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 824327a9..841dab42 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -370,7 +370,7 @@ public: } } - fUI.handlePluginKeyboard(down, static_cast(index), fKeyboardModifiers); + fUI.handlePluginKeyboardVST2(down, static_cast(index), fKeyboardModifiers); if (needsShiftRevert) fKeyboardModifiers &= ~kModifierShift; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index fff9fe40..a4cffbb2 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -219,7 +219,8 @@ public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), - fComponentHandlerArg(nullptr) + fComponentHandlerArg(nullptr), + fParameterValues(nullptr) #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fHostEventOutputHandle(nullptr) #endif @@ -288,6 +289,23 @@ public: port.busId = 0; } #endif + + if (const uint32_t parameterCount = fPlugin.getParameterCount()) + { + fParameterValues = new float[parameterCount]; + + for (uint32_t i=0; i < parameterCount; ++i) + fParameterValues[i] = fPlugin.getParameterDefault(i); + } + } + + ~PluginVst3() + { + if (fParameterValues != nullptr) + { + delete[] fParameterValues; + fParameterValues = nullptr; + } } // ---------------------------------------------------------------------------------------------------------------- @@ -842,6 +860,12 @@ public: } #endif + if (data->nframes <= 0) + { + updateParameterOutputsAndTriggers(); + return V3_OK; + } + const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS != 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1]; /* */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1]; @@ -962,8 +986,7 @@ public: fHostEventOutputHandle = nullptr; #endif - // TODO updateParameterOutputsAndTriggers() - + updateParameterOutputsAndTriggers(); return V3_OK; } @@ -1130,6 +1153,7 @@ private: void* fComponentHandlerArg; // Temporary data + float* fParameterValues; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT MidiEvent fMidiEvents[kMaxMidiEvents]; #endif @@ -1140,7 +1164,47 @@ private: TimePosition fTimePosition; #endif -#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST + // ---------------------------------------------------------------------------------------------------------------- + // functions called from the plugin side, RT no block + + void updateParameterOutputsAndTriggers() + { + float curValue; + + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (fPlugin.isParameterOutput(i)) + { + // NOTE: no output parameter support in VST3, simulate it here + curValue = fPlugin.getParameterValue(i); + + if (d_isEqual(curValue, fParameterValues[i])) + continue; + + fParameterValues[i] = curValue; + } + else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) + { + // NOTE: no trigger support in VST parameters, simulate it here + curValue = fPlugin.getParameterValue(i); + + if (d_isEqual(curValue, fPlugin.getParameterDefault(i))) + continue; + + fPlugin.setParameterValue(i, curValue); + } + else + { + continue; + } + + requestParameterValueChange(i, curValue); + } + } + + // ---------------------------------------------------------------------------------------------------------------- + // DPF callbacks + bool requestParameterValueChange(const uint32_t index, const float value) { DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); @@ -1156,6 +1220,7 @@ private: return ret; } +#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) { return ((PluginVst3*)ptr)->requestParameterValueChange(index, value); @@ -1418,7 +1483,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); + // NOTE very noisy, called many times + // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1466,7 +1532,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double { - d_stdout("dpf_edit_controller::get_parameter_normalised => %p", self); + // NOTE very noisy, called many times + // d_stdout("dpf_edit_controller::get_parameter_normalised => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 232fdb66..46de9858 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -258,7 +258,7 @@ public: } #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - bool handlePluginKeyboard(const bool press, const uint key, const uint16_t mods) + bool handlePluginKeyboardVST2(const bool press, const uint key, const uint16_t mods) { DGL_NAMESPACE::Widget::KeyboardEvent ev; ev.mod = mods; @@ -289,6 +289,15 @@ public: ui->uiScaleFactorChanged(scaleFactor); } +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + void notifyFocusChanged(const bool focus) + { + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); + + ui->uiFocus(focus, DGL_NAMESPACE::kCrossingNormal); + } +#endif + void setSampleRate(const double sampleRate, const bool doCallback = false) { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index c80fe154..3d9d484c 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -16,12 +16,6 @@ #include "DistrhoUIInternal.hpp" -#include "travesty/audio_processor.h" -#include "travesty/component.h" -#include "travesty/edit_controller.h" -#include "travesty/factory.h" -#include "travesty/view.h" - #include // TESTING awful idea dont reuse @@ -44,6 +38,12 @@ # include "DistrhoUIInternal.hpp" // #endif +#include "travesty/audio_processor.h" +#include "travesty/component.h" +#include "travesty/edit_controller.h" +#include "travesty/factory.h" +#include "travesty/view.h" + // -------------------------------------------------------------------------------------------------------------------- START_NAMESPACE_DISTRHO @@ -209,10 +209,16 @@ public: return V3_NOT_IMPLEMENTED; } - v3_result onFocus(const bool /*state*/) + v3_result onFocus(const bool state) { - // TODO +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + fUI.notifyFocusChanged(state); + return V3_OK; +#else return V3_NOT_IMPLEMENTED; + // unused + (void)state; +#endif } v3_result setFrame(v3_plugin_frame* const frame, void* const arg) noexcept @@ -692,11 +698,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.can_resize = []V3_API(void* self) -> v3_result { d_stdout("dpf_plugin_view::can_resize => %p", self); -#if DISTRHO_UI_USER_RESIZABLE - return V3_OK; -#else +// #if DISTRHO_UI_USER_RESIZABLE +// return V3_OK; +// #else return V3_NOT_IMPLEMENTED; -#endif +// #endif }; view.check_size_constraint = []V3_API(void* self, v3_view_rect* rect) -> v3_result From db465f69b2fbe30788469b29ee9425f64ada280a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 11:48:02 +0100 Subject: [PATCH 079/504] VST3 YOLO Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 76 +++++++------------------------ distrho/src/DistrhoUIVST3.cpp | 35 +++----------- 2 files changed, 22 insertions(+), 89 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index a4cffbb2..640dcb65 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1305,15 +1305,11 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { - std::atomic refcounter; - ScopedPointer* self; ScopedPointer& vst3; bool initialized; - dpf_edit_controller(ScopedPointer* const s, ScopedPointer& v) - : refcounter(1), - self(s), - vst3(v), + dpf_edit_controller(ScopedPointer& v) + : vst3(v), initialized(false) { static const uint8_t* kSupportedInterfaces[] = { @@ -1342,28 +1338,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NO_INTERFACE; }; - ref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_edit_controller::ref => %p", self); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0); - - return ++controller->refcounter; - }; - - unref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_edit_controller::unref => %p", self); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0); - - if (const int refcounter = --controller->refcounter) - return refcounter; - - *(dpf_edit_controller**)self = nullptr; - *controller->self = nullptr; - return 0; - }; + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base @@ -1679,14 +1656,10 @@ struct v3_audio_processor_cpp : v3_funknown { }; struct dpf_audio_processor : v3_audio_processor_cpp { - std::atomic refcounter; - ScopedPointer* self; ScopedPointer& vst3; - dpf_audio_processor(ScopedPointer* const s, ScopedPointer& v) - : refcounter(1), - self(s), - vst3(v) + dpf_audio_processor(ScopedPointer& v) + : vst3(v) { static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, @@ -1722,28 +1695,9 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return V3_NO_INTERFACE; }; - ref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_audio_processor::ref => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); - - return ++processor->refcounter; - }; - - unref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_audio_processor::unref => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); - - if (const int refcounter = --processor->refcounter) - return refcounter; - - *(dpf_audio_processor**)self = nullptr; - *processor->self = nullptr; - return 0; - }; + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_audio_processor @@ -1847,6 +1801,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { } }; +#if 0 // -------------------------------------------------------------------------------------------------------------------- // dpf_state_stream @@ -1886,7 +1841,7 @@ struct dpf_state_stream : v3_bstream_cpp { return V3_NO_INTERFACE; }; - // TODO + // there is only a single instance of this, so we don't have to care here ref = []V3_API(void*) -> uint32_t { return 1; }; unref = []V3_API(void*) -> uint32_t { return 0; }; @@ -1942,6 +1897,7 @@ struct dpf_state_stream : v3_bstream_cpp { }; } }; +#endif // -------------------------------------------------------------------------------------------------------------------- // dpf_component @@ -1956,7 +1912,7 @@ struct dpf_component : v3_component_cpp { ScopedPointer* self; ScopedPointer processor; ScopedPointer controller; - ScopedPointer stream; + // ScopedPointer stream; ScopedPointer vst3; dpf_component(ScopedPointer* const s) @@ -1992,7 +1948,7 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(v3_audio_processor_iid, iid)) { if (component->processor == nullptr) - component->processor = new dpf_audio_processor(&component->processor, component->vst3); + component->processor = new dpf_audio_processor(component->vst3); *iface = &component->processor; return V3_OK; } @@ -2000,7 +1956,7 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(v3_edit_controller_iid, iid)) { if (component->controller == nullptr) - component->controller = new dpf_edit_controller(&component->controller, component->vst3); + component->controller = new dpf_edit_controller(component->vst3); *iface = &component->controller; return V3_OK; } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 3d9d484c..f58b2156 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -363,16 +363,12 @@ struct v3_plugin_view_content_scale_steinberg_cpp : v3_funknown { }; struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { - std::atomic refcounter; - ScopedPointer* self; ScopedPointer& uivst3; // cached values float scaleFactor; - dpf_plugin_view_scale(ScopedPointer* s, ScopedPointer& v) - : refcounter(1), - self(s), - uivst3(v), + dpf_plugin_view_scale(ScopedPointer& v) + : uivst3(v), scaleFactor(0.0f) { static const uint8_t* kSupportedInterfaces[] = { @@ -401,28 +397,9 @@ struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { return V3_NO_INTERFACE; }; - ref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_plugin_view_scale::ref => %p", self); - dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); - - return ++scale->refcounter; - }; - - unref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_plugin_view_scale::unref => %p", self); - dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, 0); - - if (const int refcounter = --scale->refcounter) - return refcounter; - - *(dpf_plugin_view_scale**)self = nullptr; - *scale->self = nullptr; - return 0; - }; + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_view_content_scale_steinberg @@ -506,7 +483,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (v3_tuid_match(v3_plugin_view_content_scale_steinberg_iid, iid)) { if (view->scale == nullptr) - view->scale = new dpf_plugin_view_scale(&view->scale, view->uivst3); + view->scale = new dpf_plugin_view_scale(view->uivst3); *iface = &view->scale; return V3_OK; } From ef5c2d8a067c727c8186105f12f74b93880cc9b5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 12:25:00 +0100 Subject: [PATCH 080/504] VST3: Fix UI object lifetime, add temp workaround for component Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 31 ++++++++++++++++++++++++++++--- distrho/src/DistrhoUIVST3.cpp | 2 +- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 640dcb65..fc934c68 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -23,6 +23,7 @@ #include "travesty/factory.h" #include +#include // TESTING awful idea dont reuse #include "../extra/Thread.hpp" @@ -1964,6 +1965,11 @@ struct dpf_component : v3_component_cpp { return V3_NO_INTERFACE; }; +#if 1 + // TODO fix this up later + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; +#else ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_component::ref => %p", self); @@ -1979,13 +1985,18 @@ struct dpf_component : v3_component_cpp { dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, 0); - if (const int refcounter = --component->refcounter) - return refcounter; + if (const int refcount = --component->refcounter) + { + d_stdout("dpf_component::unref => %p | refcount %i", self, refcount); + return refcount; + } - *(dpf_component**)self = nullptr; + d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); *component->self = nullptr; + delete (dpf_component**)self; return 0; }; +#endif // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base @@ -2163,6 +2174,8 @@ struct v3_plugin_factory_cpp : v3_funknown { }; struct dpf_factory : v3_plugin_factory_cpp { + std::vector*> components; + dpf_factory() { static const uint8_t* kSupportedFactories[] = { @@ -2252,6 +2265,7 @@ struct dpf_factory : v3_plugin_factory_cpp { ScopedPointer* const componentptr = new ScopedPointer; *componentptr = new dpf_component(componentptr); *instance = componentptr; + factory->components.push_back(componentptr); return V3_OK; }; @@ -2304,6 +2318,17 @@ struct dpf_factory : v3_plugin_factory_cpp { }; } + ~dpf_factory() + { + d_stdout("dpf_factory deleting"); + + for (ScopedPointer* componentptr : components) + { + *componentptr = nullptr; + delete componentptr; + } + } + DISTRHO_PREVENT_HEAP_ALLOCATION }; diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index f58b2156..5a42a5e2 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -509,8 +509,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (const int refcounter = --view->refcounter) return refcounter; - *(dpf_plugin_view**)self = nullptr; *view->self = nullptr; + delete (dpf_plugin_view**)self; return 0; }; From 5614c5b8a7ff1350cf8348d0c3dc5edf88f8f1f6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 13:21:18 +0100 Subject: [PATCH 081/504] Add VST3 to CI and packaging scripts Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 12 ++++++++++++ utils/package-osx-bundles.sh | 10 +++++++++- utils/plugin.pkg/package.xml.in | 4 ++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 295953e2..48975cc2 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -172,6 +172,7 @@ jobs: !bin/*-dssi.dylib !bin/lv2 !bin/vst2 + !bin/vst3 win32: runs-on: ubuntu-20.04 @@ -321,3 +322,14 @@ jobs: --suppressions=./utils/valgrind-dpf.supp \ /usr/lib/carla/carla-bridge-native vst2 ./bin/${p} "" 1>/dev/null; \ done + - name: Test VST3 plugins + run: | + for p in $(ls bin/ | grep vst3); do \ + env CARLA_BRIDGE_DUMMY=1 CARLA_BRIDGE_TESTING=native \ + valgrind \ + --error-exitcode=255 \ + --leak-check=full \ + --track-origins=yes \ + --suppressions=./utils/valgrind-dpf.supp \ + /usr/lib/carla/carla-bridge-native vst3 ./bin/${p} "" 1>/dev/null; \ + done diff --git a/utils/package-osx-bundles.sh b/utils/package-osx-bundles.sh index 57a74844..32a52c9d 100755 --- a/utils/package-osx-bundles.sh +++ b/utils/package-osx-bundles.sh @@ -14,10 +14,12 @@ SNAME="$(echo ${NAME} | tr -d ' ' | tr '/' '-')" rm -rf lv2 rm -rf vst2 +rm -rf vst3 -mkdir lv2 vst2 +mkdir lv2 vst2 vst3 mv *.lv2 lv2/ mv *.vst vst2/ +mv *.vst3 vst3/ pkgbuild \ --identifier "studio.kx.distrho.plugins.${SNAME}.lv2bundles" \ @@ -31,6 +33,12 @@ pkgbuild \ --root "${PWD}/vst2/" \ ../dpf-${SNAME}-vst2bundles.pkg +pkgbuild \ + --identifier "studio.kx.distrho.plugins.${SNAME}.vst3bundles" \ + --install-location "/Library/Audio/Plug-Ins/VST3/" \ + --root "${PWD}/vst3/" \ + ../dpf-${SNAME}-vst3bundles.pkg + cd .. DPF_UTILS_DIR=$(dirname ${0}) diff --git a/utils/plugin.pkg/package.xml.in b/utils/plugin.pkg/package.xml.in index 8959283b..dc8e24f2 100644 --- a/utils/plugin.pkg/package.xml.in +++ b/utils/plugin.pkg/package.xml.in @@ -11,8 +11,12 @@ @vst2bundleref@ + + @vst3bundleref@ + + From 72423e1edd1ab52e397313dbc9a155a4c6510c84 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 13:26:40 +0100 Subject: [PATCH 082/504] Mention VST3 in cmake Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 42 ++++++++++++++++++++++++++++++++---- utils/package-osx-bundles.sh | 1 + 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 18860e79..38d92845 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -22,7 +22,7 @@ # add_subdirectory(DPF) # # dpf_add_plugin(MyPlugin -# TARGETS lv2 vst2 +# TARGETS lv2 vst2 vst3 # UI_TYPE opengl # FILES_DSP # src/MyPlugin.cpp @@ -71,7 +71,7 @@ include(CMakeParseArguments) # # `TARGETS` ... # a list of one of more of the following target types: -# `jack`, `ladspa`, `dssi`, `lv2`, `vst2` +# `jack`, `ladspa`, `dssi`, `lv2`, `vst2`, `vst3` # # `UI_TYPE` # the user interface type: `opengl` (default), `cairo` @@ -153,6 +153,8 @@ function(dpf_add_plugin NAME) dpf__build_lv2("${NAME}" "${_dgl_library}" "${_dpf_plugin_MONOLITHIC}") elseif(_target STREQUAL "vst2") dpf__build_vst2("${NAME}" "${_dgl_library}") + elseif(_target STREQUAL "vst3") + dpf__build_vst3("${NAME}" "${_dgl_library}") else() message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}") endif() @@ -199,7 +201,7 @@ endfunction() # dpf__build_ladspa # ------------------------------------------------------------------------------ # -# Add build rules for a DSSI plugin. +# Add build rules for a LADSPA plugin. # function(dpf__build_ladspa NAME) dpf__create_dummy_source_list(_no_srcs) @@ -259,7 +261,7 @@ endfunction() # dpf__build_lv2 # ------------------------------------------------------------------------------ # -# Add build rules for a LV2 plugin. +# Add build rules for an LV2 plugin. # function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC) dpf__create_dummy_source_list(_no_srcs) @@ -339,6 +341,38 @@ function(dpf__build_vst2 NAME DGL_LIBRARY) endif() endfunction() +# dpf__build_vst3 +# ------------------------------------------------------------------------------ +# +# Add build rules for a VST3 plugin. +# +function(dpf__build_vst3 NAME DGL_LIBRARY) + dpf__create_dummy_source_list(_no_srcs) + + dpf__add_module("${NAME}-vst3" ${_no_srcs}) + dpf__add_plugin_main("${NAME}-vst3" "vst3") + dpf__add_ui_main("${NAME}-vst3" "vst3" "${DGL_LIBRARY}") + dpf__set_module_export_list("${NAME}-vst3" "vst3") + target_link_libraries("${NAME}-vst3" PRIVATE "${NAME}-dsp" "${NAME}-ui") + set_target_properties("${NAME}-vst3" PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/obj/vst3/$<0:>" + OUTPUT_NAME "${NAME}-vst3" + PREFIX "") + # TODO set correct output directory for VST3 packaging + #if(APPLE) + #set_target_properties("${NAME}-vst3" PROPERTIES + #LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/MacOS/$<0:>" + #OUTPUT_NAME "${NAME}" + #SUFFIX "") + #set(INFO_PLIST_PROJECT_NAME "${NAME}") + #configure_file("${DPF_ROOT_DIR}/utils/plugin.vst3/Contents/Info.plist" + #"${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/Info.plist" @ONLY) + #file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst3/Contents/PkgInfo" + #DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents") + #endif() +endfunction() + # dpf__add_dgl_cairo # ------------------------------------------------------------------------------ # diff --git a/utils/package-osx-bundles.sh b/utils/package-osx-bundles.sh index 32a52c9d..12b62e77 100755 --- a/utils/package-osx-bundles.sh +++ b/utils/package-osx-bundles.sh @@ -47,6 +47,7 @@ sed -e "s|@name@|${NAME}|" ${DPF_UTILS_DIR}/plugin.pkg/welcome.txt.in > build/we sed -e "s|@builddir@|${PWD}/build|" \ -e "s|@lv2bundleref@|dpf-${SNAME}-lv2bundles.pkg|" \ -e "s|@vst2bundleref@|dpf-${SNAME}-vst2bundles.pkg|" \ + -e "s|@vst3bundleref@|dpf-${SNAME}-vst3bundles.pkg|" \ -e "s|@name@|${NAME}|g" \ -e "s|@sname@|${SNAME}|g" \ ${DPF_UTILS_DIR}/plugin.pkg/package.xml.in > build/package.xml From 4edb06e60ee5c81af38b0b060f225dfd05d5a41e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 14:59:41 +0100 Subject: [PATCH 083/504] VST3: Rework headers coding style, add C++ util, cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 59 ++++------ distrho/src/DistrhoUIVST3.cpp | 141 +++++------------------ distrho/src/travesty/audio_processor.h | 153 ++++++++++--------------- distrho/src/travesty/base.h | 39 ++++--- distrho/src/travesty/bstream.h | 14 +-- distrho/src/travesty/component.h | 73 ++++++------ distrho/src/travesty/edit_controller.h | 66 ++++------- distrho/src/travesty/events.h | 28 +---- distrho/src/travesty/factory.h | 32 ++---- distrho/src/travesty/view.h | 65 +++++------ 10 files changed, 246 insertions(+), 424 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index fc934c68..fb285e5f 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -25,18 +25,25 @@ #include #include -// TESTING awful idea dont reuse -#include "../extra/Thread.hpp" +/* TODO items: + * - base component refcount handling + * - program support + * - state support + * - midi cc parameter mapping + * - full MIDI1 encode and decode + * - call component handler restart with params-changed flag when setting program + * - call component handler restart with latency-changed flag when latency changes + */ START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT -static const writeMidiFunc writeMidiCallback = nullptr; +static constexpr const writeMidiFunc writeMidiCallback = nullptr; #endif #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST -static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; +static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif // -------------------------------------------------------------------------------------------------------------------- @@ -67,7 +74,7 @@ static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; // -------------------------------------------------------------------------------------------------------------------- // Utility functions -static const char* tuid2str(const v3_tuid iid) +const char* tuid2str(const v3_tuid iid) { if (v3_tuid_match(iid, v3_funknown_iid)) return "{v3_funknown}"; @@ -220,7 +227,6 @@ public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), - fComponentHandlerArg(nullptr), fParameterValues(nullptr) #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fHostEventOutputHandle(nullptr) @@ -1136,10 +1142,9 @@ public: return V3_OK; } - v3_result setComponentHandler(v3_component_handler* const handler, void* const arg) noexcept + v3_result setComponentHandler(v3_component_handler** const handler) noexcept { fComponentHandler = handler; - fComponentHandlerArg = arg; return V3_OK; } @@ -1150,8 +1155,7 @@ private: PluginExporter fPlugin; // VST3 stuff - v3_component_handler* fComponentHandler; - void* fComponentHandlerArg; + v3_component_handler** fComponentHandler; // Temporary data float* fParameterValues; @@ -1209,15 +1213,14 @@ private: bool requestParameterValueChange(const uint32_t index, const float value) { DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); - DISTRHO_SAFE_ASSERT_RETURN(fComponentHandlerArg != nullptr, false); - if (fComponentHandler->begin_edit(fComponentHandlerArg, index) != V3_OK) + if (v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, index) != V3_OK) return false; const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value); - const bool ret = fComponentHandler->perform_edit(fComponentHandlerArg, index, normalized) == V3_OK; + const bool ret = v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, index, normalized) == V3_OK; - fComponentHandler->end_edit(fComponentHandlerArg, index); + v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, index); return ret; } @@ -1308,10 +1311,13 @@ struct v3_edit_controller_cpp : v3_funknown { struct dpf_edit_controller : v3_edit_controller_cpp { ScopedPointer& vst3; bool initialized; + // cached values + v3_component_handler** handler; dpf_edit_controller(ScopedPointer& v) : vst3(v), - initialized(false) + initialized(false), + handler(nullptr) { static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, @@ -1547,27 +1553,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + controller->handler = handler; -// v3_component_handler_cpp** const cpphandler = (v3_component_handler_cpp**)handler; -// -// controller->handler = cpphandler; -// -// if (controller->view != nullptr) -// { -// controller->view->handler = cpphandler; -// -// if (UIVst3* const uivst3 = controller->view->uivst3) -// uivst3->setHandler(cpphandler); -// } - - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_component_handler* const handlerptr - = handler != nullptr ? (v3_component_handler*)((uint8_t*)*(handler)+sizeof(v3_funknown)) - : nullptr; + if (PluginVst3* const vst3 = controller->vst3) + return vst3->setComponentHandler(handler); - return vst3->setComponentHandler(handlerptr, handler); + return V3_NOT_INITIALISED; }; controller.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 5a42a5e2..c7f297ca 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -21,16 +21,16 @@ // TESTING awful idea dont reuse #include "../extra/Thread.hpp" -// #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI -// # undef DISTRHO_PLUGIN_HAS_UI -// # define DISTRHO_PLUGIN_HAS_UI 0 -// #endif -// -// #if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI -// # undef DISTRHO_PLUGIN_HAS_UI -// # define DISTRHO_PLUGIN_HAS_UI 0 -// #endif -// +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + // #undef DISTRHO_PLUGIN_HAS_UI // #define DISTRHO_PLUGIN_HAS_UI 1 @@ -38,93 +38,33 @@ # include "DistrhoUIInternal.hpp" // #endif -#include "travesty/audio_processor.h" -#include "travesty/component.h" #include "travesty/edit_controller.h" -#include "travesty/factory.h" #include "travesty/view.h" -// -------------------------------------------------------------------------------------------------------------------- +/* TODO items: + * - see how to handle external non-embed UI build + * - program listener + * - state listener and sender + * - sample rate change listener + * - call component handler restart with params-changed flag when setting program + * - call component handler restart with latency-changed flag when latency changes + */ START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT -static const sendNoteFunc sendNoteCallback = nullptr; +static constexpr const sendNoteFunc sendNoteCallback = nullptr; #endif #if ! DISTRHO_PLUGIN_WANT_STATE -static const setStateFunc setStateCallback = nullptr; +static constexpr const setStateFunc setStateCallback = nullptr; #endif // -------------------------------------------------------------------------------------------------------------------- -// custom v3_tuid compatible type +// Utility functions (defined on plugin side) -typedef uint32_t dpf_tuid[4]; -static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch"); - -// -------------------------------------------------------------------------------------------------------------------- -// Utility functions - -const char* tuid2str(const v3_tuid iid) -{ - if (v3_tuid_match(iid, v3_funknown_iid)) - return "{v3_funknown}"; - if (v3_tuid_match(iid, v3_plugin_base_iid)) - return "{v3_plugin_base}"; - if (v3_tuid_match(iid, v3_plugin_factory_iid)) - return "{v3_plugin_factory}"; - if (v3_tuid_match(iid, v3_plugin_factory_2_iid)) - return "{v3_plugin_factory_2}"; - if (v3_tuid_match(iid, v3_plugin_factory_3_iid)) - return "{v3_plugin_factory_3}"; - if (v3_tuid_match(iid, v3_component_iid)) - return "{v3_component}"; - if (v3_tuid_match(iid, v3_bstream_iid)) - return "{v3_bstream}"; - if (v3_tuid_match(iid, v3_event_list_iid)) - return "{v3_event_list}"; - if (v3_tuid_match(iid, v3_param_value_queue_iid)) - return "{v3_param_value_queue}"; - if (v3_tuid_match(iid, v3_param_changes_iid)) - return "{v3_param_changes}"; - if (v3_tuid_match(iid, v3_process_context_requirements_iid)) - return "{v3_process_context_requirements}"; - if (v3_tuid_match(iid, v3_audio_processor_iid)) - return "{v3_audio_processor}"; - if (v3_tuid_match(iid, v3_component_handler_iid)) - return "{v3_component_handler}"; - if (v3_tuid_match(iid, v3_edit_controller_iid)) - return "{v3_edit_controller}"; - if (v3_tuid_match(iid, v3_plugin_view_iid)) - return "{v3_plugin_view}"; - if (v3_tuid_match(iid, v3_plugin_frame_iid)) - return "{v3_plugin_frame}"; - if (v3_tuid_match(iid, v3_plugin_view_content_scale_steinberg_iid)) - return "{v3_plugin_view_content_scale_steinberg}"; - if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid)) - return "{v3_plugin_view_parameter_finder}"; - /* - if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0) - return "{dpf_tuid_class}"; - if (std::memcmp(iid, dpf_tuid_component, sizeof(dpf_tuid)) == 0) - return "{dpf_tuid_component}"; - if (std::memcmp(iid, dpf_tuid_controller, sizeof(dpf_tuid)) == 0) - return "{dpf_tuid_controller}"; - if (std::memcmp(iid, dpf_tuid_processor, sizeof(dpf_tuid)) == 0) - return "{dpf_tuid_processor}"; - if (std::memcmp(iid, dpf_tuid_view, sizeof(dpf_tuid)) == 0) - return "{dpf_tuid_view}"; - */ - - static char buf[46]; - std::snprintf(buf, sizeof(buf), "{0x%08X,0x%08X,0x%08X,0x%08X}", - (uint32_t)d_cconst(iid[ 0], iid[ 1], iid[ 2], iid[ 3]), - (uint32_t)d_cconst(iid[ 4], iid[ 5], iid[ 6], iid[ 7]), - (uint32_t)d_cconst(iid[ 8], iid[ 9], iid[10], iid[11]), - (uint32_t)d_cconst(iid[12], iid[13], iid[14], iid[15])); - return buf; -} +const char* tuid2str(const v3_tuid iid); // -------------------------------------------------------------------------------------------------------------------- @@ -145,7 +85,6 @@ public: scaleFactor), fView(view), fFrame(nullptr), - fFrameArg(nullptr), fScaleFactor(scaleFactor) { // TESTING awful idea dont reuse @@ -221,12 +160,9 @@ public: #endif } - v3_result setFrame(v3_plugin_frame* const frame, void* const arg) noexcept + v3_result setFrame(v3_plugin_frame** const frame) noexcept { - d_stdout("setFrame %p %p", frame, arg); fFrame = frame; - fFrameArg = arg; - return V3_OK; } @@ -257,8 +193,7 @@ private: // VST3 stuff v3_plugin_view** const fView; - v3_plugin_frame* fFrame; - void* fFrameArg; + v3_plugin_frame** fFrame; // v3_component_handler_cpp** handler = nullptr; // Temporary data @@ -304,8 +239,7 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(fFrameArg != nullptr,); - d_stdout("from UI setSize %u %u | %p %p %p", width, height, fView, fFrame, fFrameArg); + d_stdout("from UI setSize %u %u | %p %p", width, height, fView, fFrame); #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); @@ -317,7 +251,7 @@ private: std::memset(&rect, 0, sizeof(rect)); rect.right = width; rect.bottom = height; - fFrame->resize_view(fFrameArg, fView, &rect); + v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); } static void setSizeCallback(void* ptr, uint width, uint height) @@ -546,13 +480,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((uintptr_t)parent, scaleFactor, view->sampleRate, view->instancePointer, (v3_plugin_view**)self); - - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_plugin_frame* const frameptr - = view->frame != nullptr ? (v3_plugin_frame*)((uint8_t*)*(view->frame)+sizeof(v3_funknown)) - : nullptr; - - view->uivst3->setFrame(frameptr, view->frame); + view->uivst3->setFrame(view->frame); // view->uivst3->setHandler(view->handler); return V3_OK; } @@ -613,11 +541,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - // special case: allow UI to not be attached yet, as a way to get size before window creation - if (UIVst3* const uivst3 = view->uivst3) return uivst3->getSize(rect); + // special case: allow UI to not be attached yet, as a way to get size before window creation + const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -660,14 +588,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->frame = frame; if (UIVst3* const uivst3 = view->uivst3) - { - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_plugin_frame* const frameptr - = frame != nullptr ? (v3_plugin_frame*)((uint8_t*)*(frame)+sizeof(v3_funknown)) - : nullptr; - - return uivst3->setFrame(frameptr, frame); - } + return uivst3->setFrame(frame); return V3_NOT_INITIALISED; }; @@ -697,7 +618,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { }; // -------------------------------------------------------------------------------------------------------------------- -// dpf_plugin_view_create (called from DSP side) +// dpf_plugin_view_create (called from plugin side) v3_funknown** dpf_plugin_view_create(void* instancePointer, double sampleRate); diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h index d1ed5bf3..02c0ca06 100644 --- a/distrho/src/travesty/audio_processor.h +++ b/distrho/src/travesty/audio_processor.h @@ -28,8 +28,9 @@ typedef uint64_t v3_speaker_arrangement; enum { - V3_SPEAKER_L = 1, - V3_SPEAKER_R = 1 << 1 + V3_SPEAKER_L = 1 << 0, + V3_SPEAKER_R = 1 << 1, + V3_SPEAKER_M = 1 << 19 }; /** @@ -42,14 +43,19 @@ enum v3_process_mode { V3_OFFLINE }; -inline static const char * -v3_process_mode_str(int32_t d) +static inline +const char* v3_process_mode_str(int32_t d) { - switch (d) { - case V3_REALTIME: return "V3_REALTIME"; - case V3_PREFETCH: return "V3_PREFETCH"; - case V3_OFFLINE: return "V3_OFFLINE"; - default: return "[unknown]"; + switch (d) + { + case V3_REALTIME: + return "V3_REALTIME"; + case V3_PREFETCH: + return "V3_PREFETCH"; + case V3_OFFLINE: + return "V3_OFFLINE"; + default: + return "[unknown]"; } } @@ -58,13 +64,17 @@ enum { V3_SAMPLE_64 }; -inline static const char * -v3_sample_size_str(int32_t d) +static inline +const char* v3_sample_size_str(int32_t d) { - switch (d) { - case V3_SAMPLE_32: return "V3_SAMPLE_32"; - case V3_SAMPLE_64: return "V3_SAMPLE_64"; - default: return "[unknown]"; + switch (d) + { + case V3_SAMPLE_32: + return "V3_SAMPLE_32"; + case V3_SAMPLE_64: + return "V3_SAMPLE_64"; + default: + return "[unknown]"; } } @@ -82,31 +92,24 @@ struct v3_process_setup { struct v3_param_value_queue { struct v3_funknown; - V3_API v3_param_id (*get_param_id)(void *self); - V3_API int32_t (*get_point_count)(void *self); - - V3_API v3_result (*get_point) - (void *self, int32_t idx, int32_t *sample_offset, double *value); - V3_API v3_result (*add_point) - (void *self, int32_t sample_offset, double value, int32_t *idx); + V3_API v3_param_id (*get_param_id)(void* self); + V3_API int32_t (*get_point_count)(void* self); + V3_API v3_result (*get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); + V3_API v3_result (*add_point)(void* self, int32_t sample_offset, double value, int32_t* idx); }; -static const v3_tuid v3_param_value_queue_iid = +static constexpr const v3_tuid v3_param_value_queue_iid = V3_ID(0x01263A18, 0xED074F6F, 0x98C9D356, 0x4686F9BA); struct v3_param_changes { struct v3_funknown; - V3_API int32_t (*get_param_count)(void *self); - - V3_API struct v3_param_value_queue **(*get_param_data) - (void *self, int32_t idx); - - V3_API struct v3_param_value_queue **(*add_param_data) - (void *self, v3_param_id *id, int32_t *index); + V3_API int32_t (*get_param_count)(void* self); + V3_API struct v3_param_value_queue** (*get_param_data)(void* self, int32_t idx); + V3_API struct v3_param_value_queue** (*add_param_data)(void* self, v3_param_id* id, int32_t* index); }; -static const v3_tuid v3_param_changes_iid = +static constexpr const v3_tuid v3_param_changes_iid = V3_ID(0xA4779663, 0x0BB64A56, 0xB44384A8, 0x466FEB9D); /** @@ -121,55 +124,41 @@ struct v3_frame_rate { struct v3_chord { uint8_t key_note; uint8_t root_note; - int16_t chord_mask; }; enum { - V3_PROCESS_CTX_PLAYING = 1 << 1, - V3_PROCESS_CTX_CYCLE_ACTIVE = 1 << 2, - V3_PROCESS_CTX_RECORDING = 1 << 3, - + V3_PROCESS_CTX_PLAYING = 1 << 1, + V3_PROCESS_CTX_CYCLE_ACTIVE = 1 << 2, + V3_PROCESS_CTX_RECORDING = 1 << 3, V3_PROCESS_CTX_SYSTEM_TIME_VALID = 1 << 8, - V3_PROCESS_CTX_CONT_TIME_VALID = 1 << 17, V3_PROCESS_CTX_PROJECT_TIME_VALID = 1 << 9, - + V3_PROCESS_CTX_TEMPO_VALID = 1 << 10, V3_PROCESS_CTX_BAR_POSITION_VALID = 1 << 11, V3_PROCESS_CTX_CYCLE_VALID = 1 << 12, - - V3_PROCESS_CTX_TEMPO_VALID = 1 << 10, V3_PROCESS_CTX_TIME_SIG_VALID = 1 << 13, - V3_PROCESS_CTX_CHORD_VALID = 1 << 18, - V3_PROCESS_CTX_SMPTE_VALID = 1 << 14, - - V3_PROCESS_CTX_NEXT_CLOCK_VALID = 1 << 15 + V3_PROCESS_CTX_NEXT_CLOCK_VALID = 1 << 15, + V3_PROCESS_CTX_CONT_TIME_VALID = 1 << 17, + V3_PROCESS_CTX_CHORD_VALID = 1 << 18 }; struct v3_process_context { uint32_t state; - double sample_rate; int64_t project_time_in_samples; // with loop - int64_t system_time_ns; int64_t continuous_time_in_samples; // without loop - double project_time_quarters; double bar_position_quarters; double cycle_start_quarters; double cycle_end_quarters; - double bpm; - int32_t time_sig_numerator; int32_t time_sig_denom; - struct v3_chord chord; - int32_t smpte_offset_subframes; struct v3_frame_rate frame_rate; - int32_t samples_to_next_clock; }; @@ -194,10 +183,10 @@ enum { struct v3_process_context_requirements { struct v3_funknown; - V3_API uint32_t (*get_process_context_requirements)(void *self); + V3_API uint32_t (*get_process_context_requirements)(void* self); }; -static const v3_tuid v3_process_context_requirements_iid = +static constexpr const v3_tuid v3_process_context_requirements_iid = V3_ID(0x2A654303, 0xEF764E3D, 0x95B5FE83, 0x730EF6D0); /** @@ -207,32 +196,25 @@ static const v3_tuid v3_process_context_requirements_iid = struct v3_audio_bus_buffers { int32_t num_channels; uint64_t channel_silence_bitset; - union { - float **channel_buffers_32; - double **channel_buffers_64; + float** channel_buffers_32; + double** channel_buffers_64; }; }; struct v3_process_data { int32_t process_mode; int32_t symbolic_sample_size; - int32_t nframes; - int32_t num_input_buses; int32_t num_output_buses; - - struct v3_audio_bus_buffers *inputs; - struct v3_audio_bus_buffers *outputs; - - struct v3_param_changes **input_params; - struct v3_param_changes **output_params; - - struct v3_event_list **input_events; - struct v3_event_list **output_events; - - struct v3_process_context *ctx; + struct v3_audio_bus_buffers* inputs; + struct v3_audio_bus_buffers* outputs; + struct v3_param_changes** input_params; + struct v3_param_changes** output_params; + struct v3_event_list** input_events; + struct v3_event_list** output_events; + struct v3_process_context* ctx; }; /** @@ -242,29 +224,18 @@ struct v3_process_data { struct v3_audio_processor { struct v3_funknown; - V3_API v3_result (*set_bus_arrangements) - (void *self, v3_speaker_arrangement *inputs, int32_t num_inputs, - v3_speaker_arrangement *outputs, int32_t num_outputs); - V3_API v3_result (*get_bus_arrangement) - (void *self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement *); - - V3_API v3_result (*can_process_sample_size) - (void *self, int32_t symbolic_sample_size); - - V3_API uint32_t (*get_latency_samples)(void *self); - - V3_API v3_result (*setup_processing) - (void *self, struct v3_process_setup *); - V3_API v3_result (*set_processing) - (void *self, v3_bool state); - - V3_API v3_result (*process) - (void *self, struct v3_process_data *); - - V3_API uint32_t (*get_tail_samples)(void *self); + V3_API v3_result (*set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, + v3_speaker_arrangement* outputs, int32_t num_outputs); + V3_API v3_result (*get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); + V3_API v3_result (*can_process_sample_size)(void* self, int32_t symbolic_sample_size); + V3_API uint32_t (*get_latency_samples)(void* self); + V3_API v3_result (*setup_processing)(void* self, struct v3_process_setup* setup); + V3_API v3_result (*set_processing)(void* self, v3_bool state); + V3_API v3_result (*process)(void* self, struct v3_process_data* data); + V3_API uint32_t (*get_tail_samples)(void* self); }; -static const v3_tuid v3_audio_processor_iid = +static constexpr const v3_tuid v3_audio_processor_iid = V3_ID(0x42043F99, 0xB7DA453C, 0xA569E79D, 0x9AAEC33D); #include "align_pop.h" diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 3b01d63e..c59fd5a8 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -20,6 +20,23 @@ #include #include +/** + * deal with C vs C++ differences + */ + +#ifdef __cplusplus +struct v3_funknown; +template static inline +constexpr T* v3_cpp_obj(T** obj) +{ + return (T*)((v3_funknown*)*obj + 1); +} +#else +# ifndef constexpr +# define constexpr +# endif +#endif + /** * various types */ @@ -31,15 +48,14 @@ typedef uint8_t v3_bool; typedef uint32_t v3_param_id; - /** * low-level ABI nonsense */ typedef uint8_t v3_tuid[16]; -inline static bool -v3_tuid_match(const v3_tuid a, const v3_tuid b) +static inline +bool v3_tuid_match(const v3_tuid a, const v3_tuid b) { return memcmp(a, b, sizeof(v3_tuid)) == 0; } @@ -128,14 +144,12 @@ enum { */ struct v3_funknown { - V3_API v3_result (*query_interface) - (void *self, const v3_tuid iid, void **obj); - - V3_API uint32_t (*ref)(void *self); - V3_API uint32_t (*unref)(void *self); + V3_API v3_result (*query_interface)(void* self, const v3_tuid iid, void** obj); + V3_API uint32_t (*ref)(void* self); + V3_API uint32_t (*unref)(void* self); }; -static const v3_tuid v3_funknown_iid = +static constexpr const v3_tuid v3_funknown_iid = V3_ID(0x00000000, 0x00000000, 0xC0000000, 0x00000046); /** @@ -145,10 +159,9 @@ static const v3_tuid v3_funknown_iid = struct v3_plugin_base { struct v3_funknown; - V3_API v3_result (*initialise) - (void *self, struct v3_funknown *context); - V3_API v3_result (*terminate)(void *self); + V3_API v3_result (*initialise)(void* self, struct v3_funknown* context); + V3_API v3_result (*terminate)(void* self); }; -static const v3_tuid v3_plugin_base_iid = +static constexpr const v3_tuid v3_plugin_base_iid = V3_ID(0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625); diff --git a/distrho/src/travesty/bstream.h b/distrho/src/travesty/bstream.h index b997ff32..e0db5eb6 100644 --- a/distrho/src/travesty/bstream.h +++ b/distrho/src/travesty/bstream.h @@ -27,15 +27,11 @@ enum v3_seek_mode { struct v3_bstream { struct v3_funknown; - V3_API v3_result (*read) - (void *self, void *buffer, int32_t num_bytes, int32_t *bytes_read); - V3_API v3_result (*write) - (void *self, void *buffer, int32_t num_bytes, int32_t *bytes_written); - V3_API v3_result (*seek) - (void *self, int64_t pos, int32_t seek_mode, int64_t *result); - V3_API v3_result (*tell) - (void *self, int64_t *pos); + V3_API v3_result (*read)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_read); + V3_API v3_result (*write)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_written); + V3_API v3_result (*seek)(void* self, int64_t pos, int32_t seek_mode, int64_t* result); + V3_API v3_result (*tell)(void* self, int64_t* pos); }; -static const v3_tuid v3_bstream_iid = +static constexpr const v3_tuid v3_bstream_iid = V3_ID(0xC3BF6EA2, 0x30994752, 0x9B6BF990, 0x1EE33E9B); diff --git a/distrho/src/travesty/component.h b/distrho/src/travesty/component.h index a354545b..f90b79c8 100644 --- a/distrho/src/travesty/component.h +++ b/distrho/src/travesty/component.h @@ -30,13 +30,17 @@ enum v3_media_types { V3_EVENT }; -inline static const char * -v3_media_type_str(int32_t type) +static inline +const char* v3_media_type_str(int32_t type) { - switch (type) { - case V3_AUDIO: return "V3_AUDIO"; - case V3_EVENT: return "V3_EVENT"; - default: return "[unknown]"; + switch (type) + { + case V3_AUDIO: + return "V3_AUDIO"; + case V3_EVENT: + return "V3_EVENT"; + default: + return "[unknown]"; } } @@ -45,13 +49,17 @@ enum v3_bus_direction { V3_OUTPUT }; -inline static const char * -v3_bus_direction_str(int32_t d) +static inline +const char* v3_bus_direction_str(int32_t d) { - switch (d) { - case V3_INPUT: return "V3_INPUT"; - case V3_OUTPUT: return "V3_OUTPUT"; - default: return "[unknown]"; + switch (d) + { + case V3_INPUT: + return "V3_INPUT"; + case V3_OUTPUT: + return "V3_OUTPUT"; + default: + return "[unknown]"; } } @@ -61,7 +69,7 @@ enum v3_bus_types { }; enum v3_bus_flags { - V3_DEFAULT_ACTIVE = 1, + V3_DEFAULT_ACTIVE = 1 << 0, V3_IS_CONTROL_VOLTAGE = 1 << 1 }; @@ -69,7 +77,6 @@ struct v3_bus_info { int32_t media_type; int32_t direction; int32_t channel_count; - v3_str_128 bus_name; int32_t bus_type; uint32_t flags; @@ -84,34 +91,20 @@ struct v3_routing_info; struct v3_component { struct v3_plugin_base; - V3_API v3_result (*get_controller_class_id) - (void *self, v3_tuid class_id); - - V3_API v3_result (*set_io_mode) - (void *self, int32_t io_mode); - - V3_API int32_t (*get_bus_count) - (void *self, int32_t media_type, int32_t bus_direction); - V3_API v3_result (*get_bus_info) - (void *self, int32_t media_type, int32_t bus_direction, - int32_t bus_idx, struct v3_bus_info *bus_info); - V3_API v3_result (*get_routing_info) - (void *self, struct v3_routing_info *input, - struct v3_routing_info *output); - V3_API v3_result (*activate_bus) - (void *self, int32_t media_type, int32_t bus_direction, - int32_t bus_idx, v3_bool state); - - V3_API v3_result (*set_active) - (void *self, v3_bool state); - - V3_API v3_result (*set_state) - (void *self, struct v3_bstream **); - V3_API v3_result (*get_state) - (void *self, struct v3_bstream **); + V3_API v3_result (*get_controller_class_id)(void* self, v3_tuid class_id); + V3_API v3_result (*set_io_mode)(void* self, int32_t io_mode); + V3_API int32_t (*get_bus_count)(void* self, int32_t media_type, int32_t bus_direction); + V3_API v3_result (*get_bus_info)(void* self, int32_t media_type, int32_t bus_direction, + int32_t bus_idx, struct v3_bus_info* bus_info); + V3_API v3_result (*get_routing_info)(void* self, struct v3_routing_info* input, struct v3_routing_info* output); + V3_API v3_result (*activate_bus)(void* self, int32_t media_type, int32_t bus_direction, + int32_t bus_idx, v3_bool state); + V3_API v3_result (*set_active)(void* self, v3_bool state); + V3_API v3_result (*set_state)(void* self, struct v3_bstream **); + V3_API v3_result (*get_state)(void* self, struct v3_bstream **); }; -static const v3_tuid v3_component_iid = +static constexpr const v3_tuid v3_component_iid = V3_ID(0xE831FF31, 0xF2D54301, 0x928EBBEE, 0x25697802); #include "align_pop.h" diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 7a50906c..6e6d9260 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -29,18 +29,13 @@ struct v3_component_handler { struct v3_funknown; - V3_API v3_result (*begin_edit) - (void *self, v3_param_id); - V3_API v3_result (*perform_edit) - (void *self, v3_param_id, double value_normalised); - V3_API v3_result (*end_edit) - (void *self, v3_param_id); - - V3_API v3_result (*restart_component) - (void *self, int32_t flags); + V3_API v3_result (*begin_edit)(void* self, v3_param_id); + V3_API v3_result (*perform_edit)(void* self, v3_param_id, double value_normalised); + V3_API v3_result (*end_edit)(void* self, v3_param_id); + V3_API v3_result (*restart_component)(void* self, int32_t flags); }; -static const v3_tuid v3_component_handler_iid = +static constexpr const v3_tuid v3_component_handler_iid = V3_ID(0x93A0BEA3, 0x0BD045DB, 0x8E890B0C, 0xC1E46AC6); /** @@ -48,7 +43,7 @@ static const v3_tuid v3_component_handler_iid = */ enum { - V3_PARAM_CAN_AUTOMATE = 1, + V3_PARAM_CAN_AUTOMATE = 1 << 0, V3_PARAM_READ_ONLY = 1 << 1, V3_PARAM_WRAP_AROUND = 1 << 2, V3_PARAM_IS_LIST = 1 << 3, @@ -59,15 +54,11 @@ enum { struct v3_param_info { v3_param_id param_id; - v3_str_128 title; v3_str_128 short_title; v3_str_128 units; - int32_t step_count; - double default_normalised_value; - int32_t unit_id; int32_t flags; }; @@ -75,39 +66,22 @@ struct v3_param_info { struct v3_edit_controller { struct v3_plugin_base; - V3_API v3_result (*set_component_state) - (void *self, struct v3_bstream *); - V3_API v3_result (*set_state) - (void *self, struct v3_bstream *); - V3_API v3_result (*get_state) - (void *self, struct v3_bstream *); - - V3_API int32_t (*get_parameter_count)(void *self); - V3_API v3_result (*get_parameter_info) - (void *self, int32_t param_idx, struct v3_param_info *); - - V3_API v3_result (*get_parameter_string_for_value) - (void *self, v3_param_id, double normalised, v3_str_128 output); - V3_API v3_result (*get_parameter_value_for_string) - (void *self, v3_param_id, int16_t *input, double *output); - - V3_API double (*normalised_parameter_to_plain) - (void *self, v3_param_id, double normalised); - V3_API double (*plain_parameter_to_normalised) - (void *self, v3_param_id, double plain); - - V3_API double (*get_parameter_normalised)(void *self, v3_param_id); - V3_API v3_result (*set_parameter_normalised) - (void *self, v3_param_id, double normalised); - - V3_API v3_result (*set_component_handler) - (void *self, struct v3_component_handler **); - - V3_API struct v3_plugin_view **(*create_view) - (void *self, const char *name); + V3_API v3_result (*set_component_state)(void* self, struct v3_bstream*); + V3_API v3_result (*set_state)(void* self, struct v3_bstream*); + V3_API v3_result (*get_state)(void* self, struct v3_bstream*); + V3_API int32_t (*get_parameter_count)(void* self); + V3_API v3_result (*get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); + V3_API v3_result (*get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); + V3_API v3_result (*get_parameter_value_for_string)(void* self, v3_param_id, int16_t* input, double* output); + V3_API double (*normalised_parameter_to_plain)(void* self, v3_param_id, double normalised); + V3_API double (*plain_parameter_to_normalised)(void* self, v3_param_id, double plain); + V3_API double (*get_parameter_normalised)(void* self, v3_param_id); + V3_API v3_result (*set_parameter_normalised)(void* self, v3_param_id, double normalised); + V3_API v3_result (*set_component_handler)(void* self, struct v3_component_handler**); + V3_API struct v3_plugin_view **(*create_view)(void* self, const char* name); }; -static const v3_tuid v3_edit_controller_iid = +static constexpr const v3_tuid v3_edit_controller_iid = V3_ID(0xDCD7BBE3, 0x7742448D, 0xA874AACC, 0x979C759E); #include "align_pop.h" diff --git a/distrho/src/travesty/events.h b/distrho/src/travesty/events.h index 47d807d0..95fed04d 100644 --- a/distrho/src/travesty/events.h +++ b/distrho/src/travesty/events.h @@ -30,10 +30,8 @@ struct v3_event_note_on { int16_t channel; int16_t pitch; // MIDI note number - float tuning; float velocity; - int32_t length; int32_t note_id; }; @@ -41,9 +39,7 @@ struct v3_event_note_on { struct v3_event_note_off { int16_t channel; int16_t pitch; // MIDI note number - float velocity; - int32_t note_id; float tuning; }; @@ -51,14 +47,12 @@ struct v3_event_note_off { struct v3_event_data { uint32_t size; uint32_t type; - - const uint8_t *bytes; + const uint8_t* bytes; }; struct v3_event_poly_pressure { int16_t channel; int16_t pitch; - float pressure; int32_t note_id; }; @@ -67,7 +61,6 @@ struct v3_event_chord { int16_t root; int16_t bass_note; int16_t mask; - uint16_t text_len; const int16_t *text; }; @@ -75,14 +68,12 @@ struct v3_event_chord { struct v3_event_scale { int16_t root; int16_t mask; - uint16_t text_len; const int16_t *text; }; struct v3_event_legacy_midi_cc_out { uint8_t cc_number; - int8_t channel; int8_t value; int8_t value2; @@ -96,7 +87,6 @@ struct v3_event_note_expression_value { struct v3_event_note_expression_text { int32_t note_id; - uint32_t text_len; const int16_t *text; }; @@ -106,7 +96,7 @@ struct v3_event_note_expression_text { */ enum v3_event_flags { - V3_EVENT_IS_LIVE = 1 + V3_EVENT_IS_LIVE = 1 << 0 }; enum v3_event_type { @@ -118,19 +108,15 @@ enum v3_event_type { V3_EVENT_NOTE_EXP_TEXT = 5, V3_EVENT_CHORD = 6, V3_EVENT_SCALE = 7, - V3_EVENT_LEGACY_MIDI_CC_OUT = 65535 }; struct v3_event { int32_t bus_index; int32_t sample_offset; - double ppq_position; uint16_t flags; - uint16_t type; - union { struct v3_event_note_on note_on; struct v3_event_note_off note_off; @@ -151,14 +137,12 @@ struct v3_event { struct v3_event_list { struct v3_funknown; - V3_API uint32_t (*get_event_count)(void *self); - V3_API v3_result (*get_event) - (void *self, int32_t idx, struct v3_event *); - V3_API v3_result (*add_event) - (void *self, struct v3_event *); + V3_API uint32_t (*get_event_count)(void* self); + V3_API v3_result (*get_event)(void* self, int32_t idx, struct v3_event* event); + V3_API v3_result (*add_event)(void* self, struct v3_event* event); }; -static const v3_tuid v3_event_list_iid = +static constexpr const v3_tuid v3_event_list_iid = V3_ID(0x3A2C4214, 0x346349FE, 0xB2C4F397, 0xB9695A44); #include "align_pop.h" diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index b8c2f123..1cf233e0 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -39,19 +39,13 @@ struct v3_class_info { struct v3_plugin_factory { struct v3_funknown; - V3_API v3_result (*get_factory_info) - (void *self, struct v3_factory_info *); - - V3_API int32_t (*num_classes)(void *self); - - V3_API v3_result (*get_class_info) - (void *self, int32_t idx, struct v3_class_info *); - - V3_API v3_result (*create_instance) - (void *self, const v3_tuid class_id, const v3_tuid iid, void **instance); + V3_API v3_result (*get_factory_info)(void* self, struct v3_factory_info*); + V3_API int32_t (*num_classes)(void* self); + V3_API v3_result (*get_class_info)(void* self, int32_t idx, struct v3_class_info*); + V3_API v3_result (*create_instance)(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance); }; -static const v3_tuid v3_plugin_factory_iid = +static constexpr const v3_tuid v3_plugin_factory_iid = V3_ID(0x7A4D811C, 0x52114A1F, 0xAED9D2EE, 0x0B43BF9F); /** @@ -63,7 +57,6 @@ struct v3_class_info_2 { int32_t cardinality; // set to 0x7FFFFFFF char category[32]; char name[64]; - uint32_t class_flags; char sub_categories[128]; char vendor[64]; @@ -74,11 +67,10 @@ struct v3_class_info_2 { struct v3_plugin_factory_2 { struct v3_plugin_factory; - V3_API v3_result (*get_class_info_2) - (void *self, int32_t idx, struct v3_class_info_2 *); + V3_API v3_result (*get_class_info_2)(void* self, int32_t idx, struct v3_class_info_2*); }; -static const v3_tuid v3_plugin_factory_2_iid = +static constexpr const v3_tuid v3_plugin_factory_2_iid = V3_ID(0x0007B650, 0xF24B4C0B, 0xA464EDB9, 0xF00B2ABB); /** @@ -93,7 +85,6 @@ struct v3_class_info_3 { int32_t cardinality; // set to 0x7FFFFFFF char category[32]; int16_t name[64]; - uint32_t class_flags; char sub_categories[128]; int16_t vendor[64]; @@ -104,12 +95,9 @@ struct v3_class_info_3 { struct v3_plugin_factory_3 { struct v3_plugin_factory_2; - V3_API v3_result (*get_class_info_utf16) - (void *self, int32_t idx, struct v3_class_info_3 *); - - V3_API v3_result (*set_host_context) - (void *self, struct v3_funknown *host); + V3_API v3_result (*get_class_info_utf16)(void* self, int32_t idx, struct v3_class_info_3*); + V3_API v3_result (*set_host_context)(void* self, struct v3_funknown* host); }; -static const v3_tuid v3_plugin_factory_3_iid = +static constexpr const v3_tuid v3_plugin_factory_3_iid = V3_ID(0x4555A2AB, 0xC1234E57, 0x9B122910, 0x36878931); diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index 54e6e34e..e8467154 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -19,7 +19,7 @@ #include "base.h" /** - * base IPlugFrame stuff + * base view stuff */ struct v3_view_rect { @@ -41,50 +41,43 @@ struct v3_view_rect { # define V3_VIEW_PLATFORM_TYPE_NATIVE V3_VIEW_PLATFORM_TYPE_X11 #endif +/** + * plugin view + */ + struct v3_plugin_frame; struct v3_plugin_view { struct v3_funknown; - V3_API v3_result (*is_platform_type_supported) - (void *self, const char *platform_type); - - V3_API v3_result (*attached) - (void *self, void *parent, const char *platform_type); - V3_API v3_result (*removed)(void *self); - - V3_API v3_result (*on_wheel)(void *self, float distance); - V3_API v3_result (*on_key_down) - (void *self, int16_t key_char, int16_t key_code, int16_t modifiers); - V3_API v3_result (*on_key_up) - (void *self, int16_t key_char, int16_t key_code, int16_t modifiers); - - V3_API v3_result (*get_size) - (void *self, struct v3_view_rect *); - V3_API v3_result (*on_size) - (void *self, struct v3_view_rect *); - - V3_API v3_result (*on_focus) - (void *self, v3_bool state); - - V3_API v3_result (*set_frame) - (void *self, struct v3_plugin_frame **); - V3_API v3_result (*can_resize)(void *self); - V3_API v3_result (*check_size_constraint) - (void *self, struct v3_view_rect *); + V3_API v3_result (*is_platform_type_supported)(void* self, const char* platform_type); + V3_API v3_result (*attached)(void* self, void* parent, const char* platform_type); + V3_API v3_result (*removed)(void* self); + V3_API v3_result (*on_wheel)(void* self, float distance); + V3_API v3_result (*on_key_down)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); + V3_API v3_result (*on_key_up)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); + V3_API v3_result (*get_size)(void* self, struct v3_view_rect*); + V3_API v3_result (*on_size)(void* self, struct v3_view_rect*); + V3_API v3_result (*on_focus)(void* self, v3_bool state); + V3_API v3_result (*set_frame)(void* self, struct v3_plugin_frame**); + V3_API v3_result (*can_resize)(void* self); + V3_API v3_result (*check_size_constraint)(void* self, struct v3_view_rect*); }; -static const v3_tuid v3_plugin_view_iid = +static constexpr const v3_tuid v3_plugin_view_iid = V3_ID(0x5BC32507, 0xD06049EA, 0xA6151B52, 0x2B755B29); +/** + * plugin frame + */ + struct v3_plugin_frame { struct v3_funknown; - V3_API v3_result (*resize_view) - (void *self, struct v3_plugin_view **, struct v3_view_rect *); + V3_API v3_result (*resize_view)(void* self, struct v3_plugin_view**, struct v3_view_rect*); }; -static const v3_tuid v3_plugin_frame_iid = +static constexpr const v3_tuid v3_plugin_frame_iid = V3_ID(0x367FAF01, 0xAFA94693, 0x8D4DA2A0, 0xED0882A3); /** @@ -95,11 +88,10 @@ static const v3_tuid v3_plugin_frame_iid = struct v3_plugin_view_content_scale_steinberg { struct v3_funknown; - V3_API v3_result (*set_content_scale_factor) - (void *self, float factor); + V3_API v3_result (*set_content_scale_factor)(void* self, float factor); }; -static const v3_tuid v3_plugin_view_content_scale_steinberg_iid = +static constexpr const v3_tuid v3_plugin_view_content_scale_steinberg_iid = V3_ID(0x65ED9690, 0x8AC44525, 0x8AADEF7A, 0x72EA703F); /** @@ -109,9 +101,8 @@ static const v3_tuid v3_plugin_view_content_scale_steinberg_iid = struct v3_plugin_view_parameter_finder { struct v3_funknown; - V3_API v3_result (*find_parameter) - (void *self, int32_t x, int32_t y, v3_param_id *); + V3_API v3_result (*find_parameter)(void* self, int32_t x, int32_t y, v3_param_id *); }; -static const v3_tuid v3_plugin_view_parameter_finder_iid = +static constexpr const v3_tuid v3_plugin_view_parameter_finder_iid = V3_ID(0x0F618302, 0x215D4587, 0xA512073C, 0x77B9D383); From cfb1873d85f2d291f9338ae1897d4051b5df8f9e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 15:49:13 +0100 Subject: [PATCH 084/504] VST3: Implement programs on DSP side, update TODO items Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 123 +++++++++++++++++++++++---- distrho/src/DistrhoUIPrivateData.hpp | 5 ++ distrho/src/DistrhoUIVST3.cpp | 13 +-- distrho/src/travesty/base.h | 2 +- 4 files changed, 119 insertions(+), 24 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index fb285e5f..d8d55c84 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -27,11 +27,24 @@ /* TODO items: * - base component refcount handling - * - program support + * - parameter enumeration as lists + * - hide parameter outputs? + * - hide program parameter? * - state support + * - save and restore current program * - midi cc parameter mapping * - full MIDI1 encode and decode - * - call component handler restart with params-changed flag when setting program + * - decode version number (0x102030 -> 1.2.3) + * - bus arrangements + * - optional audio buses, create dummy buffer of max_block_size length for them + * - routing info, do we care? + * - set sidechain bus name from port group + * - implement getParameterValueForString + * - set factory class_flags + * - set factory sub_categories + * - set factory email (needs new DPF API, useful for LV2 as well) + * - do something with get_controller_class_id and set_io_mode? + * - call component handler restart with params-changed flag after changing program * - call component handler restart with latency-changed flag when latency changes */ @@ -227,9 +240,14 @@ public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), + fParameterOffset(fPlugin.getParameterOffset()), fParameterValues(nullptr) #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fHostEventOutputHandle(nullptr) +#endif +#if DISTRHO_PLUGIN_WANT_PROGRAMS + , fCurrentProgram(0), + fProgramCountMinusOne(fPlugin.getProgramCount()-1) #endif { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 @@ -417,7 +435,7 @@ public: { const AudioPortWithBusId& port(fPlugin.getAudioPort(true, i)); - // TODO find port group name + // TODO find port group name for sidechain buses if (port.busId == busId) { strncpy_utf16(busName, port.name, 128); @@ -469,7 +487,7 @@ public: { const AudioPortWithBusId& port(fPlugin.getAudioPort(false, i)); - // TODO find port group name + // TODO find port group name for sidechain buses if (port.busId == busId) { strncpy_utf16(busName, port.name, 128); @@ -558,6 +576,9 @@ public: #if DISTRHO_PLUGIN_WANT_STATE // TODO #endif +#if DISTRHO_PLUGIN_WANT_PROGRAMS + // TODO +#endif if (const uint32_t paramCount = fPlugin.getParameterCount()) { @@ -1027,14 +1048,27 @@ public: int32_t getParameterCount() const noexcept { - return fPlugin.getParameterCount(); + return fPlugin.getParameterCount() + fParameterOffset; } v3_result getParameterInfo(const int32_t index, v3_param_info* const info) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(index >= 0, V3_INVALID_ARG); - const uint32_t uindex = static_cast(index); +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (index == 0) + { + std::memset(info, 0, sizeof(v3_param_info)); + info->param_id = index; + info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_LIST | V3_PARAM_PROGRAM_CHANGE; + info->step_count = fProgramCountMinusOne; + strncpy_utf16(info->title, "Current Program", 128); + strncpy_utf16(info->short_title, "Program", 128); + return V3_OK; + } +#endif + + const uint32_t uindex = static_cast(index) - fParameterOffset; DISTRHO_SAFE_ASSERT_RETURN(uindex < fPlugin.getParameterCount(), V3_INVALID_ARG); // set up flags @@ -1082,15 +1116,35 @@ public: v3_result getParameterStringForValue(const v3_param_id index, const double normalised, v3_str_128 output) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); - snprintf_f32_utf16(output, fPlugin.getParameterRanges(index).getUnnormalizedValue(normalised), 128); +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (index == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(normalised >= 0.0 && normalised <= 1.0, V3_INVALID_ARG); + + const uint32_t program = std::round(normalised * fProgramCountMinusOne); + strncpy_utf16(output, fPlugin.getProgramName(program), 128); + return V3_OK; + } +#endif + + const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + snprintf_f32_utf16(output, ranges.getUnnormalizedValue(normalised), 128); return V3_OK; } v3_result getParameterValueForString(const v3_param_id index, int16_t*, double*) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (index == 0) + { + // TODO find program index based on name + return V3_NOT_IMPLEMENTED; + } +#endif // TODO return V3_NOT_IMPLEMENTED; @@ -1098,29 +1152,57 @@ public: double normalisedParameterToPlain(const v3_param_id index, const double normalised) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (index == 0) + return std::round(normalised * fProgramCountMinusOne); +#endif - return fPlugin.getParameterRanges(index).getUnnormalizedValue(normalised); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + return ranges.getUnnormalizedValue(normalised); }; double plainParameterToNormalised(const v3_param_id index, const double plain) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (index == 0) + return std::max(0.0, std::min(1.0, plain / fProgramCountMinusOne)); +#endif - return fPlugin.getParameterRanges(index).getNormalizedValue(plain); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + return ranges.getNormalizedValue(plain); }; double getParameterNormalized(const v3_param_id index) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), 0.0); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, 0.0); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (index == 0) + return std::max(0.0, std::min(1.0, (double)fCurrentProgram / fProgramCountMinusOne)); +#endif const float value = fPlugin.getParameterValue(index); - return fPlugin.getParameterRanges(index).getNormalizedValue(value); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + return ranges.getNormalizedValue(value); } v3_result setParameterNormalized(const v3_param_id index, const double value) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (index == 0) + { + fCurrentProgram = std::round(value * fProgramCountMinusOne); + fPlugin.loadProgram(fCurrentProgram); + return V3_OK; + } +#endif const uint32_t hints = fPlugin.getParameterHints(index); const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); @@ -1158,6 +1240,7 @@ private: v3_component_handler** fComponentHandler; // Temporary data + const uint32_t fParameterOffset; float* fParameterValues; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT MidiEvent fMidiEvents[kMaxMidiEvents]; @@ -1165,6 +1248,10 @@ private: #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT v3_event_list** fHostEventOutputHandle; #endif +#if DISTRHO_PLUGIN_WANT_PROGRAMS + uint32_t fCurrentProgram; + const uint32_t fProgramCountMinusOne; +#endif #if DISTRHO_PLUGIN_WANT_TIMEPOS TimePosition fTimePosition; #endif @@ -2276,7 +2363,7 @@ struct dpf_factory : v3_plugin_factory_cpp { info->class_flags = 0; // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), ARRAY_SIZE(info->vendor)); - DISTRHO_NAMESPACE::snprintf_u32(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); + DISTRHO_NAMESPACE::snprintf_u32(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); // FIXME DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; @@ -2297,7 +2384,7 @@ struct dpf_factory : v3_plugin_factory_cpp { info->class_flags = 0; // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", ARRAY_SIZE(info->sub_categories)); // TODO DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); - DISTRHO_NAMESPACE::snprintf_u32_utf16(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); + DISTRHO_NAMESPACE::snprintf_u32_utf16(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); // FIXME DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index ac6bd838..57744cff 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -331,6 +331,11 @@ struct UI::PrivateData { # if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) parameterOffset += 1; # endif +#endif +#ifdef DISTRHO_PLUGIN_TARGET_VST3 +# if DISTRHO_PLUGIN_WANT_PROGRAMS + parameterOffset += 1; +# endif #endif } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index c7f297ca..99307408 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -42,12 +42,15 @@ #include "travesty/view.h" /* TODO items: - * - see how to handle external non-embed UI build - * - program listener - * - state listener and sender + * - disable UI if non-embed UI build + * - parameter change listener + * - parameter change sender + * - program change listener + * - program change sender + * - state change listener + * - state change sender * - sample rate change listener - * - call component handler restart with params-changed flag when setting program - * - call component handler restart with latency-changed flag when latency changes + * - call component handler restart with params-changed flag when setting program? */ START_NAMESPACE_DISTRHO diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index c59fd5a8..7d6a687c 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -29,7 +29,7 @@ struct v3_funknown; template static inline constexpr T* v3_cpp_obj(T** obj) { - return (T*)((v3_funknown*)*obj + 1); + return (T*)((uint8_t*)*obj + sizeof(void*)*3); } #else # ifndef constexpr From 80824ae66632dddbbfa7484ddd6e9afe934c28d2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 16:11:02 +0100 Subject: [PATCH 085/504] Cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 57 +++++++------------------------ distrho/src/travesty/base.h | 1 - 2 files changed, 13 insertions(+), 45 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index d8d55c84..6a214d6d 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -44,7 +44,7 @@ * - set factory sub_categories * - set factory email (needs new DPF API, useful for LV2 as well) * - do something with get_controller_class_id and set_io_mode? - * - call component handler restart with params-changed flag after changing program + * - call component handler restart with params-changed flag after changing program (doesnt seem to be needed..?) * - call component handler restart with latency-changed flag when latency changes */ @@ -571,7 +571,7 @@ public: * the parameter symbol is used as the "key", so it is possible to reorder them or even remove and add safely. * the number of states must remain constant though. */ - v3_result setState(v3_bstream* const stream, void* arg) + v3_result setState(v3_bstream** const stream) { #if DISTRHO_PLUGIN_WANT_STATE // TODO @@ -593,7 +593,7 @@ public: for (int32_t pos = 0, read;; pos += read) { std::memset(buffer, '\xff', sizeof(buffer)); - res = stream->read(arg, buffer, sizeof(buffer)-1, &read); + res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); @@ -648,7 +648,7 @@ public: return V3_OK; } - v3_result getState(v3_bstream* const stream, void* arg) + v3_result getState(v3_bstream** const stream) { const uint32_t paramCount = fPlugin.getParameterCount(); #if DISTRHO_PLUGIN_WANT_STATE @@ -661,7 +661,7 @@ public: { char buffer = '\0'; int32_t ignored; - return stream->write(arg, &buffer, 1, &ignored); + return v3_cpp_obj(stream)->write(stream, &buffer, 1, &ignored); } String state; @@ -727,7 +727,7 @@ public: for (int32_t wrtntotal = 0, wrtn; wrtntotal < size; wrtntotal += wrtn) { wrtn = 0; - res = stream->write(arg, const_cast(buffer), size - wrtntotal, &wrtn); + res = v3_cpp_obj(stream)->write(stream, const_cast(buffer), size - wrtntotal, &wrtn); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); DISTRHO_SAFE_ASSERT_INT_RETURN(wrtn > 0, wrtn, V3_INTERNAL_ERR); @@ -932,15 +932,12 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t midiEventCount = 0; - if (v3_event_list** const eventarg = data->input_events) + if (v3_event_list** const eventptr = data->input_events) { - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_event_list* const eventptr = (v3_event_list*)((uint8_t*)(*eventarg)+sizeof(v3_funknown)); - v3_event event; - for (uint32_t i = 0, count = eventptr->get_event_count(eventarg); i < count; ++i) + for (uint32_t i = 0, count = v3_cpp_obj(eventptr)->get_event_count(eventptr); i < count; ++i) { - if (eventptr->get_event(eventarg, i, &event) != V3_OK) + if (v3_cpp_obj(eventptr)->get_event(eventptr, i, &event) != V3_OK) break; // check if event can be encoded as MIDI @@ -1366,10 +1363,7 @@ private: return true; } - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_event_list* const hostptr = (v3_event_list*)((uint8_t*)(*fHostEventOutputHandle)+sizeof(v3_funknown)); - - return hostptr->add_event(fHostEventOutputHandle, &event) == V3_OK; + return v3_cpp_obj(fHostEventOutputHandle)->add_event(fHostEventOutputHandle, &event) == V3_OK; } static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) @@ -1478,12 +1472,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); #if 0 - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_bstream* const streamptr - = stream != nullptr ? (v3_bstream*)((uint8_t*)stream+sizeof(v3_funknown)) - : nullptr; - - return vst3->setComponentState(streamptr, stream); + return vst3->setComponentState(stream); #endif return V3_NOT_IMPLEMENTED; }; @@ -1498,11 +1487,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); #if 0 - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_bstream* const streamptr - = stream != nullptr ? (v3_bstream*)((uint8_t*)stream+sizeof(v3_funknown)) - : nullptr; - return vst3->setState(stream); #endif return V3_NOT_IMPLEMENTED; @@ -1518,11 +1502,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); #if 0 - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_bstream* const streamptr - = stream != nullptr ? (v3_bstream*)((uint8_t*)stream+sizeof(v3_funknown)) - : nullptr; - return vst3->getState(stream); #endif return V3_NOT_IMPLEMENTED; @@ -2215,12 +2194,7 @@ struct dpf_component : v3_component_cpp { PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_bstream* const streamptr - = stream != nullptr ? (v3_bstream*)((uint8_t*)*(stream)+sizeof(v3_funknown)) - : nullptr; - - return vst3->setState(streamptr, stream); + return vst3->setState(stream); }; comp.get_state = []V3_API(void* self, v3_bstream** stream) -> v3_result @@ -2232,12 +2206,7 @@ struct dpf_component : v3_component_cpp { PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - // offset struct by sizeof(v3_funknown), because of differences between C and C++ - v3_bstream* const streamptr - = stream != nullptr ? (v3_bstream*)((uint8_t*)*(stream)+sizeof(v3_funknown)) - : nullptr; - - return vst3->getState(streamptr, stream); + return vst3->getState(stream); }; } }; diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 7d6a687c..7a6faca4 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -25,7 +25,6 @@ */ #ifdef __cplusplus -struct v3_funknown; template static inline constexpr T* v3_cpp_obj(T** obj) { From 4645f65e9610262aff8ed8db0537ec7743bf6658 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 18:36:22 +0100 Subject: [PATCH 086/504] VST3: Implement UI->DSP parameter changes Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 6 ++ distrho/src/DistrhoPluginVST3.cpp | 75 ++++++++++++------------ distrho/src/DistrhoUIPrivateData.hpp | 1 + distrho/src/DistrhoUIVST3.cpp | 79 +++++++++++++++----------- distrho/src/travesty/base.h | 6 +- distrho/src/travesty/edit_controller.h | 13 +++++ 6 files changed, 112 insertions(+), 68 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 202887ce..625a8822 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -170,6 +170,12 @@ struct Plugin::PrivateData { # if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) parameterOffset += 1; # endif +#endif + +#ifdef DISTRHO_PLUGIN_TARGET_VST3 +# if DISTRHO_PLUGIN_WANT_PROGRAMS + parameterOffset += 1; +# endif #endif } diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 6a214d6d..0def6475 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1048,15 +1048,15 @@ public: return fPlugin.getParameterCount() + fParameterOffset; } - v3_result getParameterInfo(const int32_t index, v3_param_info* const info) const noexcept + v3_result getParameterInfo(const int32_t rindex, v3_param_info* const info) const noexcept { - DISTRHO_SAFE_ASSERT_RETURN(index >= 0, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INVALID_ARG); #if DISTRHO_PLUGIN_WANT_PROGRAMS - if (index == 0) + if (rindex == 0) { std::memset(info, 0, sizeof(v3_param_info)); - info->param_id = index; + info->param_id = rindex; info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_LIST | V3_PARAM_PROGRAM_CHANGE; info->step_count = fProgramCountMinusOne; strncpy_utf16(info->title, "Current Program", 128); @@ -1065,8 +1065,8 @@ public: } #endif - const uint32_t uindex = static_cast(index) - fParameterOffset; - DISTRHO_SAFE_ASSERT_RETURN(uindex < fPlugin.getParameterCount(), V3_INVALID_ARG); + const uint32_t index = static_cast(rindex) - fParameterOffset; + DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fPlugin.getParameterCount(), index, V3_INVALID_ARG); // set up flags int32_t flags = 0; @@ -1100,7 +1100,7 @@ public: step_count = ranges.max - ranges.min - 1; std::memset(info, 0, sizeof(v3_param_info)); - info->param_id = index; + info->param_id = rindex; info->flags = flags; info->step_count = step_count; info->default_normalised_value = ranges.getNormalizedValue(ranges.def); @@ -1111,12 +1111,12 @@ public: return V3_OK; } - v3_result getParameterStringForValue(const v3_param_id index, const double normalised, v3_str_128 output) + v3_result getParameterStringForValue(const v3_param_id rindex, const double normalised, v3_str_128 output) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, V3_INVALID_ARG); #if DISTRHO_PLUGIN_WANT_PROGRAMS - if (index == 0) + if (rindex == 0) { DISTRHO_SAFE_ASSERT_RETURN(normalised >= 0.0 && normalised <= 1.0, V3_INVALID_ARG); @@ -1126,17 +1126,17 @@ public: } #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); snprintf_f32_utf16(output, ranges.getUnnormalizedValue(normalised), 128); return V3_OK; } - v3_result getParameterValueForString(const v3_param_id index, int16_t*, double*) + v3_result getParameterValueForString(const v3_param_id rindex, int16_t*, double*) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, V3_INVALID_ARG); #if DISTRHO_PLUGIN_WANT_PROGRAMS - if (index == 0) + if (rindex == 0) { // TODO find program index based on name return V3_NOT_IMPLEMENTED; @@ -1147,53 +1147,53 @@ public: return V3_NOT_IMPLEMENTED; }; - double normalisedParameterToPlain(const v3_param_id index, const double normalised) + double normalisedParameterToPlain(const v3_param_id rindex, const double normalised) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS - if (index == 0) + if (rindex == 0) return std::round(normalised * fProgramCountMinusOne); #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); return ranges.getUnnormalizedValue(normalised); }; - double plainParameterToNormalised(const v3_param_id index, const double plain) + double plainParameterToNormalised(const v3_param_id rindex, const double plain) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS - if (index == 0) + if (rindex == 0) return std::max(0.0, std::min(1.0, plain / fProgramCountMinusOne)); #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); return ranges.getNormalizedValue(plain); }; - double getParameterNormalized(const v3_param_id index) + double getParameterNormalized(const v3_param_id rindex) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, 0.0); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS - if (index == 0) + if (rindex == 0) return std::max(0.0, std::min(1.0, (double)fCurrentProgram / fProgramCountMinusOne)); #endif - const float value = fPlugin.getParameterValue(index); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index - fParameterOffset)); + const float value = fPlugin.getParameterValue(rindex - fParameterOffset); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); return ranges.getNormalizedValue(value); } - v3_result setParameterNormalized(const v3_param_id index, const double value) + v3_result setParameterNormalized(const v3_param_id rindex, const double value) { - DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount() + fParameterOffset, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); #if DISTRHO_PLUGIN_WANT_PROGRAMS - if (index == 0) + if (rindex == 0) { fCurrentProgram = std::round(value * fProgramCountMinusOne); fPlugin.loadProgram(fCurrentProgram); @@ -1201,6 +1201,7 @@ public: } #endif + const uint32_t index = rindex - fParameterOffset; const uint32_t hints = fPlugin.getParameterHints(index); const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); @@ -1378,7 +1379,8 @@ private: // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from DSP side) -v3_funknown** dpf_plugin_view_create(void* instancePointer, double sampleRate); +v3_funknown** dpf_plugin_view_create(v3_edit_controller** controller, v3_component_handler** handler, + void* instancePointer, double sampleRate); #endif // -------------------------------------------------------------------------------------------------------------------- @@ -1570,7 +1572,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double { - d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %f", self, plain); + d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1583,7 +1585,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double { // NOTE very noisy, called many times - // d_stdout("dpf_edit_controller::get_parameter_normalised => %p", self); + // d_stdout("dpf_edit_controller::get_parameter_normalised => %p %u", self, index); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); @@ -1595,7 +1597,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result { - d_stdout("dpf_edit_controller::set_parameter_normalised => %p %f", self, normalised); + d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1637,7 +1639,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); #if DISTRHO_PLUGIN_HAS_UI - return (v3_plugin_view**)dpf_plugin_view_create(vst3->getInstancePointer(), vst3->getSampleRate()); + return (v3_plugin_view**)dpf_plugin_view_create((v3_edit_controller**)self, + controller->handler, + vst3->getInstancePointer(), + vst3->getSampleRate()); #else return nullptr; #endif diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 57744cff..79c7deb2 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -332,6 +332,7 @@ struct UI::PrivateData { parameterOffset += 1; # endif #endif + #ifdef DISTRHO_PLUGIN_TARGET_VST3 # if DISTRHO_PLUGIN_WANT_PROGRAMS parameterOffset += 1; diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 99307408..34676693 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -74,8 +74,13 @@ const char* tuid2str(const v3_tuid iid); class UIVst3 : public Thread { public: - UIVst3(const intptr_t winId, const float scaleFactor, const double sampleRate, - void* const instancePointer, v3_plugin_view** const view) + UIVst3(v3_plugin_view** const view, + v3_edit_controller** const controller, + v3_component_handler** const handler, + const intptr_t winId, + const float scaleFactor, + const double sampleRate, + void* const instancePointer) : fUI(this, winId, sampleRate, editParameterCallback, setParameterCallback, @@ -87,6 +92,8 @@ public: instancePointer, scaleFactor), fView(view), + fController(controller), + fHandler(handler), fFrame(nullptr), fScaleFactor(scaleFactor) { @@ -196,41 +203,35 @@ private: // VST3 stuff v3_plugin_view** const fView; + v3_edit_controller** const fController; + v3_component_handler** const fHandler; v3_plugin_frame** fFrame; - // v3_component_handler_cpp** handler = nullptr; // Temporary data float fScaleFactor; - void editParameter(const uint32_t /*index*/, const bool /*started*/) const + void editParameter(const uint32_t rindex, const bool started) const { -// DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); -// -// v3_component_handler_cpp* const chandler = *handler; -// DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); -// -// if (started) -// chandler->handler.begin_edit(handler, index); -// else -// chandler->handler.end_edit(handler, index); + DISTRHO_SAFE_ASSERT_RETURN(fHandler != nullptr,); + + if (started) + v3_cpp_obj(fHandler)->begin_edit(fHandler, rindex); + else + v3_cpp_obj(fHandler)->end_edit(fHandler, rindex); } - static void editParameterCallback(void* ptr, uint32_t index, bool started) + static void editParameterCallback(void* ptr, uint32_t rindex, bool started) { - ((UIVst3*)ptr)->editParameter(index, started); + ((UIVst3*)ptr)->editParameter(rindex, started); } - void setParameterValue(const uint32_t /*index*/, const float /*realValue*/) + void setParameterValue(const uint32_t rindex, const float realValue) { -// DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); -// -// v3_component_handler_cpp* const chandler = *handler; -// DISTRHO_SAFE_ASSERT_RETURN(chandler != nullptr,); - -// const double value = vst3->plainParameterToNormalised(index, realValue); -// chandler->handler.perform_edit(handler, index, value); + DISTRHO_SAFE_ASSERT_RETURN(fController != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fHandler != nullptr,); - // TODO send change to DSP side? + const double value = v3_cpp_obj(fController)->plain_parameter_to_normalised(fController, rindex, realValue); + v3_cpp_obj(fHandler)->perform_edit(fHandler, rindex, value); } static void setParameterCallback(void* ptr, uint32_t rindex, float value) @@ -370,14 +371,22 @@ struct dpf_plugin_view : v3_plugin_view_cpp { ScopedPointer scale; ScopedPointer uivst3; // cached values + v3_edit_controller** const controller; + v3_component_handler** const handler; void* const instancePointer; double sampleRate; // v3_component_handler_cpp** handler = nullptr; v3_plugin_frame** frame = nullptr; - dpf_plugin_view(ScopedPointer* s, void* const instance, const double sr) + dpf_plugin_view(ScopedPointer* selfptr, + v3_edit_controller** const controllerptr, + v3_component_handler** const handlerptr, + void* const instance, + const double sr) : refcounter(1), - self(s), + self(selfptr), + controller(controllerptr), + handler(handlerptr), instancePointer(instance), sampleRate(sr) { @@ -481,10 +490,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - view->uivst3 = new UIVst3((uintptr_t)parent, scaleFactor, view->sampleRate, - view->instancePointer, (v3_plugin_view**)self); + view->uivst3 = new UIVst3((v3_plugin_view**)self, + view->controller, + view->handler, + (uintptr_t)parent, + scaleFactor, + view->sampleRate, + view->instancePointer); view->uivst3->setFrame(view->frame); - // view->uivst3->setHandler(view->handler); return V3_OK; } } @@ -623,12 +636,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from plugin side) -v3_funknown** dpf_plugin_view_create(void* instancePointer, double sampleRate); +v3_funknown** dpf_plugin_view_create(v3_edit_controller** controller, v3_component_handler** handler, + void* instancePointer, double sampleRate); -v3_funknown** dpf_plugin_view_create(void* const instancePointer, const double sampleRate) +v3_funknown** dpf_plugin_view_create(v3_edit_controller** const controller, v3_component_handler** const handler, + void* const instancePointer, const double sampleRate) { ScopedPointer* const viewptr = new ScopedPointer; - *viewptr = new dpf_plugin_view(viewptr, instancePointer, sampleRate); + *viewptr = new dpf_plugin_view(viewptr, controller, handler, instancePointer, sampleRate); return (v3_funknown**)viewptr; } diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 7a6faca4..9c54f215 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -28,7 +28,11 @@ template static inline constexpr T* v3_cpp_obj(T** obj) { - return (T*)((uint8_t*)*obj + sizeof(void*)*3); + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast(static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3)); } #else # ifndef constexpr diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 6e6d9260..5db8993d 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -84,4 +84,17 @@ struct v3_edit_controller { static constexpr const v3_tuid v3_edit_controller_iid = V3_ID(0xDCD7BBE3, 0x7742448D, 0xA874AACC, 0x979C759E); +#ifdef __cplusplus +template<> inline +constexpr v3_edit_controller* v3_cpp_obj(v3_edit_controller** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*5)); +} +#endif + #include "align_pop.h" From 80e35fb562e6bf011e339e7e361c813f9fab1263 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 18:44:29 +0100 Subject: [PATCH 087/504] Simplify some code Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 0def6475..f9a3cd1c 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -241,6 +241,7 @@ public: : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), fParameterOffset(fPlugin.getParameterOffset()), + fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), fParameterValues(nullptr) #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fHostEventOutputHandle(nullptr) @@ -1045,7 +1046,7 @@ public: int32_t getParameterCount() const noexcept { - return fPlugin.getParameterCount() + fParameterOffset; + return fRealParameterCount; } v3_result getParameterInfo(const int32_t rindex, v3_param_info* const info) const noexcept @@ -1113,7 +1114,7 @@ public: v3_result getParameterStringForValue(const v3_param_id rindex, const double normalised, v3_str_128 output) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) @@ -1133,7 +1134,7 @@ public: v3_result getParameterValueForString(const v3_param_id rindex, int16_t*, double*) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) @@ -1149,7 +1150,7 @@ public: double normalisedParameterToPlain(const v3_param_id rindex, const double normalised) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, 0.0); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) @@ -1162,7 +1163,7 @@ public: double plainParameterToNormalised(const v3_param_id rindex, const double plain) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, 0.0); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) @@ -1175,7 +1176,7 @@ public: double getParameterNormalized(const v3_param_id rindex) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, 0.0); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) @@ -1189,7 +1190,7 @@ public: v3_result setParameterNormalized(const v3_param_id rindex, const double value) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fPlugin.getParameterCount() + fParameterOffset, rindex, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); #if DISTRHO_PLUGIN_WANT_PROGRAMS @@ -1239,6 +1240,7 @@ private: // Temporary data const uint32_t fParameterOffset; + const uint32_t fRealParameterCount; // regular parameters + current program float* fParameterValues; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT MidiEvent fMidiEvents[kMaxMidiEvents]; From b3c3cd6448ae25c373ac0dc494de90a9cb481a2f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 18:54:42 +0100 Subject: [PATCH 088/504] Add a few code comments Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 14 ++++++++++++++ distrho/src/DistrhoUIVST3.cpp | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index f9a3cd1c..5ad26600 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -219,6 +219,14 @@ static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = // -------------------------------------------------------------------------------------------------------------------- +/** + * VST3 DSP class. + * + * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things. + * The DSP is created during the "initialise" component event, and destroyed during "terminate". + * + * The low-level VST3 stuff comes after. + */ class PluginVst3 { /* buses: we provide 1 for the main audio (if there is any) plus 1 for each sidechain or cv port. @@ -1377,6 +1385,12 @@ private: }; +// -------------------------------------------------------------------------------------------------------------------- + +/** + * VST3 low-level pointer thingies follow, proceed with care. + */ + #if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from DSP side) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 34676693..7aafbb98 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -71,6 +71,14 @@ const char* tuid2str(const v3_tuid iid); // -------------------------------------------------------------------------------------------------------------------- +/** + * VST3 UI class. + * + * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things. + * The UI is created during the "attach" view event, and destroyed during "removed". + * + * The low-level VST3 stuff comes after. + */ class UIVst3 : public Thread { public: @@ -293,6 +301,12 @@ private: #endif }; +// -------------------------------------------------------------------------------------------------------------------- + +/** + * VST3 low-level pointer thingies follow, proceed with care. + */ + // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale From 57a430e9d5cf79beb28d4e47c5bc2913ddb519c7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 19:11:05 +0100 Subject: [PATCH 089/504] Experiments with C++98 compat Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 4 +- distrho/src/DistrhoUIVST3.cpp | 84 ++++++++++++++++--------------- distrho/src/travesty/view.h | 4 +- 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 5ad26600..07d02d2b 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -121,8 +121,8 @@ const char* tuid2str(const v3_tuid iid) return "{v3_plugin_view}"; if (v3_tuid_match(iid, v3_plugin_frame_iid)) return "{v3_plugin_frame}"; - if (v3_tuid_match(iid, v3_plugin_view_content_scale_steinberg_iid)) - return "{v3_plugin_view_content_scale_steinberg}"; + if (v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) + return "{v3_plugin_view_content_scale_iid}"; if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid)) return "{v3_plugin_view_parameter_finder}"; if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 7aafbb98..8ec5b1ec 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -310,65 +310,70 @@ private: // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale -struct v3_plugin_view_content_scale_steinberg_cpp : v3_funknown { - v3_plugin_view_content_scale_steinberg scale; +struct v3_plugin_view_content_scale_cpp : v3_funknown { + v3_plugin_view_content_scale scale; }; -struct dpf_plugin_view_scale : v3_plugin_view_content_scale_steinberg_cpp { +struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { ScopedPointer& uivst3; // cached values float scaleFactor; - dpf_plugin_view_scale(ScopedPointer& v) + dpf_plugin_view_content_scale(ScopedPointer& v) : uivst3(v), scaleFactor(0.0f) { + query_interface = query_interface_fn; + ref = ref_fn; + unref = unref_fn; + scale.set_content_scale_factor = set_content_scale_factor_fn; + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_fn(void* self, const v3_tuid iid, void** iface) + { + d_stdout("dpf_plugin_view_content_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, - v3_plugin_view_content_scale_steinberg_iid + v3_plugin_view_content_scale_iid }; - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + for (const uint8_t* interface_iid : kSupportedInterfaces) { - d_stdout("dpf_plugin_view_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) + if (v3_tuid_match(interface_iid, iid)) { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } + *iface = self; + return V3_OK; } + } - return V3_NO_INTERFACE; - }; + return V3_NO_INTERFACE; + } - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; + // there is only a single instance of this, so we don't have to care here + static V3_API uint32_t ref_fn(void*) { return 1; }; + static V3_API uint32_t unref_fn(void*) { return 0; }; - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_view_content_scale_steinberg + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_view_content_scale_steinberg - scale.set_content_scale_factor = []V3_API(void* self, float factor) -> v3_result - { - d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); - dpf_plugin_view_scale* const scale = *(dpf_plugin_view_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_content_scale_factor_fn(void* self, float factor) + { + d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); + dpf_plugin_view_content_scale* const scale = *(dpf_plugin_view_content_scale**)self; + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALISED); - scale->scaleFactor = factor; + scale->scaleFactor = factor; - if (UIVst3* const uivst3 = scale->uivst3) - return uivst3->setContentScaleFactor(factor); + if (UIVst3* const uivst3 = scale->uivst3) + return uivst3->setContentScaleFactor(factor); - return V3_NOT_INITIALISED; - }; + return V3_NOT_INITIALISED; } }; @@ -382,14 +387,13 @@ struct v3_plugin_view_cpp : v3_funknown { struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic refcounter; ScopedPointer* self; - ScopedPointer scale; + ScopedPointer scale; ScopedPointer uivst3; // cached values v3_edit_controller** const controller; v3_component_handler** const handler; void* const instancePointer; double sampleRate; -// v3_component_handler_cpp** handler = nullptr; v3_plugin_frame** frame = nullptr; dpf_plugin_view(ScopedPointer* selfptr, @@ -440,10 +444,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); - if (v3_tuid_match(v3_plugin_view_content_scale_steinberg_iid, iid)) + if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) { if (view->scale == nullptr) - view->scale = new dpf_plugin_view_scale(view->uivst3); + view->scale = new dpf_plugin_view_content_scale(view->uivst3); *iface = &view->scale; return V3_OK; } diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index e8467154..80dde1c6 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -85,13 +85,13 @@ static constexpr const v3_tuid v3_plugin_frame_iid = * (same IID/iface as presonus view scaling) */ -struct v3_plugin_view_content_scale_steinberg { +struct v3_plugin_view_content_scale { struct v3_funknown; V3_API v3_result (*set_content_scale_factor)(void* self, float factor); }; -static constexpr const v3_tuid v3_plugin_view_content_scale_steinberg_iid = +static constexpr const v3_tuid v3_plugin_view_content_scale_iid = V3_ID(0x65ED9690, 0x8AC44525, 0x8AADEF7A, 0x72EA703F); /** From e41e03f7247349b36754a425e0d41d5029079773 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Sep 2021 23:55:04 +0100 Subject: [PATCH 090/504] VST3: Start handling UI->DSP messages, WIP Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 251 +++++++++++++++++++++---- distrho/src/DistrhoUIVST3.cpp | 213 +++++++++++++++++---- distrho/src/travesty/edit_controller.h | 13 ++ distrho/src/travesty/message.h | 55 ++++++ 4 files changed, 457 insertions(+), 75 deletions(-) create mode 100644 distrho/src/travesty/message.h diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 07d02d2b..08a25a1f 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -21,6 +21,7 @@ #include "travesty/component.h" #include "travesty/edit_controller.h" #include "travesty/factory.h" +#include "travesty/message.h" #include #include @@ -89,8 +90,49 @@ static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; const char* tuid2str(const v3_tuid iid) { + static constexpr const struct { + v3_tuid iid; + const char* name; + } extra_known_iids[] = { + { V3_ID(0x00000000,0x00000000,0x00000000,0x00000000), "(nil)" }, + // edit-controller + { V3_ID(0xF040B4B3,0xA36045EC,0xABCDC045,0xB4D5A2CC), "{v3_component_handler2|NOT}" }, + { V3_ID(0x7F4EFE59,0xF3204967,0xAC27A3AE,0xAFB63038), "{v3_edit_controller2|NOT}" }, + { V3_ID(0x067D02C1,0x5B4E274D,0xA92D90FD,0x6EAF7240), "{v3_component_handler_bus_activation|NOT}" }, + { V3_ID(0xDF0FF9F7,0x49B74669,0xB63AB732,0x7ADBF5E5), "{v3_midi_mapping|NOT}" }, + { V3_ID(0xC1271208,0x70594098,0xB9DD34B3,0x6BB0195E), "{v3_edit_controller_host_editing|NOT}" }, + // units + { V3_ID(0x8683B01F,0x7B354F70,0xA2651DEC,0x353AF4FF), "{v3_program_list_data|NOT}" }, + { V3_ID(0x6C389611,0xD391455D,0xB870B833,0x94A0EFDD), "{v3_unit_data|NOT}" }, + { V3_ID(0x4B5147F8,0x4654486B,0x8DAB30BA,0x163A3C56), "{v3_unit_handler|NOT}" }, + { V3_ID(0xF89F8CDF,0x699E4BA5,0x96AAC9A4,0x81452B01), "{v3_unit_handler2|NOT}" }, + { V3_ID(0x3D4BD6B5,0x913A4FD2,0xA886E768,0xA5EB92C1), "{v3_unit_info|NOT}" }, + // misc + { V3_ID(0x0F194781,0x8D984ADA,0xBBA0C1EF,0xC011D8D0), "{v3_info_listener|NOT}" }, + }; + + if (v3_tuid_match(iid, v3_audio_processor_iid)) + return "{v3_audio_processor}"; + if (v3_tuid_match(iid, v3_bstream_iid)) + return "{v3_bstream}"; + if (v3_tuid_match(iid, v3_component_iid)) + return "{v3_component}"; + if (v3_tuid_match(iid, v3_component_handler_iid)) + return "{v3_component_handler}"; + if (v3_tuid_match(iid, v3_connection_point_iid)) + return "{v3_connection_point_iid}"; + if (v3_tuid_match(iid, v3_edit_controller_iid)) + return "{v3_edit_controller}"; + if (v3_tuid_match(iid, v3_event_list_iid)) + return "{v3_event_list}"; if (v3_tuid_match(iid, v3_funknown_iid)) return "{v3_funknown}"; + if (v3_tuid_match(iid, v3_message_iid)) + return "{v3_message_iid}"; + if (v3_tuid_match(iid, v3_param_value_queue_iid)) + return "{v3_param_value_queue}"; + if (v3_tuid_match(iid, v3_param_changes_iid)) + return "{v3_param_changes}"; if (v3_tuid_match(iid, v3_plugin_base_iid)) return "{v3_plugin_base}"; if (v3_tuid_match(iid, v3_plugin_factory_iid)) @@ -99,32 +141,17 @@ const char* tuid2str(const v3_tuid iid) return "{v3_plugin_factory_2}"; if (v3_tuid_match(iid, v3_plugin_factory_3_iid)) return "{v3_plugin_factory_3}"; - if (v3_tuid_match(iid, v3_component_iid)) - return "{v3_component}"; - if (v3_tuid_match(iid, v3_bstream_iid)) - return "{v3_bstream}"; - if (v3_tuid_match(iid, v3_event_list_iid)) - return "{v3_event_list}"; - if (v3_tuid_match(iid, v3_param_value_queue_iid)) - return "{v3_param_value_queue}"; - if (v3_tuid_match(iid, v3_param_changes_iid)) - return "{v3_param_changes}"; - if (v3_tuid_match(iid, v3_process_context_requirements_iid)) - return "{v3_process_context_requirements}"; - if (v3_tuid_match(iid, v3_audio_processor_iid)) - return "{v3_audio_processor}"; - if (v3_tuid_match(iid, v3_component_handler_iid)) - return "{v3_component_handler}"; - if (v3_tuid_match(iid, v3_edit_controller_iid)) - return "{v3_edit_controller}"; - if (v3_tuid_match(iid, v3_plugin_view_iid)) - return "{v3_plugin_view}"; if (v3_tuid_match(iid, v3_plugin_frame_iid)) return "{v3_plugin_frame}"; + if (v3_tuid_match(iid, v3_plugin_view_iid)) + return "{v3_plugin_view}"; if (v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) return "{v3_plugin_view_content_scale_iid}"; if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid)) return "{v3_plugin_view_parameter_finder}"; + if (v3_tuid_match(iid, v3_process_context_requirements_iid)) + return "{v3_process_context_requirements}"; + if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_class}"; if (std::memcmp(iid, dpf_tuid_component, sizeof(dpf_tuid)) == 0) @@ -136,6 +163,12 @@ const char* tuid2str(const v3_tuid iid) if (std::memcmp(iid, dpf_tuid_view, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_view}"; + for (size_t i=0; i %s", message, v3_cpp_obj(message)->get_message_id(message)); + // TESTING, ensure host gets the message + requestParameterValueChange(2, 1.0f); + return V3_OK; + } + + // ---------------------------------------------------------------------------------------------------------------- private: // Plugin @@ -1309,13 +1353,15 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); - if (v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, index) != V3_OK) + const uint32_t rindex = index + fParameterOffset; + + if (v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex) != V3_OK) return false; const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value); - const bool ret = v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, index, normalized) == V3_OK; + const bool ret = v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, rindex, normalized) == V3_OK; - v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, index); + v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex); return ret; } @@ -1395,10 +1441,99 @@ private: // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from DSP side) -v3_funknown** dpf_plugin_view_create(v3_edit_controller** controller, v3_component_handler** handler, - void* instancePointer, double sampleRate); +v3_funknown** dpf_plugin_view_create(v3_connection_point** connection, void* instancePointer, double sampleRate); #endif +// -------------------------------------------------------------------------------------------------------------------- +// dpf_connection_point + +struct v3_connection_point_cpp : v3_funknown { + v3_connection_point point; +}; + +struct dpf_connection_point : v3_connection_point_cpp { + ScopedPointer& vst3; + const bool controller; // component otherwise + bool connected; + + dpf_connection_point(const bool isEditCtrl, ScopedPointer& v) + : vst3(v), + controller(isEditCtrl), + connected(false) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_connection_point_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_connection_point + + point.connect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + { + d_stdout("dpf_connection_point::connect => %p %p", self, other); + dpf_connection_point* const point = *(dpf_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + + const bool connected = point->connected; + DISTRHO_SAFE_ASSERT_RETURN(! connected, V3_INVALID_ARG); + + point->connected = true; + return V3_OK; + }; + + point.disconnect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + { + d_stdout("dpf_connection_point::disconnect => %p %p", self, other); + dpf_connection_point* const point = *(dpf_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + + const bool connected = point->connected; + DISTRHO_SAFE_ASSERT_RETURN(connected, V3_INVALID_ARG); + + point->connected = false; + return V3_OK; + }; + + point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result + { + d_stdout("dpf_connection_point::notify => %p %p", self, message); + dpf_connection_point* const point = *(dpf_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + + PluginVst3* const vst3 = point->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + + return vst3->notify(message); + }; + } +}; + // -------------------------------------------------------------------------------------------------------------------- // dpf_edit_controller @@ -1408,6 +1543,7 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { + ScopedPointer connection; ScopedPointer& vst3; bool initialized; // cached values @@ -1418,7 +1554,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { initialized(false), handler(nullptr) { - static const uint8_t* kSupportedInterfaces[] = { + static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, v3_edit_controller_iid }; @@ -1432,7 +1568,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* interface_iid : kSupportedInterfaces) + for (const uint8_t* interface_iid : kSupportedInterfacesBase) { if (v3_tuid_match(interface_iid, iid)) { @@ -1441,6 +1577,17 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } } + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NO_INTERFACE); + + if (v3_tuid_match(v3_connection_point_iid, iid)) + { + if (controller->connection == nullptr) + controller->connection = new dpf_connection_point(true, controller->vst3); + *iface = &controller->connection; + return V3_OK; + } + return V3_NO_INTERFACE; }; @@ -1527,7 +1674,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_count = []V3_API(void* self) -> int32_t { - d_stdout("dpf_edit_controller::get_parameter_count => %p", self); + // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1539,7 +1686,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result { - d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); + // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -1651,14 +1798,39 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); + // if there is no connection yet we can still manage ourselves, but only if component is initialized + DISTRHO_SAFE_ASSERT_RETURN((controller->connection != nullptr && controller->connection->connected) || + controller->vst3 != nullptr, nullptr); + + if (controller->connection != nullptr) + { + // if there is a connection point alreay it needs to be connected + DISTRHO_SAFE_ASSERT_RETURN(controller->connection->connected, nullptr); + } + else + { + // no connection point, let's do it ourselves (assume local usage) + controller->connection = new dpf_connection_point(true, controller->vst3); + controller->connection->connected = true; + } + + void* instancePointer; + double sampleRate; + + if (PluginVst3* const vst3 = controller->vst3) + { + instancePointer = vst3->getInstancePointer(); + sampleRate = vst3->getSampleRate(); + } + else + { + instancePointer = nullptr; + sampleRate = 44100.0; + } #if DISTRHO_PLUGIN_HAS_UI - return (v3_plugin_view**)dpf_plugin_view_create((v3_edit_controller**)self, - controller->handler, - vst3->getInstancePointer(), - vst3->getSampleRate()); + return (v3_plugin_view**)dpf_plugin_view_create((v3_connection_point**)&controller->connection, + instancePointer, sampleRate); #else return nullptr; #endif @@ -1990,6 +2162,7 @@ struct dpf_component : v3_component_cpp { std::atomic refcounter; ScopedPointer* self; ScopedPointer processor; + ScopedPointer connection; ScopedPointer controller; // ScopedPointer stream; ScopedPointer vst3; @@ -2032,6 +2205,14 @@ struct dpf_component : v3_component_cpp { return V3_OK; } + if (v3_tuid_match(v3_connection_point_iid, iid)) + { + if (component->connection == nullptr) + component->connection = new dpf_connection_point(false, component->vst3); + *iface = &component->connection; + return V3_OK; + } + if (v3_tuid_match(v3_edit_controller_iid, iid)) { if (component->controller == nullptr) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 8ec5b1ec..677501c7 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -39,6 +39,7 @@ // #endif #include "travesty/edit_controller.h" +#include "travesty/message.h" #include "travesty/view.h" /* TODO items: @@ -69,6 +70,21 @@ static constexpr const setStateFunc setStateCallback = nullptr; const char* tuid2str(const v3_tuid iid); +// -------------------------------------------------------------------------------------------------------------------- +// dpf_message (needed by the UI class, implementation comes later) + +struct v3_message_cpp : v3_funknown { + v3_message msg; +}; + +// NOTE value type must be POD +template +struct dpf_message : v3_message_cpp { + dpf_message(ScopedPointer* self, const char* id, T value); + struct PrivateData; + PrivateData* const pData; +}; + // -------------------------------------------------------------------------------------------------------------------- /** @@ -77,14 +93,17 @@ const char* tuid2str(const v3_tuid iid); * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things. * The UI is created during the "attach" view event, and destroyed during "removed". * + * Note that DPF VST3 implementation works over the connection point interface, + * rather than using edit controller directly. + * This allows the UI to be running remotely from the DSP. + * * The low-level VST3 stuff comes after. */ class UIVst3 : public Thread { public: UIVst3(v3_plugin_view** const view, - v3_edit_controller** const controller, - v3_component_handler** const handler, + v3_connection_point** const connection, const intptr_t winId, const float scaleFactor, const double sampleRate, @@ -100,8 +119,7 @@ public: instancePointer, scaleFactor), fView(view), - fController(controller), - fHandler(handler), + fConnection(connection), fFrame(nullptr), fScaleFactor(scaleFactor) { @@ -211,21 +229,23 @@ private: // VST3 stuff v3_plugin_view** const fView; - v3_edit_controller** const fController; - v3_component_handler** const fHandler; + v3_connection_point** const fConnection; v3_plugin_frame** fFrame; // Temporary data float fScaleFactor; - void editParameter(const uint32_t rindex, const bool started) const + // ---------------------------------------------------------------------------------------------------------------- + // DPF callbacks + + void editParameter(const uint32_t /*rindex*/, const bool /*started*/) const { - DISTRHO_SAFE_ASSERT_RETURN(fHandler != nullptr,); +// DISTRHO_SAFE_ASSERT_RETURN(fHandler != nullptr,); - if (started) - v3_cpp_obj(fHandler)->begin_edit(fHandler, rindex); - else - v3_cpp_obj(fHandler)->end_edit(fHandler, rindex); +// if (started) +// v3_cpp_obj(fHandler)->begin_edit(fHandler, rindex); +// else +// v3_cpp_obj(fHandler)->end_edit(fHandler, rindex); } static void editParameterCallback(void* ptr, uint32_t rindex, bool started) @@ -235,11 +255,17 @@ private: void setParameterValue(const uint32_t rindex, const float realValue) { - DISTRHO_SAFE_ASSERT_RETURN(fController != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(fHandler != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + d_stdout("setParameterValue %p %u %f", this, rindex, realValue); + + struct IndexAndValue { + uint32_t index; + float value; + }; + ScopedPointer>* const messageptr = new ScopedPointer>; + *messageptr = new dpf_message(messageptr, "parameter-value-set", { rindex, realValue }); - const double value = v3_cpp_obj(fController)->plain_parameter_to_normalised(fController, rindex, realValue); - v3_cpp_obj(fHandler)->perform_edit(fHandler, rindex, value); + v3_cpp_obj(fConnection)->notify(fConnection, (v3_message**)messageptr); } static void setParameterCallback(void* ptr, uint32_t rindex, float value) @@ -253,17 +279,17 @@ private: DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); d_stdout("from UI setSize %u %u | %p %p", width, height, fView, fFrame); -#ifdef DISTRHO_OS_MAC - const double scaleFactor = fUI.getScaleFactor(); - width /= scaleFactor; - height /= scaleFactor; -#endif - - v3_view_rect rect; - std::memset(&rect, 0, sizeof(rect)); - rect.right = width; - rect.bottom = height; - v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); +// #ifdef DISTRHO_OS_MAC +// const double scaleFactor = fUI.getScaleFactor(); +// width /= scaleFactor; +// height /= scaleFactor; +// #endif +// +// v3_view_rect rect; +// std::memset(&rect, 0, sizeof(rect)); +// rect.right = width; +// rect.bottom = height; +// v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); } static void setSizeCallback(void* ptr, uint width, uint height) @@ -307,6 +333,118 @@ private: * VST3 low-level pointer thingies follow, proceed with care. */ +// -------------------------------------------------------------------------------------------------------------------- +// dpf_message (implementation only, declaration already done as needed by UI class) + +template +struct dpf_message::PrivateData { + std::atomic refcounter; + ScopedPointer* self; + String id; + const T value; + + PrivateData(ScopedPointer* const s, const char* const id2, T value2) + : refcounter(1), + self(s), + id(id2), + value(value2) {} +}; + +static V3_API v3_result dpf_message__query_interface(void* self, const v3_tuid iid, void** iface) +{ + d_stdout("dpf_message::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_message_iid + }; + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; +} + +template +static V3_API uint32_t dpf_message__ref(void* const self) +{ + d_stdout("dpf_message::ref => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); + + return ++message->pData->refcounter; +} + +template +static V3_API uint32_t dpf_message__unref(void* const self) +{ + d_stdout("dpf_message::unref => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); + + if (const int refcounter = --message->pData->refcounter) + return refcounter; + + dpf_message* const messageptr = message->pData->self->release(); + delete message->pData; + delete messageptr; + delete (dpf_message**)self; + return 0; +} + +template +static V3_API const char* dpf_message__get_message_id(void* const self) +{ + d_stdout("dpf_message::get_message_id => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + + return message->pData->id; +} + +template +static V3_API void dpf_message__set_message_id(void* const self, const char* const id) +{ + d_stdout("dpf_message::set_message_id => %p %s", self, id); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + message->pData->id = id; +} + +template +static V3_API v3_attribute_list* dpf_message__get_attributes(void* const self) +{ + d_stdout("dpf_message::get_attributes => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + + // TODO + return nullptr; +} + +template +dpf_message::dpf_message(ScopedPointer* const self, const char* const id, const T value) + : pData(new PrivateData(self, id, value)) +{ + query_interface = dpf_message__query_interface; + ref = dpf_message__ref; + unref = dpf_message__unref; + msg.get_message_id = dpf_message__get_message_id; + msg.set_message_id = dpf_message__set_message_id; + msg.get_attributes = dpf_message__get_attributes; +} + +template struct dpf_message; + // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale @@ -390,21 +528,18 @@ struct dpf_plugin_view : v3_plugin_view_cpp { ScopedPointer scale; ScopedPointer uivst3; // cached values - v3_edit_controller** const controller; - v3_component_handler** const handler; + v3_connection_point** const connection; void* const instancePointer; double sampleRate; v3_plugin_frame** frame = nullptr; dpf_plugin_view(ScopedPointer* selfptr, - v3_edit_controller** const controllerptr, - v3_component_handler** const handlerptr, + v3_connection_point** const connectionptr, void* const instance, const double sr) : refcounter(1), self(selfptr), - controller(controllerptr), - handler(handlerptr), + connection(connectionptr), instancePointer(instance), sampleRate(sr) { @@ -508,9 +643,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - view->uivst3 = new UIVst3((v3_plugin_view**)self, - view->controller, - view->handler, + view->uivst3 = new UIVst3((v3_plugin_view**)view->self, + view->connection, (uintptr_t)parent, scaleFactor, view->sampleRate, @@ -654,14 +788,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from plugin side) -v3_funknown** dpf_plugin_view_create(v3_edit_controller** controller, v3_component_handler** handler, - void* instancePointer, double sampleRate); +v3_funknown** dpf_plugin_view_create(v3_connection_point** connection, void* instancePointer, double sampleRate); -v3_funknown** dpf_plugin_view_create(v3_edit_controller** const controller, v3_component_handler** const handler, +v3_funknown** dpf_plugin_view_create(v3_connection_point** const connection, void* const instancePointer, const double sampleRate) { ScopedPointer* const viewptr = new ScopedPointer; - *viewptr = new dpf_plugin_view(viewptr, controller, handler, instancePointer, sampleRate); + *viewptr = new dpf_plugin_view(viewptr, connection, instancePointer, sampleRate); return (v3_funknown**)viewptr; } diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 5db8993d..a5facf02 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -26,6 +26,19 @@ * component handler */ +enum { + V3_RESTART_RELOAD_COMPONENT = 1 << 0, + V3_RESTART_IO_CHANGED = 1 << 1, + V3_RESTART_PARAM_VALUES_CHANGED = 1 << 2, + V3_RESTART_LATENCY_CHANGED = 1 << 3, + V3_RESTART_PARAM_TITLES_CHANGED = 1 << 4, + V3_RESTART_MIDI_CC_ASSIGNMENT_CHANGED = 1 << 5, + V3_RESTART_NOTE_EXPRESSION_CHANGED = 1 << 6, + V3_RESTART_IO_TITLES_CHANGED = 1 << 7, + V3_RESTART_PREFETCHABLE_SUPPORT_CHANGED = 1 << 8, + V3_RESTART_ROUTING_INFO_CHANGED = 1 << 9 +}; + struct v3_component_handler { struct v3_funknown; diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h new file mode 100644 index 00000000..70e42b18 --- /dev/null +++ b/distrho/src/travesty/message.h @@ -0,0 +1,55 @@ +/* + * travesty, pure C VST3-compatible interface + * Copyright (C) 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. + */ + +#pragma once + +#include "base.h" + +#include "align_push.h" + +/** + * message + */ + +struct v3_attribute_list; + +struct v3_message { + struct v3_funknown; + + V3_API const char* (*get_message_id)(void* self); + V3_API void (*set_message_id)(void* self, const char* id); + V3_API v3_attribute_list* (*get_attributes)(void* self); +}; + +static constexpr const v3_tuid v3_message_iid = + V3_ID(0x936F033B, 0xC6C047DB, 0xBB0882F8, 0x13C1E613); + +/** + * connection point + */ + +struct v3_connection_point { + struct v3_funknown; + + V3_API v3_result (*connect)(void* self, struct v3_connection_point** other); + V3_API v3_result (*disconnect)(void* self, struct v3_connection_point** other); + V3_API v3_result (*notify)(void* self, struct v3_message** message); +}; + +static constexpr const v3_tuid v3_connection_point_iid = + V3_ID(0x70A4156F, 0x6E6E4026, 0x989148BF, 0xAA60D8D1); + +#include "align_pop.h" From 5df663bba62b16afb346e0559675d109ecbb0f56 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 01:24:26 +0100 Subject: [PATCH 091/504] VST3: use connection point to send parameter grab and changes Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 52 ++++- distrho/src/DistrhoUIVST3.cpp | 372 +++++++++++++++++++++--------- distrho/src/travesty/base.h | 20 ++ distrho/src/travesty/message.h | 24 +- 4 files changed, 347 insertions(+), 121 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 08a25a1f..81479aeb 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -113,6 +113,8 @@ const char* tuid2str(const v3_tuid iid) if (v3_tuid_match(iid, v3_audio_processor_iid)) return "{v3_audio_processor}"; + if (v3_tuid_match(iid, v3_attribute_list_iid)) + return "{v3_attribute_list_iid}"; if (v3_tuid_match(iid, v3_bstream_iid)) return "{v3_bstream}"; if (v3_tuid_match(iid, v3_component_iid)) @@ -1275,10 +1277,52 @@ public: v3_result notify(v3_message** const message) { - d_stdout("notify received %p -> %s", message, v3_cpp_obj(message)->get_message_id(message)); - // TESTING, ensure host gets the message - requestParameterValueChange(2, 1.0f); - return V3_OK; + const char* const msgid = v3_cpp_obj(message)->get_message_id(message); + DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG); + + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); + + if (std::strcmp(msgid, "parameter-edit") == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); + + int64_t rindex; + int64_t started; + v3_result res; + + res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_int(attrs, "started", &started); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + rindex -= fParameterOffset; + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + + return started != 0 ? v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex) + : v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex); + } + + if (std::strcmp(msgid, "parameter-set") == 0) + { + int64_t rindex; + double value; + v3_result res; + + res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + rindex -= fParameterOffset; + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + + return requestParameterValueChange(rindex, value) ? V3_OK : V3_INTERNAL_ERR; + } + + return V3_NOT_IMPLEMENTED; } // ---------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 677501c7..a6142142 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -42,6 +42,10 @@ #include "travesty/message.h" #include "travesty/view.h" +#include +#include +#include + /* TODO items: * - disable UI if non-embed UI build * - parameter change listener @@ -71,19 +75,9 @@ static constexpr const setStateFunc setStateCallback = nullptr; const char* tuid2str(const v3_tuid iid); // -------------------------------------------------------------------------------------------------------------------- -// dpf_message (needed by the UI class, implementation comes later) - -struct v3_message_cpp : v3_funknown { - v3_message msg; -}; +// create message object (needed by the UI class, implementation comes later) -// NOTE value type must be POD -template -struct dpf_message : v3_message_cpp { - dpf_message(ScopedPointer* self, const char* id, T value); - struct PrivateData; - PrivateData* const pData; -}; +static v3_message** dpf_message_create(const char* id); // -------------------------------------------------------------------------------------------------------------------- @@ -238,14 +232,21 @@ private: // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks - void editParameter(const uint32_t /*rindex*/, const bool /*started*/) const + void editParameter(const uint32_t rindex, const bool started) const { -// DISTRHO_SAFE_ASSERT_RETURN(fHandler != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + + v3_message** const message = dpf_message_create("parameter-edit"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); -// if (started) -// v3_cpp_obj(fHandler)->begin_edit(fHandler, rindex); -// else -// v3_cpp_obj(fHandler)->end_edit(fHandler, rindex); + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + + v3_cpp_obj(attrs)->set_int(attrs, "rindex", rindex); + v3_cpp_obj(attrs)->set_int(attrs, "started", started ? 1 : 0); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); } static void editParameterCallback(void* ptr, uint32_t rindex, bool started) @@ -256,16 +257,18 @@ private: void setParameterValue(const uint32_t rindex, const float realValue) { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - d_stdout("setParameterValue %p %u %f", this, rindex, realValue); - struct IndexAndValue { - uint32_t index; - float value; - }; - ScopedPointer>* const messageptr = new ScopedPointer>; - *messageptr = new dpf_message(messageptr, "parameter-value-set", { rindex, realValue }); + v3_message** const message = dpf_message_create("parameter-set"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + + v3_cpp_obj(attrs)->set_int(attrs, "rindex", rindex); + v3_cpp_obj(attrs)->set_float(attrs, "value", realValue); + v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj(fConnection)->notify(fConnection, (v3_message**)messageptr); + v3_cpp_obj_unref(message); } static void setParameterCallback(void* ptr, uint32_t rindex, float value) @@ -334,117 +337,258 @@ private: */ // -------------------------------------------------------------------------------------------------------------------- -// dpf_message (implementation only, declaration already done as needed by UI class) +// dpf_attribute_list + +struct dpf_attribute_value { + char type; + union { + int64_t integer; + double v_float; + int16_t* string; + struct { + void* ptr; + uint32_t size; + } binary; + }; +}; + +struct v3_attribute_list_cpp : v3_funknown { + v3_attribute_list attr; +}; + +struct dpf_attribute_list : v3_attribute_list_cpp { + std::map attrs; + + dpf_attribute_list() + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_attribute_list_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_attribute_list::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_attribute_list + + attr.set_int = []V3_API(void* self, const char* id, int64_t value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.integer = value; + return V3_OK; + }; + + attr.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + *value = attrval.integer; + return V3_OK; + }; + + attr.set_float = []V3_API(void* self, const char* id, double value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.v_float = value; + return V3_OK; + }; + + attr.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + *value = attrval.v_float; + return V3_OK; + }; + + attr.set_string = []V3_API(void* self, const char* /*id*/, const int16_t* /*string*/) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + return V3_NOT_IMPLEMENTED; + }; + + attr.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + return V3_NOT_IMPLEMENTED; + }; + + attr.set_binary = []V3_API(void* self, const char* /*id*/, const void* /*data*/, uint32_t /*size*/) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + return V3_NOT_IMPLEMENTED; + }; + + attr.get_binary = []V3_API(void* self, const char* id, const void** /*data*/, uint32_t* /*size*/) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + return V3_NOT_IMPLEMENTED; + }; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_message -template -struct dpf_message::PrivateData { +struct v3_message_cpp : v3_funknown { + v3_message msg; +}; + +struct dpf_message : v3_message_cpp { std::atomic refcounter; ScopedPointer* self; + ScopedPointer attrlist; String id; - const T value; - PrivateData(ScopedPointer* const s, const char* const id2, T value2) - : refcounter(1), - self(s), - id(id2), - value(value2) {} -}; + dpf_message(ScopedPointer* const s, const char* const id2) + : self(s), + id(id2) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_message_iid + }; -static V3_API v3_result dpf_message__query_interface(void* self, const v3_tuid iid, void** iface) -{ - d_stdout("dpf_message::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_message_iid - }; + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + ref = []V3_API(void* const self) -> uint32_t { - *iface = self; - return V3_OK; - } - } + d_stdout("dpf_message::ref => %p", self); + dpf_message** const messageptr = static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); + dpf_message* const message = *messageptr; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - return V3_NO_INTERFACE; -} + return ++message->refcounter; + }; -template -static V3_API uint32_t dpf_message__ref(void* const self) -{ - d_stdout("dpf_message::ref => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); + unref = []V3_API(void* const self) -> uint32_t + { + d_stdout("dpf_message::unref => %p", self); + dpf_message** const messageptr = static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); + dpf_message* const message = *messageptr; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - return ++message->pData->refcounter; -} + if (const int refcounter = --message->refcounter) + return refcounter; -template -static V3_API uint32_t dpf_message__unref(void* const self) -{ - d_stdout("dpf_message::unref => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - - if (const int refcounter = --message->pData->refcounter) - return refcounter; - - dpf_message* const messageptr = message->pData->self->release(); - delete message->pData; - delete messageptr; - delete (dpf_message**)self; - return 0; -} + *message->self = nullptr; + delete messageptr; + return 0; + }; -template -static V3_API const char* dpf_message__get_message_id(void* const self) -{ - d_stdout("dpf_message::get_message_id => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + msg.get_message_id = []V3_API(void* const self) -> const char* + { + d_stdout("dpf_message::get_message_id => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); - return message->pData->id; -} + return message->id; + }; -template -static V3_API void dpf_message__set_message_id(void* const self, const char* const id) -{ - d_stdout("dpf_message::set_message_id => %p %s", self, id); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + msg.set_message_id = []V3_API(void* const self, const char* const id) -> void + { + d_stdout("dpf_message::set_message_id => %p %s", self, id); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - message->pData->id = id; -} + message->id = id; + }; -template -static V3_API v3_attribute_list* dpf_message__get_attributes(void* const self) -{ - d_stdout("dpf_message::get_attributes => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + msg.get_attributes = []V3_API(void* const self) -> v3_attribute_list** + { + d_stdout("dpf_message::get_attributes => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); - // TODO - return nullptr; -} + if (message->attrlist == nullptr) + message->attrlist = new dpf_attribute_list(); -template -dpf_message::dpf_message(ScopedPointer* const self, const char* const id, const T value) - : pData(new PrivateData(self, id, value)) + return (v3_attribute_list**)&message->attrlist; + }; + } +}; + +static v3_message** dpf_message_create(const char* const id) { - query_interface = dpf_message__query_interface; - ref = dpf_message__ref; - unref = dpf_message__unref; - msg.get_message_id = dpf_message__get_message_id; - msg.set_message_id = dpf_message__set_message_id; - msg.get_attributes = dpf_message__get_attributes; + ScopedPointer* const messageptr = new ScopedPointer; + *messageptr = new dpf_message(messageptr, id); + return static_cast(static_cast(messageptr)); } -template struct dpf_message; - // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 9c54f215..5b015318 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -25,6 +25,15 @@ */ #ifdef __cplusplus +/** + * cast object into its proper C++ type. + * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. + * we can use this as a little helper for keeping both C and C++ compatiblity. + * specialized templated calls are defined where required + * (that is, object inherits from something other than `v3_funknown`) + * + * example usage: `v3_cpp_obj(obj)->method(obj, args...);` + */ template static inline constexpr T* v3_cpp_obj(T** obj) { @@ -168,3 +177,14 @@ struct v3_plugin_base { static constexpr const v3_tuid v3_plugin_base_iid = V3_ID(0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625); + +#ifdef __cplusplus +/** + * helper C++ function to manually call unref on an object. + */ +template static inline +uint32_t v3_cpp_obj_unref(T** obj) +{ + return static_cast(static_cast(*obj))->unref(obj); +} +#endif diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h index 70e42b18..2d022bbd 100644 --- a/distrho/src/travesty/message.h +++ b/distrho/src/travesty/message.h @@ -21,17 +21,35 @@ #include "align_push.h" /** - * message + * attribute list */ -struct v3_attribute_list; +struct v3_attribute_list { + struct v3_funknown; + + V3_API v3_result (*set_int)(void* self, const char* id, int64_t value); + V3_API v3_result (*get_int)(void* self, const char* id, int64_t* value); + V3_API v3_result (*set_float)(void* self, const char* id, double value); + V3_API v3_result (*get_float)(void* self, const char* id, double* value); + V3_API v3_result (*set_string)(void* self, const char* id, const int16_t* string); + V3_API v3_result (*get_string)(void* self, const char* id, int16_t* string, uint32_t size); + V3_API v3_result (*set_binary)(void* self, const char* id, const void* data, uint32_t size); + V3_API v3_result (*get_binary)(void* self, const char* id, const void** data, uint32_t* size); +}; + +static constexpr const v3_tuid v3_attribute_list_iid = + V3_ID(0x1E5F0AEB, 0xCC7F4533, 0xA2544011, 0x38AD5EE4); + +/** + * message + */ struct v3_message { struct v3_funknown; V3_API const char* (*get_message_id)(void* self); V3_API void (*set_message_id)(void* self, const char* id); - V3_API v3_attribute_list* (*get_attributes)(void* self); + V3_API v3_attribute_list** (*get_attributes)(void* self); }; static constexpr const v3_tuid v3_message_iid = From 4ef519266dbf3ab8d58db68367f9c0f0076a9d9c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 02:42:49 +0100 Subject: [PATCH 092/504] VST3: Implement UI->DSP state messaging Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 40 ++++++- distrho/src/DistrhoUIVST3.cpp | 190 ++++++++++++++++++++++++++++-- distrho/src/travesty/base.h | 21 +++- examples/States/Makefile | 11 +- 4 files changed, 235 insertions(+), 27 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 81479aeb..b97b8a72 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -182,11 +182,11 @@ const char* tuid2str(const v3_tuid iid) // -------------------------------------------------------------------------------------------------------------------- -static void strncpy(char* const dst, const char* const src, const size_t size) +static void strncpy(char* const dst, const char* const src, const size_t length) { - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); - if (const size_t len = std::min(std::strlen(src), size-1U)) + if (const size_t len = std::min(std::strlen(src), length-1U)) { std::memcpy(dst, src, len); dst[len] = '\0'; @@ -197,11 +197,11 @@ static void strncpy(char* const dst, const char* const src, const size_t size) } } -static void strncpy_utf16(int16_t* const dst, const char* const src, const size_t size) +void strncpy_utf16(int16_t* const dst, const char* const src, const size_t length) { - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); - if (const size_t len = std::min(std::strlen(src), size-1U)) + if (const size_t len = std::min(std::strlen(src), length-1U)) { for (size_t i=0; iget_binary(attrs, "key", (const void**)&key16, &keySize); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_binary(attrs, "value", (const void**)&value16, &valueSize); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + // do cheap inline conversion + char* const key = (char*)key16; + char* const value = (char*)value16; + + for (uint32_t i=0; isetStateFromUI(key, value); + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + + v3_message** const message = dpf_message_create("state-set"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + + v3_attribute_list_utf8** utf8attrs = nullptr; + DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrs, v3_attribute_list_utf8_iid, &utf8attrs) == V3_OK,); + DISTRHO_SAFE_ASSERT_RETURN(utf8attrs != nullptr,); + + v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "key", key); + v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "value", value); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); } +#if DISTRHO_PLUGIN_WANT_STATE static void setStateCallback(void* ptr, const char* key, const char* value) { ((UIVst3*)ptr)->setState(key, value); @@ -337,10 +368,9 @@ private: */ // -------------------------------------------------------------------------------------------------------------------- -// dpf_attribute_list struct dpf_attribute_value { - char type; + char type; // one of: i, f, s, b union { int64_t integer; double v_float; @@ -352,16 +382,113 @@ struct dpf_attribute_value { }; }; +static void dpf_attribute_list_free(std::map& attrs) +{ + for (auto& it : attrs) + { + dpf_attribute_value& v(it.second); + + switch (v.type) + { + case 's': + case 'b': + std::free(v.binary.ptr); + break; + } + } +} + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_attribute_list_dpf (the custom utf8 variant) + +struct v3_attribute_list_utf8_cpp : v3_funknown { + v3_attribute_list_utf8 attr; +}; + +struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp { + std::map& attrs; + + dpf_attribute_list_dpf(std::map& a) + : attrs(a) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_attribute_list_utf8_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_attribute_list_dpf::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_attribute_list_utf8 + + attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result + { + dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + const uint32_t size = std::strlen(string) + 1; + int16_t* const copy = (int16_t*)std::malloc(sizeof(int16_t) * size); + DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); + + DISTRHO_NAMESPACE::strncpy_utf16(copy, string, size); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 's'; + attrval.binary.ptr = copy; + attrval.binary.size = sizeof(int16_t) * size; + return V3_OK; + }; + + attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result + { + dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + return V3_NOT_IMPLEMENTED; + }; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_attribute_list + struct v3_attribute_list_cpp : v3_funknown { v3_attribute_list attr; }; struct dpf_attribute_list : v3_attribute_list_cpp { + ScopedPointer attrdpf; std::map attrs; dpf_attribute_list() { - static const uint8_t* kSupportedInterfaces[] = { + static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, v3_attribute_list_iid }; @@ -375,7 +502,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - for (const uint8_t* interface_iid : kSupportedInterfaces) + for (const uint8_t* interface_iid : kSupportedInterfacesBase) { if (v3_tuid_match(interface_iid, iid)) { @@ -384,6 +511,17 @@ struct dpf_attribute_list : v3_attribute_list_cpp { } } + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NO_INTERFACE); + + if (v3_tuid_match(v3_attribute_list_utf8_iid, iid)) + { + if (attr->attrdpf == nullptr) + attr->attrdpf = new dpf_attribute_list_dpf(attr->attrs); + *iface = &attr->attrdpf; + return V3_OK; + } + return V3_NO_INTERFACE; }; @@ -400,6 +538,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 'i'; attrval.integer = value; return V3_OK; }; @@ -413,6 +552,8 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_INVALID_ARG; const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'i', V3_INVALID_ARG); + *value = attrval.integer; return V3_OK; }; @@ -423,6 +564,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 'f'; attrval.v_float = value; return V3_OK; }; @@ -436,15 +578,21 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_INVALID_ARG; const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'f', V3_INVALID_ARG); + *value = attrval.v_float; return V3_OK; }; - attr.set_string = []V3_API(void* self, const char* /*id*/, const int16_t* /*string*/) -> v3_result + attr.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 's'; + attrval.binary.ptr = nullptr; + attrval.binary.size = 0; return V3_NOT_IMPLEMENTED; }; @@ -456,18 +604,30 @@ struct dpf_attribute_list : v3_attribute_list_cpp { if (attr->attrs.find(id) == attr->attrs.end()) return V3_INVALID_ARG; + const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's', V3_INVALID_ARG); + return V3_NOT_IMPLEMENTED; }; - attr.set_binary = []V3_API(void* self, const char* /*id*/, const void* /*data*/, uint32_t /*size*/) -> v3_result + attr.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + void* const copy = std::malloc(size); + DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); + + std::memcpy(copy, data, size); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 'b'; + attrval.binary.ptr = copy; + attrval.binary.size = size; return V3_NOT_IMPLEMENTED; }; - attr.get_binary = []V3_API(void* self, const char* id, const void** /*data*/, uint32_t* /*size*/) -> v3_result + attr.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -475,7 +635,12 @@ struct dpf_attribute_list : v3_attribute_list_cpp { if (attr->attrs.find(id) == attr->attrs.end()) return V3_INVALID_ARG; - return V3_NOT_IMPLEMENTED; + const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's' || attrval.type == 'b', V3_INVALID_ARG); + + *data = attrval.binary.ptr; + *size = attrval.binary.size; + return V3_OK; }; } }; @@ -545,6 +710,9 @@ struct dpf_message : v3_message_cpp { if (const int refcounter = --message->refcounter) return refcounter; + if (message->attrlist != nullptr) + dpf_attribute_list_free(message->attrlist->attrs); + *message->self = nullptr; delete messageptr; return 0; diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 5b015318..a6b9dea0 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -25,6 +25,7 @@ */ #ifdef __cplusplus + /** * cast object into its proper C++ type. * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. @@ -43,10 +44,13 @@ constexpr T* v3_cpp_obj(T** obj) */ return static_cast(static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3)); } + #else + # ifndef constexpr # define constexpr # endif + #endif /** @@ -179,12 +183,27 @@ static constexpr const v3_tuid v3_plugin_base_iid = V3_ID(0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625); #ifdef __cplusplus + /** - * helper C++ function to manually call unref on an object. + * helper C++ functions to manually call v3_funknown methods on an object. */ + +template static inline +v3_result v3_cpp_obj_query_interface(T** obj, const v3_tuid iid, M*** obj2) +{ + return static_cast(static_cast(*obj))->query_interface(obj, iid, (void**)obj2); +} + +template static inline +uint32_t v3_cpp_obj_ref(T** obj) +{ + return static_cast(static_cast(*obj))->ref(obj); +} + template static inline uint32_t v3_cpp_obj_unref(T** obj) { return static_cast(static_cast(*obj))->unref(obj); } + #endif diff --git a/examples/States/Makefile b/examples/States/Makefile index 0a0bfb77..b4190c6e 100644 --- a/examples/States/Makefile +++ b/examples/States/Makefile @@ -26,17 +26,10 @@ include ../../Makefile.plugins.mk # -------------------------------------------------------------- # Enable all possible plugin types -ifeq ($(HAVE_OPENGL),true) TARGETS += jack -endif # HAVE_OPENGL - -ifeq ($(HAVE_OPENGL),true) TARGETS += lv2_sep -else -TARGETS += lv2_dsp -endif - -TARGETS += vst +TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) From 8e25227168fb528aaedf84efb0e875f9c248fbe8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 02:58:56 +0100 Subject: [PATCH 093/504] VST3: Implement UI->DSP send note Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 45 +++++++++++++++++++++++++++++-- distrho/src/DistrhoUIVST3.cpp | 27 +++++++++++++------ examples/SendNote/Makefile | 7 +++-- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index b97b8a72..26656d3c 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -17,6 +17,10 @@ #include "DistrhoPluginInternal.hpp" #include "../extra/ScopedPointer.hpp" +#if DISTRHO_PLUGIN_HAS_UI +# include "../extra/RingBuffer.hpp" +#endif + #include "travesty/audio_processor.h" #include "travesty/component.h" #include "travesty/edit_controller.h" @@ -976,6 +980,23 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t midiEventCount = 0; +# if DISTRHO_PLUGIN_HAS_UI + while (fNotesRingBuffer.isDataAvailableForReading()) + { + uint8_t midiData[3]; + if (! fNotesRingBuffer.readCustomData(midiData, 3)) + break; + + MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); + midiEvent.frame = 0; + midiEvent.size = 3; + std::memcpy(midiEvent.data, midiData, 3); + + if (midiEventCount == kMaxMidiEvents) + break; + } +# endif + if (v3_event_list** const eventptr = data->input_events) { v3_event event; @@ -1283,6 +1304,23 @@ public: v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI + if (std::strcmp(msgid, "midi") == 0) + { + uint8_t* data; + uint32_t size; + v3_result res; + + res = v3_cpp_obj(attrs)->get_binary(attrs, "data", (const void**)&data, &size); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + // known maximum size + DISTRHO_SAFE_ASSERT_UINT_RETURN(size == 3, size, V3_INTERNAL_ERR); + + return fNotesRingBuffer.writeCustomData(data, size) && fNotesRingBuffer.commitWrite() ? V3_OK : V3_NOMEM; + } +#endif + if (std::strcmp(msgid, "parameter-edit") == 0) { DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); @@ -1368,6 +1406,9 @@ private: float* fParameterValues; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT MidiEvent fMidiEvents[kMaxMidiEvents]; +# if DISTRHO_PLUGIN_HAS_UI + SmallStackRingBuffer fNotesRingBuffer; +# endif #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT v3_event_list** fHostEventOutputHandle; @@ -1870,13 +1911,14 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); +#if DISTRHO_PLUGIN_HAS_UI // if there is no connection yet we can still manage ourselves, but only if component is initialized DISTRHO_SAFE_ASSERT_RETURN((controller->connection != nullptr && controller->connection->connected) || controller->vst3 != nullptr, nullptr); if (controller->connection != nullptr) { - // if there is a connection point alreay it needs to be connected + // if there is a connection point, it needs to be connected DISTRHO_SAFE_ASSERT_RETURN(controller->connection->connected, nullptr); } else @@ -1900,7 +1942,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { sampleRate = 44100.0; } -#if DISTRHO_PLUGIN_HAS_UI return (v3_plugin_view**)dpf_plugin_view_create((v3_connection_point**)&controller->connection, instancePointer, sampleRate); #else diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index dfdc0863..f9d21088 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -316,14 +316,25 @@ private: } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void sendNote(const uint8_t /*channel*/, const uint8_t /*note*/, const uint8_t /*velocity*/) + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) { -// uint8_t midiData[3]; -// midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; -// midiData[1] = note; -// midiData[2] = velocity; -// fNotesRingBuffer.writeCustomData(midiData, 3); -// fNotesRingBuffer.commitWrite(); + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + + v3_message** const message = dpf_message_create("midi"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + + uint8_t midiData[3]; + midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; + midiData[1] = note; + midiData[2] = velocity; + + v3_cpp_obj(attrs)->set_binary(attrs, "data", midiData, sizeof(midiData)); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); } static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) @@ -332,6 +343,7 @@ private: } #endif +#if DISTRHO_PLUGIN_WANT_STATE void setState(const char* const key, const char* const value) { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); @@ -353,7 +365,6 @@ private: v3_cpp_obj_unref(message); } -#if DISTRHO_PLUGIN_WANT_STATE static void setStateCallback(void* ptr, const char* key, const char* value) { ((UIVst3*)ptr)->setState(key, value); diff --git a/examples/SendNote/Makefile b/examples/SendNote/Makefile index ca2144db..c317ccb7 100644 --- a/examples/SendNote/Makefile +++ b/examples/SendNote/Makefile @@ -29,12 +29,11 @@ include ../../Makefile.plugins.mk ifeq ($(HAVE_OPENGL),true) TARGETS += jack -TARGETS += lv2_sep -else -TARGETS += lv2_dsp endif -TARGETS += vst +TARGETS += lv2_sep +TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) From 4c59baa3140863353827038b1e5966d7c1166880 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 15:36:11 +0100 Subject: [PATCH 094/504] Experiments with VST3 full data passing Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 266 ++++++++++++++++++++++------- distrho/src/DistrhoUIVST3.cpp | 275 ++++++++++++++++++++++-------- distrho/src/travesty/message.h | 20 +++ distrho/src/travesty/view.h | 24 +++ 4 files changed, 449 insertions(+), 136 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 26656d3c..836a11a5 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -256,6 +256,11 @@ static constexpr void (*const snprintf_u32)(char*, uint32_t, size_t) = snprintf_ static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snprintf_utf16_t; static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t; +// -------------------------------------------------------------------------------------------------------------------- +// TESTING + +v3_message** dpf_message_create(const char* id); + // -------------------------------------------------------------------------------------------------------------------- /** @@ -287,6 +292,7 @@ public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), + fConnectionToUI(nullptr), fParameterOffset(fPlugin.getParameterOffset()), fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), fParameterValues(nullptr) @@ -394,6 +400,11 @@ public: return fPlugin.getSampleRate(); } + void setConnectionToUI(v3_connection_point** const point) noexcept + { + fConnectionToUI = point; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_component interface calls @@ -1257,6 +1268,9 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); + // TESTING remove this + sendParameterChangeTest(rindex, value); + #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) { @@ -1294,7 +1308,21 @@ public: } // ---------------------------------------------------------------------------------------------------------------- - // dpf_connection_point + // dpf_dsp_connection_point + + void connect(v3_connection_point** const other) + { + fConnectionToUI = other; + + d_stdout("---------------------------------------------------------- will send plugin state now"); + } + + void disconnect() + { + fConnectionToUI = nullptr; + + d_stdout("---------------------------------------------------------- ui conn now null"); + } v3_result notify(v3_message** const message) { @@ -1399,6 +1427,7 @@ private: // VST3 stuff v3_component_handler** fComponentHandler; + v3_connection_point** fConnectionToUI; // Temporary data const uint32_t fParameterOffset; @@ -1422,7 +1451,7 @@ private: #endif // ---------------------------------------------------------------------------------------------------------------- - // functions called from the plugin side, RT no block + // helper functions called during process, cannot block void updateParameterOutputsAndTriggers() { @@ -1459,6 +1488,28 @@ private: } } + // ---------------------------------------------------------------------------------------------------------------- + // helper functions called during message passing, can block + + void sendParameterChangeTest(const v3_param_id rindex, const double value) + { + d_stdout("will send message now"); + DISTRHO_SAFE_ASSERT_RETURN(fConnectionToUI != nullptr,); + + v3_message** const message = dpf_message_create("parameter-set"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); + v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex); + v3_cpp_obj(attrlist)->set_float(attrlist, "value", value); + v3_cpp_obj(fConnectionToUI)->notify(fConnectionToUI, message); + + v3_cpp_obj_unref(message); + } + // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks @@ -1554,25 +1605,33 @@ private: // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from DSP side) -v3_funknown** dpf_plugin_view_create(v3_connection_point** connection, void* instancePointer, double sampleRate); +v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate); #endif // -------------------------------------------------------------------------------------------------------------------- -// dpf_connection_point +// dpf_dsp_connection_point + +enum ConnectionPointType { + kConnectionPointComponent, + kConnectionControllerToComponent, + kConnectionControllerToView +}; struct v3_connection_point_cpp : v3_funknown { v3_connection_point point; }; -struct dpf_connection_point : v3_connection_point_cpp { +struct dpf_dsp_connection_point : v3_connection_point_cpp { ScopedPointer& vst3; - const bool controller; // component otherwise - bool connected; + const ConnectionPointType type; + v3_connection_point** other; + v3_connection_point** bridge; // when type is controller this points to ctrl<->view point - dpf_connection_point(const bool isEditCtrl, ScopedPointer& v) + dpf_dsp_connection_point(const ConnectionPointType t, ScopedPointer& v) : vst3(v), - controller(isEditCtrl), - connected(false) + type(t), + other(nullptr), + bridge(nullptr) { static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, @@ -1584,7 +1643,7 @@ struct dpf_connection_point : v3_connection_point_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); + d_stdout("dpf_dsp_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -1609,40 +1668,121 @@ struct dpf_connection_point : v3_connection_point_cpp { point.connect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result { - d_stdout("dpf_connection_point::connect => %p %p", self, other); - dpf_connection_point* const point = *(dpf_connection_point**)self; + d_stdout("dpf_dsp_connection_point::connect => %p %p", self, other); + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT(point->bridge == nullptr); + + point->other = other; - const bool connected = point->connected; - DISTRHO_SAFE_ASSERT_RETURN(! connected, V3_INVALID_ARG); + if (point->type == kConnectionPointComponent) + if (PluginVst3* const vst3 = point->vst3) + vst3->connect((v3_connection_point**)self); - point->connected = true; return V3_OK; }; point.disconnect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result { - d_stdout("dpf_connection_point::disconnect => %p %p", self, other); - dpf_connection_point* const point = *(dpf_connection_point**)self; + d_stdout("dpf_dsp_connection_point::disconnect => %p %p", self, other); + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + + point->other = nullptr; + point->bridge = nullptr; - const bool connected = point->connected; - DISTRHO_SAFE_ASSERT_RETURN(connected, V3_INVALID_ARG); + if (point->type == kConnectionPointComponent) + if (PluginVst3* const vst3 = point->vst3) + vst3->disconnect(); - point->connected = false; return V3_OK; }; point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result { - d_stdout("dpf_connection_point::notify => %p %p", self, message); - dpf_connection_point* const point = *(dpf_connection_point**)self; + d_stdout("dpf_dsp_connection_point::notify => %p %p", self, message); + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); PluginVst3* const vst3 = point->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - return vst3->notify(message); + v3_connection_point** const other = point->other; + DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALISED); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG); + + int64_t target = 0; + const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target); + DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res); + DISTRHO_SAFE_ASSERT_RETURN(target != 0, V3_INTERNAL_ERR); + + switch (point->type) + { + // pass message from component (aka plugin) to controller + case kConnectionPointComponent: + { + d_stdout("dpf_dsp_connection_point::notify kConnectionPointComponent"); + + switch (target) + { + case 1: + // message is from view to controller to component + return vst3->notify(message); + case 2: + // message is from component to controller to view + return v3_cpp_obj(other)->notify(other, message); + } + break; + } + + // pass message from controller to component (aka plugin) + case kConnectionControllerToComponent: + { + d_stdout("dpf_dsp_connection_point::notify kConnectionControllerToComponent"); + + switch (target) + { + case 1: + // message is from view to controller to component + return v3_cpp_obj(other)->notify(other, message); + case 2: + { + // message is from component to controller to view + v3_connection_point** const bridge = point->bridge; + DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED); + return v3_cpp_obj(bridge)->notify(bridge, message); + } + } + break; + } + + // pass message from from view (aka ui) to controller + case kConnectionControllerToView: + { + d_stdout("dpf_dsp_connection_point::notify kConnectionControllerToView"); + + switch (target) + { + case 1: + { + // message is from view to controller to component + v3_connection_point** const bridge = point->bridge; + DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED); + return v3_cpp_obj(bridge)->notify(bridge, message); + } + case 2: + // message is from component to controller to view + return v3_cpp_obj(other)->notify(other, message); + } + break; + } + } + + return V3_INTERNAL_ERR; }; } }; @@ -1656,15 +1796,18 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { - ScopedPointer connection; + ScopedPointer connectionComp; // kConnectionControllerToComponent + ScopedPointer connectionView; // kConnectionControllerToView ScopedPointer& vst3; bool initialized; // cached values + v3_connection_point** connectionToUI; v3_component_handler** handler; dpf_edit_controller(ScopedPointer& v) : vst3(v), initialized(false), + connectionToUI(nullptr), handler(nullptr) { static const uint8_t* kSupportedInterfacesBase[] = { @@ -1695,9 +1838,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (v3_tuid_match(v3_connection_point_iid, iid)) { - if (controller->connection == nullptr) - controller->connection = new dpf_connection_point(true, controller->vst3); - *iface = &controller->connection; + if (controller->connectionComp == nullptr) + controller->connectionComp = new dpf_dsp_connection_point(kConnectionControllerToComponent, + controller->vst3); + *iface = &controller->connectionComp; return V3_OK; } @@ -1734,6 +1878,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); controller->initialized = false; + controller->connectionToUI = nullptr; return V3_OK; }; @@ -1880,14 +2025,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); -// if (dpf_plugin_view* const view = controller->view) -// { -// if (UIVst3* const uivst3 = view->uivst3) -// { -// uivst3->setParameterValueFromDSP(index, vst3->normalisedParameterToPlain(index, normalised)); -// } -// } - return vst3->setParameterNormalized(index, normalised); }; @@ -1911,39 +2048,37 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); -#if DISTRHO_PLUGIN_HAS_UI - // if there is no connection yet we can still manage ourselves, but only if component is initialized - DISTRHO_SAFE_ASSERT_RETURN((controller->connection != nullptr && controller->connection->connected) || - controller->vst3 != nullptr, nullptr); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); - if (controller->connection != nullptr) - { - // if there is a connection point, it needs to be connected - DISTRHO_SAFE_ASSERT_RETURN(controller->connection->connected, nullptr); - } - else - { - // no connection point, let's do it ourselves (assume local usage) - controller->connection = new dpf_connection_point(true, controller->vst3); - controller->connection->connected = true; - } +#if 1 // DISTRHO_PLUGIN_HAS_UI + // we require a component connection + DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp != nullptr, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp->other != nullptr, nullptr); - void* instancePointer; - double sampleRate; + v3_plugin_view** const view = dpf_plugin_view_create(vst3->getInstancePointer(), + vst3->getSampleRate()); + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr); - if (PluginVst3* const vst3 = controller->vst3) - { - instancePointer = vst3->getInstancePointer(); - sampleRate = vst3->getSampleRate(); - } - else + v3_connection_point** connection = nullptr; + if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &connection) == V3_OK) { - instancePointer = nullptr; - sampleRate = 44100.0; + d_stdout("view connection query ok %p", connection); + + v3_connection_point** const bridge = (v3_connection_point**)&controller->connectionComp; + controller->connectionView = new dpf_dsp_connection_point(kConnectionControllerToView, + controller->vst3); + + v3_connection_point** const other = (v3_connection_point**)&controller->connectionView; + v3_cpp_obj(connection)->connect(connection, other); + v3_cpp_obj(other)->connect(other, connection); + + controller->connectionComp->bridge = other; + controller->connectionView->bridge = bridge; + } - return (v3_plugin_view**)dpf_plugin_view_create((v3_connection_point**)&controller->connection, - instancePointer, sampleRate); + return view; #else return nullptr; #endif @@ -2275,7 +2410,7 @@ struct dpf_component : v3_component_cpp { std::atomic refcounter; ScopedPointer* self; ScopedPointer processor; - ScopedPointer connection; + ScopedPointer connection; // kConnectionPointComponent ScopedPointer controller; // ScopedPointer stream; ScopedPointer vst3; @@ -2321,7 +2456,8 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(v3_connection_point_iid, iid)) { if (component->connection == nullptr) - component->connection = new dpf_connection_point(false, component->vst3); + component->connection = new dpf_dsp_connection_point(kConnectionPointComponent, + component->vst3); *iface = &component->connection; return V3_OK; } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index f9d21088..7a2d0b2e 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -49,11 +49,9 @@ /* TODO items: * - disable UI if non-embed UI build * - parameter change listener - * - parameter change sender * - program change listener * - program change sender * - state change listener - * - state change sender * - sample rate change listener * - call component handler restart with params-changed flag when setting program? */ @@ -78,12 +76,13 @@ void strncpy_utf16(int16_t* dst, const char* src, size_t length); // -------------------------------------------------------------------------------------------------------------------- // create message object (needed by the UI class, implementation comes later) -static v3_message** dpf_message_create(const char* id); +v3_message** dpf_message_create(const char* id); // -------------------------------------------------------------------------------------------------------------------- // custom attribute list struct, used for sending utf8 strings struct v3_attribute_list_utf8 { + struct v3_funknown; V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); }; @@ -112,7 +111,6 @@ class UIVst3 : public Thread { public: UIVst3(v3_plugin_view** const view, - v3_connection_point** const connection, const intptr_t winId, const float scaleFactor, const double sampleRate, @@ -128,7 +126,7 @@ public: instancePointer, scaleFactor), fView(view), - fConnection(connection), + fConnection(nullptr), fFrame(nullptr), fScaleFactor(scaleFactor) { @@ -231,6 +229,53 @@ public: } // ---------------------------------------------------------------------------------------------------------------- + // dpf_ui_connection_point + + void connect(v3_connection_point** const point) noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr,); + + fConnection = point; + } + + void disconnect() noexcept + { + fConnection = nullptr; + } + + v3_result notify(v3_message** const message) + { + const char* const msgid = v3_cpp_obj(message)->get_message_id(message); + DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG); + + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); + + if (std::strcmp(msgid, "parameter-set") == 0) + { + int64_t rindex; + double value; + v3_result res; + + res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + rindex -= fUI.getParameterOffset(); + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + + fUI.parameterChanged(rindex, value); + return V3_OK; + } + + d_stdout("UIVst3 received unknown msg '%s'", msgid); + + return V3_NOT_IMPLEMENTED; + } + + // ---------------------------------------------------------------------------------------------------------------- private: // Plugin UI @@ -238,7 +283,7 @@ private: // VST3 stuff v3_plugin_view** const fView; - v3_connection_point** const fConnection; + v3_connection_point** fConnection; v3_plugin_frame** fFrame; // Temporary data @@ -254,11 +299,12 @@ private: v3_message** const message = dpf_message_create("parameter-edit"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); - DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); - v3_cpp_obj(attrs)->set_int(attrs, "rindex", rindex); - v3_cpp_obj(attrs)->set_int(attrs, "started", started ? 1 : 0); + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex); + v3_cpp_obj(attrlist)->set_int(attrlist, "started", started ? 1 : 0); v3_cpp_obj(fConnection)->notify(fConnection, message); v3_cpp_obj_unref(message); @@ -276,11 +322,12 @@ private: v3_message** const message = dpf_message_create("parameter-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); - DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); - v3_cpp_obj(attrs)->set_int(attrs, "rindex", rindex); - v3_cpp_obj(attrs)->set_float(attrs, "value", realValue); + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex); + v3_cpp_obj(attrlist)->set_float(attrlist, "value", realValue); v3_cpp_obj(fConnection)->notify(fConnection, message); v3_cpp_obj_unref(message); @@ -323,15 +370,16 @@ private: v3_message** const message = dpf_message_create("midi"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); - DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); uint8_t midiData[3]; midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; midiData[1] = note; midiData[2] = velocity; - v3_cpp_obj(attrs)->set_binary(attrs, "data", midiData, sizeof(midiData)); + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(attrlist)->set_binary(attrlist, "data", midiData, sizeof(midiData)); v3_cpp_obj(fConnection)->notify(fConnection, message); v3_cpp_obj_unref(message); @@ -351,15 +399,16 @@ private: v3_message** const message = dpf_message_create("state-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); - DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); - v3_attribute_list_utf8** utf8attrs = nullptr; - DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrs, v3_attribute_list_utf8_iid, &utf8attrs) == V3_OK,); - DISTRHO_SAFE_ASSERT_RETURN(utf8attrs != nullptr,); + v3_attribute_list_utf8** utf8attrlist = nullptr; + DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrlist, v3_attribute_list_utf8_iid, &utf8attrs) == V3_OK,); + DISTRHO_SAFE_ASSERT_RETURN(utf8attrlist != nullptr,); - v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "key", key); - v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "value", value); + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "key", key); + v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "value", value); v3_cpp_obj(fConnection)->notify(fConnection, message); v3_cpp_obj_unref(message); @@ -410,16 +459,16 @@ static void dpf_attribute_list_free(std::map& } // -------------------------------------------------------------------------------------------------------------------- -// dpf_attribute_list_dpf (the custom utf8 variant) +// dpf_attribute_list_utf8 (the custom variant) struct v3_attribute_list_utf8_cpp : v3_funknown { v3_attribute_list_utf8 attr; }; -struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp { +struct dpf_attribute_list_utf8 : v3_attribute_list_utf8_cpp { std::map& attrs; - dpf_attribute_list_dpf(std::map& a) + dpf_attribute_list_utf8(std::map& a) : attrs(a) { static const uint8_t* kSupportedInterfaces[] = { @@ -432,7 +481,7 @@ struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_attribute_list_dpf::query_interface => %p %s %p", self, tuid2str(iid), iface); + d_stdout("dpf_attribute_list_utf8::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); @@ -457,7 +506,7 @@ struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp { attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result { - dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self; + dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); const uint32_t size = std::strlen(string) + 1; @@ -475,7 +524,7 @@ struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp { attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result { - dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self; + dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); if (attr->attrs.find(id) == attr->attrs.end()) @@ -489,12 +538,8 @@ struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_attribute_list -struct v3_attribute_list_cpp : v3_funknown { - v3_attribute_list attr; -}; - struct dpf_attribute_list : v3_attribute_list_cpp { - ScopedPointer attrdpf; + ScopedPointer attrutf8; std::map attrs; dpf_attribute_list() @@ -527,9 +572,9 @@ struct dpf_attribute_list : v3_attribute_list_cpp { if (v3_tuid_match(v3_attribute_list_utf8_iid, iid)) { - if (attr->attrdpf == nullptr) - attr->attrdpf = new dpf_attribute_list_dpf(attr->attrs); - *iface = &attr->attrdpf; + if (attr->attrutf8 == nullptr) + attr->attrutf8 = new dpf_attribute_list_utf8(attr->attrs); + *iface = &attr->attrutf8; return V3_OK; } @@ -543,7 +588,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_attribute_list - attr.set_int = []V3_API(void* self, const char* id, int64_t value) -> v3_result + attrlist.set_int = []V3_API(void* self, const char* id, int64_t value) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -554,7 +599,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_OK; }; - attr.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result + attrlist.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -569,7 +614,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_OK; }; - attr.set_float = []V3_API(void* self, const char* id, double value) -> v3_result + attrlist.set_float = []V3_API(void* self, const char* id, double value) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -580,7 +625,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_OK; }; - attr.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result + attrlist.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -595,7 +640,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_OK; }; - attr.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result + attrlist.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -607,7 +652,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_NOT_IMPLEMENTED; }; - attr.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result + attrlist.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -621,7 +666,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_NOT_IMPLEMENTED; }; - attr.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result + attrlist.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -638,7 +683,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { return V3_NOT_IMPLEMENTED; }; - attr.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result + attrlist.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result { dpf_attribute_list* const attr = *(dpf_attribute_list**)self; DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); @@ -659,10 +704,6 @@ struct dpf_attribute_list : v3_attribute_list_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_message -struct v3_message_cpp : v3_funknown { - v3_message msg; -}; - struct dpf_message : v3_message_cpp { std::atomic refcounter; ScopedPointer* self; @@ -761,7 +802,7 @@ struct dpf_message : v3_message_cpp { } }; -static v3_message** dpf_message_create(const char* const id) +v3_message** dpf_message_create(const char* const id) { ScopedPointer* const messageptr = new ScopedPointer; *messageptr = new dpf_message(messageptr, id); @@ -771,10 +812,6 @@ static v3_message** dpf_message_create(const char* const id) // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale -struct v3_plugin_view_content_scale_cpp : v3_funknown { - v3_plugin_view_content_scale scale; -}; - struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { ScopedPointer& uivst3; // cached values @@ -839,32 +876,113 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { }; // -------------------------------------------------------------------------------------------------------------------- -// dpf_plugin_view +// dpf_ui_connection_point + +struct dpf_ui_connection_point : v3_connection_point_cpp { + ScopedPointer& uivst3; + v3_connection_point** other; + + dpf_ui_connection_point(ScopedPointer& v) + : uivst3(v), + other(nullptr) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_connection_point_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_ui_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_connection_point + + point.connect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + { + d_stdout("dpf_ui_connection_point::connect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); + + point->other = other; + + if (UIVst3* const uivst3 = point->uivst3) + uivst3->connect(other); + + return V3_OK; + }; + + point.disconnect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + { + d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + + point->other = nullptr; + + if (UIVst3* const uivst3 = point->uivst3) + uivst3->disconnect(); + + return V3_OK; + }; + + point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result + { + d_stdout("dpf_ui_connection_point::notify => %p %p", self, message); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + + UIVst3* const uivst3 = point->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); -struct v3_plugin_view_cpp : v3_funknown { - v3_plugin_view view; + return uivst3->notify(message); + }; + } }; +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view + struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic refcounter; ScopedPointer* self; ScopedPointer scale; + ScopedPointer connection; ScopedPointer uivst3; // cached values - v3_connection_point** const connection; void* const instancePointer; double sampleRate; v3_plugin_frame** frame = nullptr; - dpf_plugin_view(ScopedPointer* selfptr, - v3_connection_point** const connectionptr, - void* const instance, - const double sr) + dpf_plugin_view(ScopedPointer* selfptr, void* const instance, const double sr) : refcounter(1), self(selfptr), - connection(connectionptr), instancePointer(instance), - sampleRate(sr) + sampleRate(sr), + frame(nullptr) { static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, @@ -902,6 +1020,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); + if (v3_tuid_match(v3_connection_point_iid, iid)) + { + if (view->connection == nullptr) + view->connection = new dpf_ui_connection_point(view->uivst3); + *iface = &view->connection; + return V3_OK; + } + if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) { if (view->scale == nullptr) @@ -931,6 +1057,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (const int refcounter = --view->refcounter) return refcounter; + if (view->connection != nullptr && view->connection->other) + v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, + (v3_connection_point**)&view->connection); + *view->self = nullptr; delete (dpf_plugin_view**)self; return 0; @@ -967,11 +1097,15 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)view->self, - view->connection, (uintptr_t)parent, scaleFactor, view->sampleRate, view->instancePointer); + + if (dpf_ui_connection_point* const point = view->connection) + if (point->other != nullptr) + view->uivst3->connect(point->other); + view->uivst3->setFrame(view->frame); return V3_OK; } @@ -1111,14 +1245,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from plugin side) -v3_funknown** dpf_plugin_view_create(v3_connection_point** connection, void* instancePointer, double sampleRate); +v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate); -v3_funknown** dpf_plugin_view_create(v3_connection_point** const connection, - void* const instancePointer, const double sampleRate) +v3_plugin_view** dpf_plugin_view_create(void* const instancePointer, const double sampleRate) { ScopedPointer* const viewptr = new ScopedPointer; - *viewptr = new dpf_plugin_view(viewptr, connection, instancePointer, sampleRate); - return (v3_funknown**)viewptr; + *viewptr = new dpf_plugin_view(viewptr, instancePointer, sampleRate); + return (v3_plugin_view**)static_cast(viewptr); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h index 2d022bbd..6f66adff 100644 --- a/distrho/src/travesty/message.h +++ b/distrho/src/travesty/message.h @@ -70,4 +70,24 @@ struct v3_connection_point { static constexpr const v3_tuid v3_connection_point_iid = V3_ID(0x70A4156F, 0x6E6E4026, 0x989148BF, 0xAA60D8D1); +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_attribute_list_cpp : v3_funknown { + v3_attribute_list attrlist; +}; + +struct v3_message_cpp : v3_funknown { + v3_message msg; +}; + +struct v3_connection_point_cpp : v3_funknown { + v3_connection_point point; +}; + +#endif + #include "align_pop.h" diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index 80dde1c6..264b830e 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -106,3 +106,27 @@ struct v3_plugin_view_parameter_finder { static constexpr const v3_tuid v3_plugin_view_parameter_finder_iid = V3_ID(0x0F618302, 0x215D4587, 0xA512073C, 0x77B9D383); + +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_plugin_view_cpp : v3_funknown { + v3_plugin_view view; +}; + +struct v3_plugin_frame_cpp : v3_funknown { + v3_plugin_frame frame; +}; + +struct v3_plugin_view_content_scale_cpp : v3_funknown { + v3_plugin_view_content_scale scale; +}; + +struct v3_plugin_view_parameter_finder_cpp : v3_funknown { + v3_plugin_view_parameter_finder finder; +}; + +#endif From 1ae94bf2216b02e99513f8a3ff7d39efe8152a9d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 16:19:57 +0100 Subject: [PATCH 095/504] VST3 cleanup, add state-set UI message Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 146 ++++-------------------------- distrho/src/DistrhoUIVST3.cpp | 79 ++++++++++++---- 2 files changed, 77 insertions(+), 148 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 836a11a5..3d1acf4c 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -17,6 +17,16 @@ #include "DistrhoPluginInternal.hpp" #include "../extra/ScopedPointer.hpp" +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + #if DISTRHO_PLUGIN_HAS_UI # include "../extra/RingBuffer.hpp" #endif @@ -35,8 +45,10 @@ * - parameter enumeration as lists * - hide parameter outputs? * - hide program parameter? - * - state support + * - save and restore state * - save and restore current program + * - proper send of parameter, program and state changes to UI + * - send current state to UI on request * - midi cc parameter mapping * - full MIDI1 encode and decode * - decode version number (0x102030 -> 1.2.3) @@ -795,33 +807,6 @@ public: return V3_OK; } - // ---------------------------------------------------------------------------------------------------------------- - // v3_bstream interface calls (for state support) - - v3_result read(void* /*buffer*/, int32_t /*num_bytes*/, int32_t* /*bytes_read*/) - { - // TODO - return V3_NOT_IMPLEMENTED; - } - - v3_result write(void* /*buffer*/, int32_t /*num_bytes*/, int32_t* /*bytes_written*/) - { - // TODO - return V3_NOT_IMPLEMENTED; - } - - v3_result seek(int64_t /*pos*/, int32_t /*seek_mode*/, int64_t* /*result*/) - { - // TODO - return V3_NOT_IMPLEMENTED; - } - - v3_result tell(int64_t* /*pos*/) - { - // TODO - return V3_NOT_IMPLEMENTED; - } - // ---------------------------------------------------------------------------------------------------------------- // v3_audio_processor interface calls @@ -1269,7 +1254,7 @@ public: DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); // TESTING remove this - sendParameterChangeTest(rindex, value); + sendParameterChangeToUI(rindex, value); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) @@ -1491,7 +1476,7 @@ private: // ---------------------------------------------------------------------------------------------------------------- // helper functions called during message passing, can block - void sendParameterChangeTest(const v3_param_id rindex, const double value) + void sendParameterChangeToUI(const v3_param_id rindex, const double value) { d_stdout("will send message now"); DISTRHO_SAFE_ASSERT_RETURN(fConnectionToUI != nullptr,); @@ -2051,7 +2036,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); -#if 1 // DISTRHO_PLUGIN_HAS_UI +#if DISTRHO_PLUGIN_HAS_UI // we require a component connection DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp != nullptr, nullptr); DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp->other != nullptr, nullptr); @@ -2300,104 +2285,6 @@ struct dpf_audio_processor : v3_audio_processor_cpp { } }; -#if 0 -// -------------------------------------------------------------------------------------------------------------------- -// dpf_state_stream - -struct v3_bstream_cpp : v3_funknown { - v3_bstream stream; -}; - -struct dpf_state_stream : v3_bstream_cpp { - ScopedPointer& vst3; - - dpf_state_stream(ScopedPointer& v) - : vst3(v) - { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_bstream_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_factory::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_bstream - - stream.read = []V3_API(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_read) -> v3_result - { - d_stdout("dpf_state_stream::read => %p %p %i %p", self, buffer, num_bytes, bytes_read); - dpf_state_stream* const stream = *(dpf_state_stream**)self; - DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); - - PluginVst3* const vst3 = stream->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - - return V3_NOT_IMPLEMENTED; - }; - - stream.write = []V3_API(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_written) -> v3_result - { - d_stdout("dpf_state_stream::write => %p %p %i %p", self, buffer, num_bytes, bytes_written); - dpf_state_stream* const stream = *(dpf_state_stream**)self; - DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); - - PluginVst3* const vst3 = stream->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - - return V3_NOT_IMPLEMENTED; - }; - - stream.seek = []V3_API(void* self, int64_t pos, int32_t seek_mode, int64_t* result) -> v3_result - { - d_stdout("dpf_state_stream::seek => %p %lu %i %p", self, pos, seek_mode, result); - dpf_state_stream* const stream = *(dpf_state_stream**)self; - DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); - - PluginVst3* const vst3 = stream->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - - return V3_NOT_IMPLEMENTED; - }; - - stream.tell = []V3_API(void* self, int64_t* pos) -> v3_result - { - d_stdout("dpf_state_stream::tell => %p %p", self, pos); - dpf_state_stream* const stream = *(dpf_state_stream**)self; - DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr, V3_NOT_INITIALISED); - - PluginVst3* const vst3 = stream->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); - - return V3_NOT_IMPLEMENTED; - }; - } -}; -#endif - // -------------------------------------------------------------------------------------------------------------------- // dpf_component @@ -2412,7 +2299,6 @@ struct dpf_component : v3_component_cpp { ScopedPointer processor; ScopedPointer connection; // kConnectionPointComponent ScopedPointer controller; - // ScopedPointer stream; ScopedPointer vst3; dpf_component(ScopedPointer* const s) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 7a2d0b2e..3a714c16 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -14,12 +14,7 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "DistrhoUIInternal.hpp" - -#include - -// TESTING awful idea dont reuse -#include "../extra/Thread.hpp" +#include "DistrhoPluginChecks.h" #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI # undef DISTRHO_PLUGIN_HAS_UI @@ -34,9 +29,16 @@ // #undef DISTRHO_PLUGIN_HAS_UI // #define DISTRHO_PLUGIN_HAS_UI 1 -// #if DISTRHO_PLUGIN_HAS_UI -# include "DistrhoUIInternal.hpp" -// #endif +#if DISTRHO_PLUGIN_HAS_UI + +// -------------------------------------------------------------------------------------------------------------------- + +#include "DistrhoUIInternal.hpp" + +#include + +// TESTING awful idea dont reuse +#include "../extra/Thread.hpp" #include "travesty/edit_controller.h" #include "travesty/message.h" @@ -48,12 +50,9 @@ /* TODO items: * - disable UI if non-embed UI build - * - parameter change listener - * - program change listener - * - program change sender - * - state change listener * - sample rate change listener - * - call component handler restart with params-changed flag when setting program? + * - proper send of parameter, program and state changes to UI + * - request current state of UI when created */ START_NAMESPACE_DISTRHO @@ -122,7 +121,7 @@ public: sendNoteCallback, setSizeCallback, nullptr, // TODO file request - nullptr, + nullptr, // bundlePath instancePointer, scaleFactor), fView(view), @@ -263,12 +262,52 @@ public: res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - rindex -= fUI.getParameterOffset(); - DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (rindex == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INTERNAL_ERR); + + fUI.programLoaded(static_cast(value + 0.5)); + } + else +#endif + { + rindex -= fUI.getParameterOffset(); + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + + fUI.parameterChanged(static_cast(rindex), value); + } + + return V3_OK; + } + +#if DISTRHO_PLUGIN_WANT_STATE + if (std::strcmp(msgid, "state-set") == 0) + { + int16_t* key16; + int16_t* value16; + uint32_t keySize, valueSize; + v3_result res; + + res = v3_cpp_obj(attrs)->get_binary(attrs, "key", (const void**)&key16, &keySize); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_binary(attrs, "value", (const void**)&value16, &valueSize); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + // do cheap inline conversion + char* const key = (char*)key16; + char* const value = (char*)value16; - fUI.parameterChanged(rindex, value); + for (uint32_t i=0; i Date: Sun, 26 Sep 2021 16:29:34 +0100 Subject: [PATCH 096/504] Move VST3 message related code to plugin side Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 401 +++++++++++++++++++++++++++++- distrho/src/DistrhoUIVST3.cpp | 396 ----------------------------- 2 files changed, 400 insertions(+), 397 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 3d1acf4c..8c2cee86 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -38,6 +38,8 @@ #include "travesty/message.h" #include +#include +#include #include /* TODO items: @@ -269,7 +271,22 @@ static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snp static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t; // -------------------------------------------------------------------------------------------------------------------- -// TESTING +// custom attribute list struct, used for sending utf8 strings + +struct v3_attribute_list_utf8 { + struct v3_funknown; + V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); + V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); +}; + +static constexpr const v3_tuid v3_attribute_list_utf8_iid = + V3_ID(d_cconst('D','P','F',' '), + d_cconst('c','l','a','s'), + d_cconst('a','t','t','r'), + d_cconst('u','t','f','8')); + +// -------------------------------------------------------------------------------------------------------------------- +// create message object (implementation comes later) v3_message** dpf_message_create(const char* id); @@ -1593,6 +1610,388 @@ private: v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate); #endif +// -------------------------------------------------------------------------------------------------------------------- + +struct dpf_attribute_value { + char type; // one of: i, f, s, b + union { + int64_t integer; + double v_float; + int16_t* string; + struct { + void* ptr; + uint32_t size; + } binary; + }; +}; + +static void dpf_attribute_list_free(std::map& attrs) +{ + for (auto& it : attrs) + { + dpf_attribute_value& v(it.second); + + switch (v.type) + { + case 's': + case 'b': + std::free(v.binary.ptr); + break; + } + } +} + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_attribute_list_utf8 (the custom variant) + +struct v3_attribute_list_utf8_cpp : v3_funknown { + v3_attribute_list_utf8 attr; +}; + +struct dpf_attribute_list_utf8 : v3_attribute_list_utf8_cpp { + std::map& attrs; + + dpf_attribute_list_utf8(std::map& a) + : attrs(a) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_attribute_list_utf8_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_attribute_list_utf8::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_attribute_list_utf8 + + attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result + { + dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + const uint32_t size = std::strlen(string) + 1; + int16_t* const copy = (int16_t*)std::malloc(sizeof(int16_t) * size); + DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); + + DISTRHO_NAMESPACE::strncpy_utf16(copy, string, size); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 's'; + attrval.binary.ptr = copy; + attrval.binary.size = sizeof(int16_t) * size; + return V3_OK; + }; + + attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result + { + dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + return V3_NOT_IMPLEMENTED; + }; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_attribute_list + +struct dpf_attribute_list : v3_attribute_list_cpp { + ScopedPointer attrutf8; + std::map attrs; + + dpf_attribute_list() + { + static const uint8_t* kSupportedInterfacesBase[] = { + v3_funknown_iid, + v3_attribute_list_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_attribute_list::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfacesBase) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NO_INTERFACE); + + if (v3_tuid_match(v3_attribute_list_utf8_iid, iid)) + { + if (attr->attrutf8 == nullptr) + attr->attrutf8 = new dpf_attribute_list_utf8(attr->attrs); + *iface = &attr->attrutf8; + return V3_OK; + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_attribute_list + + attrlist.set_int = []V3_API(void* self, const char* id, int64_t value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 'i'; + attrval.integer = value; + return V3_OK; + }; + + attrlist.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'i', V3_INVALID_ARG); + + *value = attrval.integer; + return V3_OK; + }; + + attrlist.set_float = []V3_API(void* self, const char* id, double value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 'f'; + attrval.v_float = value; + return V3_OK; + }; + + attrlist.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'f', V3_INVALID_ARG); + + *value = attrval.v_float; + return V3_OK; + }; + + attrlist.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 's'; + attrval.binary.ptr = nullptr; + attrval.binary.size = 0; + return V3_NOT_IMPLEMENTED; + }; + + attrlist.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's', V3_INVALID_ARG); + + return V3_NOT_IMPLEMENTED; + }; + + attrlist.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + void* const copy = std::malloc(size); + DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); + + std::memcpy(copy, data, size); + + dpf_attribute_value& attrval(attr->attrs[id]); + attrval.type = 'b'; + attrval.binary.ptr = copy; + attrval.binary.size = size; + return V3_NOT_IMPLEMENTED; + }; + + attrlist.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's' || attrval.type == 'b', V3_INVALID_ARG); + + *data = attrval.binary.ptr; + *size = attrval.binary.size; + return V3_OK; + }; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_message + +struct dpf_message : v3_message_cpp { + std::atomic refcounter; + ScopedPointer* self; + ScopedPointer attrlist; + String id; + + dpf_message(ScopedPointer* const s, const char* const id2) + : self(s), + id(id2) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_message_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + ref = []V3_API(void* const self) -> uint32_t + { + d_stdout("dpf_message::ref => %p", self); + dpf_message** const messageptr = static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); + dpf_message* const message = *messageptr; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); + + return ++message->refcounter; + }; + + unref = []V3_API(void* const self) -> uint32_t + { + d_stdout("dpf_message::unref => %p", self); + dpf_message** const messageptr = static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); + dpf_message* const message = *messageptr; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); + + if (const int refcounter = --message->refcounter) + return refcounter; + + if (message->attrlist != nullptr) + dpf_attribute_list_free(message->attrlist->attrs); + + *message->self = nullptr; + delete messageptr; + return 0; + }; + + msg.get_message_id = []V3_API(void* const self) -> const char* + { + d_stdout("dpf_message::get_message_id => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + + return message->id; + }; + + msg.set_message_id = []V3_API(void* const self, const char* const id) -> void + { + d_stdout("dpf_message::set_message_id => %p %s", self, id); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + message->id = id; + }; + + msg.get_attributes = []V3_API(void* const self) -> v3_attribute_list** + { + d_stdout("dpf_message::get_attributes => %p", self); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + + if (message->attrlist == nullptr) + message->attrlist = new dpf_attribute_list(); + + return (v3_attribute_list**)&message->attrlist; + }; + } +}; + +v3_message** dpf_message_create(const char* const id) +{ + ScopedPointer* const messageptr = new ScopedPointer; + *messageptr = new dpf_message(messageptr, id); + return static_cast(static_cast(messageptr)); +} + // -------------------------------------------------------------------------------------------------------------------- // dpf_dsp_connection_point diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 3a714c16..06dd58cc 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -44,10 +44,6 @@ #include "travesty/message.h" #include "travesty/view.h" -#include -#include -#include - /* TODO items: * - disable UI if non-embed UI build * - sample rate change listener @@ -71,21 +67,11 @@ static constexpr const setStateFunc setStateCallback = nullptr; const char* tuid2str(const v3_tuid iid); void strncpy_utf16(int16_t* dst, const char* src, size_t length); - -// -------------------------------------------------------------------------------------------------------------------- -// create message object (needed by the UI class, implementation comes later) - v3_message** dpf_message_create(const char* id); // -------------------------------------------------------------------------------------------------------------------- // custom attribute list struct, used for sending utf8 strings -struct v3_attribute_list_utf8 { - struct v3_funknown; - V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); - V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); -}; - static constexpr const v3_tuid v3_attribute_list_utf8_iid = V3_ID(d_cconst('D','P','F',' '), d_cconst('c','l','a','s'), @@ -466,388 +452,6 @@ private: * VST3 low-level pointer thingies follow, proceed with care. */ -// -------------------------------------------------------------------------------------------------------------------- - -struct dpf_attribute_value { - char type; // one of: i, f, s, b - union { - int64_t integer; - double v_float; - int16_t* string; - struct { - void* ptr; - uint32_t size; - } binary; - }; -}; - -static void dpf_attribute_list_free(std::map& attrs) -{ - for (auto& it : attrs) - { - dpf_attribute_value& v(it.second); - - switch (v.type) - { - case 's': - case 'b': - std::free(v.binary.ptr); - break; - } - } -} - -// -------------------------------------------------------------------------------------------------------------------- -// dpf_attribute_list_utf8 (the custom variant) - -struct v3_attribute_list_utf8_cpp : v3_funknown { - v3_attribute_list_utf8 attr; -}; - -struct dpf_attribute_list_utf8 : v3_attribute_list_utf8_cpp { - std::map& attrs; - - dpf_attribute_list_utf8(std::map& a) - : attrs(a) - { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_attribute_list_utf8_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_attribute_list_utf8::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_attribute_list_utf8 - - attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result - { - dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - const uint32_t size = std::strlen(string) + 1; - int16_t* const copy = (int16_t*)std::malloc(sizeof(int16_t) * size); - DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); - - DISTRHO_NAMESPACE::strncpy_utf16(copy, string, size); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 's'; - attrval.binary.ptr = copy; - attrval.binary.size = sizeof(int16_t) * size; - return V3_OK; - }; - - attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result - { - dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - return V3_NOT_IMPLEMENTED; - }; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- -// dpf_attribute_list - -struct dpf_attribute_list : v3_attribute_list_cpp { - ScopedPointer attrutf8; - std::map attrs; - - dpf_attribute_list() - { - static const uint8_t* kSupportedInterfacesBase[] = { - v3_funknown_iid, - v3_attribute_list_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_attribute_list::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfacesBase) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NO_INTERFACE); - - if (v3_tuid_match(v3_attribute_list_utf8_iid, iid)) - { - if (attr->attrutf8 == nullptr) - attr->attrutf8 = new dpf_attribute_list_utf8(attr->attrs); - *iface = &attr->attrutf8; - return V3_OK; - } - - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_attribute_list - - attrlist.set_int = []V3_API(void* self, const char* id, int64_t value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 'i'; - attrval.integer = value; - return V3_OK; - }; - - attrlist.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'i', V3_INVALID_ARG); - - *value = attrval.integer; - return V3_OK; - }; - - attrlist.set_float = []V3_API(void* self, const char* id, double value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 'f'; - attrval.v_float = value; - return V3_OK; - }; - - attrlist.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'f', V3_INVALID_ARG); - - *value = attrval.v_float; - return V3_OK; - }; - - attrlist.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 's'; - attrval.binary.ptr = nullptr; - attrval.binary.size = 0; - return V3_NOT_IMPLEMENTED; - }; - - attrlist.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's', V3_INVALID_ARG); - - return V3_NOT_IMPLEMENTED; - }; - - attrlist.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - void* const copy = std::malloc(size); - DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); - - std::memcpy(copy, data, size); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 'b'; - attrval.binary.ptr = copy; - attrval.binary.size = size; - return V3_NOT_IMPLEMENTED; - }; - - attrlist.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's' || attrval.type == 'b', V3_INVALID_ARG); - - *data = attrval.binary.ptr; - *size = attrval.binary.size; - return V3_OK; - }; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- -// dpf_message - -struct dpf_message : v3_message_cpp { - std::atomic refcounter; - ScopedPointer* self; - ScopedPointer attrlist; - String id; - - dpf_message(ScopedPointer* const s, const char* const id2) - : self(s), - id(id2) - { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_message_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - ref = []V3_API(void* const self) -> uint32_t - { - d_stdout("dpf_message::ref => %p", self); - dpf_message** const messageptr = static_cast(self); - DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); - dpf_message* const message = *messageptr; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - - return ++message->refcounter; - }; - - unref = []V3_API(void* const self) -> uint32_t - { - d_stdout("dpf_message::unref => %p", self); - dpf_message** const messageptr = static_cast(self); - DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); - dpf_message* const message = *messageptr; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - - if (const int refcounter = --message->refcounter) - return refcounter; - - if (message->attrlist != nullptr) - dpf_attribute_list_free(message->attrlist->attrs); - - *message->self = nullptr; - delete messageptr; - return 0; - }; - - msg.get_message_id = []V3_API(void* const self) -> const char* - { - d_stdout("dpf_message::get_message_id => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); - - return message->id; - }; - - msg.set_message_id = []V3_API(void* const self, const char* const id) -> void - { - d_stdout("dpf_message::set_message_id => %p %s", self, id); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - - message->id = id; - }; - - msg.get_attributes = []V3_API(void* const self) -> v3_attribute_list** - { - d_stdout("dpf_message::get_attributes => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); - - if (message->attrlist == nullptr) - message->attrlist = new dpf_attribute_list(); - - return (v3_attribute_list**)&message->attrlist; - }; - } -}; - -v3_message** dpf_message_create(const char* const id) -{ - ScopedPointer* const messageptr = new ScopedPointer; - *messageptr = new dpf_message(messageptr, id); - return static_cast(static_cast(messageptr)); -} - // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale From 3c0d54c3de7bc15a9352723640860c2e73316248 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 18:50:28 +0100 Subject: [PATCH 097/504] Fix build Signed-off-by: falkTX --- distrho/src/DistrhoUIVST3.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 06dd58cc..957f707f 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -72,6 +72,12 @@ v3_message** dpf_message_create(const char* id); // -------------------------------------------------------------------------------------------------------------------- // custom attribute list struct, used for sending utf8 strings +struct v3_attribute_list_utf8 { + struct v3_funknown; + V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); + V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); +}; + static constexpr const v3_tuid v3_attribute_list_utf8_iid = V3_ID(d_cconst('D','P','F',' '), d_cconst('c','l','a','s'), @@ -290,7 +296,7 @@ public: for (uint32_t i=0; iset_int(attrlist, "__dpf_msg_target__", 1); From 9b94fe398e6d1810d6d49ca34862d02fad5d14ec Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 20:24:45 +0100 Subject: [PATCH 098/504] VST3: Use UI idle to request changes from DSP Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 5 + distrho/src/DistrhoPluginVST3.cpp | 231 +++++++++++++++++--------- distrho/src/DistrhoUIVST3.cpp | 95 ++++++++--- 3 files changed, 226 insertions(+), 105 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 625a8822..5b50682a 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -541,6 +541,11 @@ public: return (getParameterHints(index) & kParameterIsOutput) != 0x0; } + bool isParameterTrigger(const uint32_t index) const noexcept + { + return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger; + } + bool isParameterOutputOrTrigger(const uint32_t index) const noexcept { const uint32_t hints = getParameterHints(index); diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 8c2cee86..978408f3 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -49,7 +49,7 @@ * - hide program parameter? * - save and restore state * - save and restore current program - * - proper send of parameter, program and state changes to UI + * - deal with parameter triggers * - send current state to UI on request * - midi cc parameter mapping * - full MIDI1 encode and decode @@ -290,6 +290,11 @@ static constexpr const v3_tuid v3_attribute_list_utf8_iid = v3_message** dpf_message_create(const char* id); +// -------------------------------------------------------------------------------------------------------------------- +// dpf_plugin_view_create (implemented on UI side) + +v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate); + // -------------------------------------------------------------------------------------------------------------------- /** @@ -321,10 +326,14 @@ public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), - fConnectionToUI(nullptr), + fConnection(nullptr), + fConnectedToUI(false), fParameterOffset(fPlugin.getParameterOffset()), fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), fParameterValues(nullptr) +#if DISTRHO_PLUGIN_HAS_UI + , fChangedParameterValues(nullptr) +#endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fHostEventOutputHandle(nullptr) #endif @@ -405,6 +414,14 @@ public: for (uint32_t i=0; i < parameterCount; ++i) fParameterValues[i] = fPlugin.getParameterDefault(i); } + +#if DISTRHO_PLUGIN_HAS_UI + if (fRealParameterCount != 0) + { + fChangedParameterValues = new bool[fRealParameterCount]; + std::memset(fChangedParameterValues, 0, sizeof(bool)*fRealParameterCount); + } +#endif } ~PluginVst3() @@ -414,6 +431,14 @@ public: delete[] fParameterValues; fParameterValues = nullptr; } + +#if DISTRHO_PLUGIN_HAS_UI + if (fChangedParameterValues != nullptr) + { + delete[] fChangedParameterValues; + fChangedParameterValues = nullptr; + } +#endif } // ---------------------------------------------------------------------------------------------------------------- @@ -429,11 +454,6 @@ public: return fPlugin.getSampleRate(); } - void setConnectionToUI(v3_connection_point** const point) noexcept - { - fConnectionToUI = point; - } - // ---------------------------------------------------------------------------------------------------------------- // v3_component interface calls @@ -1270,36 +1290,39 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); - // TESTING remove this - sendParameterChangeToUI(rindex, value); - #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) { fCurrentProgram = std::round(value * fProgramCountMinusOne); fPlugin.loadProgram(fCurrentProgram); - return V3_OK; } + else #endif + { + const uint32_t index = rindex - fParameterOffset; + const uint32_t hints = fPlugin.getParameterHints(index); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); - const uint32_t index = rindex - fParameterOffset; - const uint32_t hints = fPlugin.getParameterHints(index); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + float realValue = ranges.getUnnormalizedValue(value); - float realValue = ranges.getUnnormalizedValue(value); + if (hints & kParameterIsBoolean) + { + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + realValue = realValue > midRange ? ranges.max : ranges.min; + } - if (hints & kParameterIsBoolean) - { - const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; - realValue = realValue > midRange ? ranges.max : ranges.min; - } + if (hints & kParameterIsInteger) + { + realValue = std::round(realValue); + } - if (hints & kParameterIsInteger) - { - realValue = std::round(realValue); + fParameterValues[index] = realValue; + fPlugin.setParameterValue(index, realValue); } - fPlugin.setParameterValue(index, realValue); +#if DISTRHO_PLUGIN_HAS_UI + fChangedParameterValues[rindex] = true; +#endif return V3_OK; } @@ -1314,14 +1337,18 @@ public: void connect(v3_connection_point** const other) { - fConnectionToUI = other; + DISTRHO_SAFE_ASSERT(fConnectedToUI == false); + + fConnection = other; + fConnectedToUI = false; d_stdout("---------------------------------------------------------- will send plugin state now"); } void disconnect() { - fConnectionToUI = nullptr; + fConnection = nullptr; + fConnectedToUI = false; d_stdout("---------------------------------------------------------- ui conn now null"); } @@ -1351,9 +1378,63 @@ public: } #endif + if (std::strcmp(msgid, "init") == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR); + fConnectedToUI = true; + + // report current state to UI + for (uint32_t i=0; iset_int(attrlist, "__dpf_msg_target__", 2); v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex); v3_cpp_obj(attrlist)->set_float(attrlist, "value", value); - v3_cpp_obj(fConnectionToUI)->notify(fConnectionToUI, message); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); + } + + void sendReadyToUI() + { + v3_message** const message = dpf_message_create("ready"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); + v3_cpp_obj(fConnection)->notify(fConnection, message); v3_cpp_obj_unref(message); } @@ -1603,13 +1705,6 @@ private: * VST3 low-level pointer thingies follow, proceed with care. */ -#if DISTRHO_PLUGIN_HAS_UI -// -------------------------------------------------------------------------------------------------------------------- -// dpf_plugin_view_create (called from DSP side) - -v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate); -#endif - // -------------------------------------------------------------------------------------------------------------------- struct dpf_attribute_value { @@ -1936,7 +2031,6 @@ struct dpf_message : v3_message_cpp { unref = []V3_API(void* const self) -> uint32_t { - d_stdout("dpf_message::unref => %p", self); dpf_message** const messageptr = static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); dpf_message* const message = *messageptr; @@ -1955,7 +2049,6 @@ struct dpf_message : v3_message_cpp { msg.get_message_id = []V3_API(void* const self) -> const char* { - d_stdout("dpf_message::get_message_id => %p", self); dpf_message* const message = *(dpf_message**)self; DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); @@ -1973,7 +2066,6 @@ struct dpf_message : v3_message_cpp { msg.get_attributes = []V3_API(void* const self) -> v3_attribute_list** { - d_stdout("dpf_message::get_attributes => %p", self); dpf_message* const message = *(dpf_message**)self; DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); @@ -2086,7 +2178,6 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result { - d_stdout("dpf_dsp_connection_point::notify => %p %p", self, message); dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); @@ -2102,68 +2193,52 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { int64_t target = 0; const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target); DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res); - DISTRHO_SAFE_ASSERT_RETURN(target != 0, V3_INTERNAL_ERR); + DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR); switch (point->type) { - // pass message from component (aka plugin) to controller + // message belongs to component (aka plugin) case kConnectionPointComponent: - { - d_stdout("dpf_dsp_connection_point::notify kConnectionPointComponent"); - - switch (target) + if (target == 1) { - case 1: - // message is from view to controller to component + // view -> edit controller -> component return vst3->notify(message); - case 2: + } + else + { // message is from component to controller to view return v3_cpp_obj(other)->notify(other, message); } - break; - } - // pass message from controller to component (aka plugin) + // message belongs to edit controller case kConnectionControllerToComponent: - { - d_stdout("dpf_dsp_connection_point::notify kConnectionControllerToComponent"); - - switch (target) + if (target == 1) { - case 1: - // message is from view to controller to component + // view -> edit controller -> component return v3_cpp_obj(other)->notify(other, message); - case 2: + } + else { // message is from component to controller to view v3_connection_point** const bridge = point->bridge; DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED); return v3_cpp_obj(bridge)->notify(bridge, message); } - } - break; - } - // pass message from from view (aka ui) to controller + // message belongs to view (aka ui) case kConnectionControllerToView: - { - d_stdout("dpf_dsp_connection_point::notify kConnectionControllerToView"); - - switch (target) - { - case 1: + if (target == 1) { - // message is from view to controller to component + // view -> edit controller -> component v3_connection_point** const bridge = point->bridge; DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED); return v3_cpp_obj(bridge)->notify(bridge, message); } - case 2: + else + { // message is from component to controller to view return v3_cpp_obj(other)->notify(other, message); } - break; - } } return V3_INTERNAL_ERR; diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 957f707f..1afb5124 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -14,25 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "DistrhoPluginChecks.h" - -#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -// #undef DISTRHO_PLUGIN_HAS_UI -// #define DISTRHO_PLUGIN_HAS_UI 1 - -#if DISTRHO_PLUGIN_HAS_UI - -// -------------------------------------------------------------------------------------------------------------------- - #include "DistrhoUIInternal.hpp" #include @@ -45,10 +26,9 @@ #include "travesty/view.h" /* TODO items: - * - disable UI if non-embed UI build * - sample rate change listener - * - proper send of parameter, program and state changes to UI - * - request current state of UI when created + * - ui mousewheel event + * - ui key down/up events */ START_NAMESPACE_DISTRHO @@ -119,6 +99,7 @@ public: fView(view), fConnection(nullptr), fFrame(nullptr), + fReadyForPluginData(false), fScaleFactor(scaleFactor) { // TESTING awful idea dont reuse @@ -128,6 +109,9 @@ public: ~UIVst3() override { stopThread(5000); + + if (fConnection != nullptr) + disconnect(); } // TESTING awful idea dont reuse @@ -135,6 +119,12 @@ public: { while (! shouldThreadExit()) { + if (fReadyForPluginData) + { + fReadyForPluginData = false; + requestMorePluginData(); + } + fUI.plugin_idle(); d_msleep(50); } @@ -227,11 +217,40 @@ public: DISTRHO_SAFE_ASSERT_RETURN(point != nullptr,); fConnection = point; + + d_stdout("requesting current plugin state"); + + v3_message** const message = dpf_message_create("init"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); } void disconnect() noexcept { + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + + d_stdout("reporting UI closed"); + + v3_message** const message = dpf_message_create("close"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); + fConnection = nullptr; + fReadyForPluginData = false; } v3_result notify(v3_message** const message) @@ -301,6 +320,13 @@ public: } #endif + if (std::strcmp(msgid, "ready") == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(! fReadyForPluginData, V3_INTERNAL_ERR); + fReadyForPluginData = true; + return V3_OK; + } + d_stdout("UIVst3 received unknown msg '%s'", msgid); return V3_NOT_IMPLEMENTED; @@ -318,8 +344,28 @@ private: v3_plugin_frame** fFrame; // Temporary data + bool fReadyForPluginData; float fScaleFactor; + // ---------------------------------------------------------------------------------------------------------------- + // helper functions called during message passing + + void requestMorePluginData() const + { + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + + v3_message** const message = dpf_message_create("idle"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); + } + // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks @@ -600,7 +646,6 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result { - d_stdout("dpf_ui_connection_point::notify => %p %p", self, message); dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); @@ -906,7 +951,3 @@ v3_plugin_view** dpf_plugin_view_create(void* const instancePointer, const doubl // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO - -// -------------------------------------------------------------------------------------------------------------------- - -#endif // DISTRHO_PLUGIN_HAS_UI From 1a7ed1d45c34fffdfe8afddd3b604603e64518ca Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 22:03:01 +0100 Subject: [PATCH 099/504] VST3: shortcircuit view/controller for hosts without connections Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 117 ++++++++++++++++++++---------- distrho/src/DistrhoUIVST3.cpp | 27 +++---- 2 files changed, 91 insertions(+), 53 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 978408f3..edd156d3 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1333,7 +1333,7 @@ public: } // ---------------------------------------------------------------------------------------------------------------- - // dpf_dsp_connection_point + // v3_connection_point interface calls void connect(v3_connection_point** const other) { @@ -1341,16 +1341,12 @@ public: fConnection = other; fConnectedToUI = false; - - d_stdout("---------------------------------------------------------- will send plugin state now"); } void disconnect() { fConnection = nullptr; fConnectedToUI = false; - - d_stdout("---------------------------------------------------------- ui conn now null"); } v3_result notify(v3_message** const message) @@ -1410,6 +1406,9 @@ public: #if DISTRHO_PLUGIN_HAS_UI for (uint32_t i=0; iget_int(attrs, "rindex", &rindex); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(rindex > 0, rindex, V3_INTERNAL_ERR); res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - rindex -= fParameterOffset; - DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); - - return requestParameterValueChange(rindex, value) ? V3_OK : V3_INTERNAL_ERR; + const uint32_t index = static_cast(rindex -= fParameterOffset); + return requestParameterValueChange(index, value) ? V3_OK : V3_INTERNAL_ERR; } #if DISTRHO_PLUGIN_WANT_STATE @@ -1622,15 +1620,20 @@ private: DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); const uint32_t rindex = index + fParameterOffset; + const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value); - if (v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex) != V3_OK) - return false; + const v3_result res_edit = v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex); + DISTRHO_SAFE_ASSERT_INT_RETURN(res_edit == V3_TRUE || res_edit == V3_FALSE, res_edit, res_edit); - const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value); - const bool ret = v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, rindex, normalized) == V3_OK; + const v3_result res_perf = v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, rindex, normalized); - v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex); - return ret; + if (res_perf == V3_TRUE) + fParameterValues[index] = value; + + if (res_edit == V3_TRUE) + v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex); + + return res_perf == V3_TRUE; } #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST @@ -2089,8 +2092,8 @@ v3_message** dpf_message_create(const char* const id) enum ConnectionPointType { kConnectionPointComponent, - kConnectionControllerToComponent, - kConnectionControllerToView + kConnectionPointController, + kConnectionPointBridge }; struct v3_connection_point_cpp : v3_funknown { @@ -2102,12 +2105,14 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { const ConnectionPointType type; v3_connection_point** other; v3_connection_point** bridge; // when type is controller this points to ctrl<->view point + bool shortcircuit; // plugin as controller, should pass directly to view dpf_dsp_connection_point(const ConnectionPointType t, ScopedPointer& v) : vst3(v), type(t), other(nullptr), - bridge(nullptr) + bridge(nullptr), + shortcircuit(false) { static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, @@ -2211,22 +2216,30 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { } // message belongs to edit controller - case kConnectionControllerToComponent: + case kConnectionPointController: if (target == 1) { + // we are in view<->dsp short-circuit, all happens in the controller without bridge + if (point->shortcircuit) + return vst3->notify(message); + // view -> edit controller -> component return v3_cpp_obj(other)->notify(other, message); } else { + // we are in view<->dsp short-circuit, all happens in the controller without bridge + if (point->shortcircuit) + return v3_cpp_obj(other)->notify(other, message); + // message is from component to controller to view v3_connection_point** const bridge = point->bridge; DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED); return v3_cpp_obj(bridge)->notify(bridge, message); } - // message belongs to view (aka ui) - case kConnectionControllerToView: + // message belongs to bridge (aka ui) + case kConnectionPointBridge: if (target == 1) { // view -> edit controller -> component @@ -2255,8 +2268,8 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { - ScopedPointer connectionComp; // kConnectionControllerToComponent - ScopedPointer connectionView; // kConnectionControllerToView + ScopedPointer connectionComp; // kConnectionPointController + ScopedPointer connectionBridge; // kConnectionPointBridge ScopedPointer& vst3; bool initialized; // cached values @@ -2298,7 +2311,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (v3_tuid_match(v3_connection_point_iid, iid)) { if (controller->connectionComp == nullptr) - controller->connectionComp = new dpf_dsp_connection_point(kConnectionControllerToComponent, + controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, controller->vst3); *iface = &controller->connectionComp; return V3_OK; @@ -2356,7 +2369,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { #if 0 return vst3->setComponentState(stream); #endif - return V3_NOT_IMPLEMENTED; + + // TODO, returning ok to make renoise happy + return V3_OK; }; controller.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result @@ -2507,34 +2522,56 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); +#if DISTRHO_PLUGIN_HAS_UI PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); -#if DISTRHO_PLUGIN_HAS_UI - // we require a component connection - DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp != nullptr, nullptr); - DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp->other != nullptr, nullptr); + // if there is a component connection, we require it to be active + if (controller->connectionComp != nullptr) + { + DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp->other != nullptr, nullptr); + } + // otherwise short-circuit and deal with this ourselves (assume local usage) + else + { + controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, + controller->vst3); + controller->connectionComp->shortcircuit = true; + } v3_plugin_view** const view = dpf_plugin_view_create(vst3->getInstancePointer(), vst3->getSampleRate()); DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr); - v3_connection_point** connection = nullptr; - if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &connection) == V3_OK) + v3_connection_point** uiconn = nullptr; + if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &uiconn) == V3_OK) { - d_stdout("view connection query ok %p", connection); + d_stdout("view connection query ok %p | shortcircuit %d", + uiconn, controller->connectionComp->shortcircuit); - v3_connection_point** const bridge = (v3_connection_point**)&controller->connectionComp; - controller->connectionView = new dpf_dsp_connection_point(kConnectionControllerToView, - controller->vst3); + v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionComp; - v3_connection_point** const other = (v3_connection_point**)&controller->connectionView; - v3_cpp_obj(connection)->connect(connection, other); - v3_cpp_obj(other)->connect(other, connection); + if (controller->connectionComp->shortcircuit) + { + vst3->disconnect(); + + v3_cpp_obj(uiconn)->connect(uiconn, ctrlconn); + v3_cpp_obj(ctrlconn)->connect(ctrlconn, uiconn); + + vst3->connect(ctrlconn); + } + else + { + controller->connectionBridge = new dpf_dsp_connection_point(kConnectionPointBridge, + controller->vst3); - controller->connectionComp->bridge = other; - controller->connectionView->bridge = bridge; + v3_connection_point** const bridgeconn = (v3_connection_point**)&controller->connectionBridge; + v3_cpp_obj(uiconn)->connect(uiconn, bridgeconn); + v3_cpp_obj(bridgeconn)->connect(bridgeconn, uiconn); + controller->connectionComp->bridge = bridgeconn; + controller->connectionBridge->bridge = ctrlconn; + } } return view; diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 1afb5124..b7d14688 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -27,8 +27,9 @@ /* TODO items: * - sample rate change listener - * - ui mousewheel event - * - ui key down/up events + * - mousewheel event + * - key down/up events + * - size constraints */ START_NAMESPACE_DISTRHO @@ -210,7 +211,7 @@ public: } // ---------------------------------------------------------------------------------------------------------------- - // dpf_ui_connection_point + // v3_connection_point interface calls void connect(v3_connection_point** const point) noexcept { @@ -237,6 +238,7 @@ public: DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); d_stdout("reporting UI closed"); + fReadyForPluginData = false; v3_message** const message = dpf_message_create("close"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); @@ -250,7 +252,6 @@ public: v3_cpp_obj_unref(message); fConnection = nullptr; - fReadyForPluginData = false; } v3_result notify(v3_message** const message) @@ -261,6 +262,15 @@ public: v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); + if (std::strcmp(msgid, "ready") == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(! fReadyForPluginData, V3_INTERNAL_ERR); + fReadyForPluginData = true; + return V3_OK; + } + + d_stdout("UIVst3 received msg '%s'", msgid); + if (std::strcmp(msgid, "parameter-set") == 0) { int64_t rindex; @@ -320,15 +330,6 @@ public: } #endif - if (std::strcmp(msgid, "ready") == 0) - { - DISTRHO_SAFE_ASSERT_RETURN(! fReadyForPluginData, V3_INTERNAL_ERR); - fReadyForPluginData = true; - return V3_OK; - } - - d_stdout("UIVst3 received unknown msg '%s'", msgid); - return V3_NOT_IMPLEMENTED; } From a51cb9f8dbade84554227c8f7f126a13e7791f15 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 22:32:17 +0100 Subject: [PATCH 100/504] VST3: trigger restart component for preset and latency changes Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 31 +++++++++++++++++++++++++++++-- examples/Latency/Makefile | 5 +++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index edd156d3..1d9e5c77 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -63,8 +63,7 @@ * - set factory sub_categories * - set factory email (needs new DPF API, useful for LV2 as well) * - do something with get_controller_class_id and set_io_mode? - * - call component handler restart with params-changed flag after changing program (doesnt seem to be needed..?) - * - call component handler restart with latency-changed flag when latency changes + * - replace dpf_message_create with host-side message creation */ START_NAMESPACE_DISTRHO @@ -334,6 +333,9 @@ public: #if DISTRHO_PLUGIN_HAS_UI , fChangedParameterValues(nullptr) #endif +#if DISTRHO_PLUGIN_WANT_LATENCY + , fLastKnownLatency(fPlugin.getLatency()) +#endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fHostEventOutputHandle(nullptr) #endif @@ -1295,6 +1297,16 @@ public: { fCurrentProgram = std::round(value * fProgramCountMinusOne); fPlugin.loadProgram(fCurrentProgram); + + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if (fPlugin.isParameterOutputOrTrigger(i)) + continue; + fParameterValues[i] = fPlugin.getParameterValue(i); + } + + if (fComponentHandler != nullptr) + v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED); } else #endif @@ -1518,6 +1530,9 @@ private: #if DISTRHO_PLUGIN_HAS_UI bool* fChangedParameterValues; #endif +#if DISTRHO_PLUGIN_WANT_LATENCY + uint32_t fLastKnownLatency; +#endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT MidiEvent fMidiEvents[kMaxMidiEvents]; # if DISTRHO_PLUGIN_HAS_UI @@ -1577,6 +1592,18 @@ private: requestParameterValueChange(i, curValue); } + +#if DISTRHO_PLUGIN_WANT_LATENCY + const uint32_t latency = fPlugin.getLatency(); + + if (fLastKnownLatency != latency) + { + fLastKnownLatency = latency; + + if (fComponentHandler != nullptr) + v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_LATENCY_CHANGED); + } +#endif } // ---------------------------------------------------------------------------------------------------------------- diff --git a/examples/Latency/Makefile b/examples/Latency/Makefile index 4b0067b9..51e51564 100644 --- a/examples/Latency/Makefile +++ b/examples/Latency/Makefile @@ -24,8 +24,9 @@ include ../../Makefile.plugins.mk # Enable all possible plugin types TARGETS += ladspa -TARGETS += lv2_dsp -TARGETS += vst +TARGETS += lv2_sep +TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) From ca35da32e45de5027aca69cf7609881629c466a0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 22:54:27 +0100 Subject: [PATCH 101/504] VST3: report sample rate changes to UI Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 67 +++++++++++++++++++++++-------- distrho/src/DistrhoUIVST3.cpp | 14 ++++++- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 1d9e5c77..ed00e831 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -332,6 +332,7 @@ public: fParameterValues(nullptr) #if DISTRHO_PLUGIN_HAS_UI , fChangedParameterValues(nullptr) + , fNextSampleRate(0.0) #endif #if DISTRHO_PLUGIN_WANT_LATENCY , fLastKnownLatency(fPlugin.getLatency()) @@ -340,8 +341,8 @@ public: , fHostEventOutputHandle(nullptr) #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS - , fCurrentProgram(0), - fProgramCountMinusOne(fPlugin.getProgramCount()-1) + , fCurrentProgram(0) + , fProgramCountMinusOne(fPlugin.getProgramCount()-1) #endif { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 @@ -879,6 +880,11 @@ public: // TODO process_mode can be V3_REALTIME, V3_PREFETCH, V3_OFFLINE +#if DISTRHO_PLUGIN_HAS_UI + if (d_isNotEqual(setup->sample_rate, fPlugin.getSampleRate())) + fNextSampleRate = setup->sample_rate; +#endif + fPlugin.setSampleRate(setup->sample_rate, true); fPlugin.setBufferSize(setup->max_block_size, true); @@ -1347,6 +1353,7 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point interface calls +#if DISTRHO_PLUGIN_HAS_UI void connect(v3_connection_point** const other) { DISTRHO_SAFE_ASSERT(fConnectedToUI == false); @@ -1369,7 +1376,7 @@ public: v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (std::strcmp(msgid, "midi") == 0) { uint8_t* data; @@ -1384,25 +1391,29 @@ public: return fNotesRingBuffer.writeCustomData(data, size) && fNotesRingBuffer.commitWrite() ? V3_OK : V3_NOMEM; } -#endif +# endif if (std::strcmp(msgid, "init") == 0) { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR); fConnectedToUI = true; + if (const double sampleRate = fNextSampleRate) + { + fNextSampleRate = 0.0; + sendSampleRateToUI(sampleRate); + } + // report current state to UI for (uint32_t i=0; iget_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); + v3_cpp_obj(attrlist)->set_float(attrlist, "value", sampleRate); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); + } + void sendParameterChangeToUI(const v3_param_id rindex, const double value) { v3_message** const message = dpf_message_create("parameter-set"); @@ -2114,6 +2140,7 @@ v3_message** dpf_message_create(const char* const id) return static_cast(static_cast(messageptr)); } +#if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- // dpf_dsp_connection_point @@ -2285,6 +2312,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { }; } }; +#endif // -------------------------------------------------------------------------------------------------------------------- // dpf_edit_controller @@ -2295,18 +2323,18 @@ struct v3_edit_controller_cpp : v3_funknown { }; struct dpf_edit_controller : v3_edit_controller_cpp { +#if DISTRHO_PLUGIN_HAS_UI ScopedPointer connectionComp; // kConnectionPointController ScopedPointer connectionBridge; // kConnectionPointBridge +#endif ScopedPointer& vst3; bool initialized; // cached values - v3_connection_point** connectionToUI; v3_component_handler** handler; dpf_edit_controller(ScopedPointer& v) : vst3(v), initialized(false), - connectionToUI(nullptr), handler(nullptr) { static const uint8_t* kSupportedInterfacesBase[] = { @@ -2332,6 +2360,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } } +#if DISTRHO_PLUGIN_HAS_UI dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NO_INTERFACE); @@ -2343,6 +2372,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { *iface = &controller->connectionComp; return V3_OK; } +#endif return V3_NO_INTERFACE; }; @@ -2377,7 +2407,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); controller->initialized = false; - controller->connectionToUI = nullptr; return V3_OK; }; @@ -2835,7 +2864,9 @@ struct dpf_component : v3_component_cpp { std::atomic refcounter; ScopedPointer* self; ScopedPointer processor; +#if DISTRHO_PLUGIN_HAS_UI ScopedPointer connection; // kConnectionPointComponent +#endif ScopedPointer controller; ScopedPointer vst3; @@ -2877,6 +2908,7 @@ struct dpf_component : v3_component_cpp { return V3_OK; } +#if DISTRHO_PLUGIN_HAS_UI if (v3_tuid_match(v3_connection_point_iid, iid)) { if (component->connection == nullptr) @@ -2885,6 +2917,7 @@ struct dpf_component : v3_component_cpp { *iface = &component->connection; return V3_OK; } +#endif if (v3_tuid_match(v3_edit_controller_iid, iid)) { diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index b7d14688..bb6b6cd9 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -26,7 +26,6 @@ #include "travesty/view.h" /* TODO items: - * - sample rate change listener * - mousewheel event * - key down/up events * - size constraints @@ -330,6 +329,19 @@ public: } #endif + if (std::strcmp(msgid, "sample-rate") == 0) + { + double sampleRate; + v3_result res; + + res = v3_cpp_obj(attrs)->get_float(attrs, "value", &sampleRate); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_RETURN(sampleRate > 0, V3_INVALID_ARG); + + fUI.setSampleRate(sampleRate, true); + return V3_OK; + } + return V3_NOT_IMPLEMENTED; } From f90cc570dde844f8963bd63b81913990fc30105b Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 23:33:22 +0100 Subject: [PATCH 102/504] VST3: Cache state key/values and send them to UI on init Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 129 ++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index ed00e831..e4fab400 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -77,6 +77,8 @@ static constexpr const writeMidiFunc writeMidiCallback = nullptr; static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif +typedef std::map StringMap; + // -------------------------------------------------------------------------------------------------------------------- // custom v3_tuid compatible type @@ -102,6 +104,21 @@ static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, 0 }; static dpf_tuid dpf_tuid_processor = { dpf_id_entry, dpf_id_proc, 0, 0 }; static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; +// -------------------------------------------------------------------------------------------------------------------- +// custom attribute list struct, used for sending utf8 strings + +struct v3_attribute_list_utf8 { + struct v3_funknown; + V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); + V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); +}; + +static constexpr const v3_tuid v3_attribute_list_utf8_iid = + V3_ID(d_cconst('D','P','F',' '), + d_cconst('c','l','a','s'), + d_cconst('a','t','t','r'), + d_cconst('u','t','f','8')); + // -------------------------------------------------------------------------------------------------------------------- // Utility functions @@ -132,6 +149,8 @@ const char* tuid2str(const v3_tuid iid) return "{v3_audio_processor}"; if (v3_tuid_match(iid, v3_attribute_list_iid)) return "{v3_attribute_list_iid}"; + if (v3_tuid_match(iid, v3_attribute_list_utf8_iid)) + return "{v3_attribute_list_utf8_iid|CUSTOM}"; if (v3_tuid_match(iid, v3_bstream_iid)) return "{v3_bstream}"; if (v3_tuid_match(iid, v3_component_iid)) @@ -269,21 +288,6 @@ static constexpr void (*const snprintf_u32)(char*, uint32_t, size_t) = snprintf_ static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snprintf_utf16_t; static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t; -// -------------------------------------------------------------------------------------------------------------------- -// custom attribute list struct, used for sending utf8 strings - -struct v3_attribute_list_utf8 { - struct v3_funknown; - V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); - V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); -}; - -static constexpr const v3_tuid v3_attribute_list_utf8_iid = - V3_ID(d_cconst('D','P','F',' '), - d_cconst('c','l','a','s'), - d_cconst('a','t','t','r'), - d_cconst('u','t','f','8')); - // -------------------------------------------------------------------------------------------------------------------- // create message object (implementation comes later) @@ -325,12 +329,14 @@ public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), +#if DISTRHO_PLUGIN_HAS_UI fConnection(nullptr), - fConnectedToUI(false), +#endif fParameterOffset(fPlugin.getParameterOffset()), fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), fParameterValues(nullptr) #if DISTRHO_PLUGIN_HAS_UI + , fConnectedToUI(false) , fChangedParameterValues(nullptr) , fNextSampleRate(0.0) #endif @@ -425,6 +431,14 @@ public: std::memset(fChangedParameterValues, 0, sizeof(bool)*fRealParameterCount); } #endif + +#if DISTRHO_PLUGIN_WANT_STATE + for (uint32_t i=0, count=fPlugin.getStateCount(); ifirst; + fStateMap[key] = fPlugin.getState(key); + } +# endif -# if DISTRHO_PLUGIN_WANT_PROGRAMS - if (i == 0) - sendParameterChangeToUI(i, fCurrentProgram); - else +# if DISTRHO_PLUGIN_WANT_STATE + // Set state + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) + { + const String& key = cit->first; + const String& value = cit->second; + + sendStateChangeToUI(key, value); + } # endif - sendParameterChangeToUI(i, fPlugin.getParameterValue(i - fParameterOffset)); + + for (uint32_t i=fParameterOffset; ifirst); + + if (dkey == key) + { + it->second = value; + return V3_OK; + } + } + + d_stderr("Failed to find plugin state with key \"%s\"", key); + } + return V3_OK; } # endif @@ -1530,14 +1580,16 @@ private: // VST3 stuff v3_component_handler** fComponentHandler; +#if DISTRHO_PLUGIN_HAS_UI v3_connection_point** fConnection; - bool fConnectedToUI; +#endif // Temporary data const uint32_t fParameterOffset; const uint32_t fRealParameterCount; // regular parameters + current program float* fParameterValues; #if DISTRHO_PLUGIN_HAS_UI + bool fConnectedToUI; bool* fChangedParameterValues; double fNextSampleRate; // if not zero, report to UI #endif @@ -1557,6 +1609,9 @@ private: uint32_t fCurrentProgram; const uint32_t fProgramCountMinusOne; #endif +#if DISTRHO_PLUGIN_WANT_STATE + StringMap fStateMap; +#endif #if DISTRHO_PLUGIN_WANT_TIMEPOS TimePosition fTimePosition; #endif @@ -1617,6 +1672,7 @@ private: #endif } +#if DISTRHO_PLUGIN_HAS_UI // ---------------------------------------------------------------------------------------------------------------- // helper functions called during message passing, can block @@ -1651,6 +1707,26 @@ private: v3_cpp_obj_unref(message); } + void sendStateChangeToUI(const char* const key, const char* const value) + { + v3_message** const message = dpf_message_create("state-set"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); + + v3_attribute_list_utf8** utf8attrlist = nullptr; + DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrlist, v3_attribute_list_utf8_iid, &utf8attrlist) == V3_OK,); + DISTRHO_SAFE_ASSERT_RETURN(utf8attrlist != nullptr,); + + v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); + v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "key", key); + v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "value", value); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); + } + void sendReadyToUI() { v3_message** const message = dpf_message_create("ready"); @@ -1664,6 +1740,7 @@ private: v3_cpp_obj_unref(message); } +#endif // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks From a4aa2d83c8d51a47402ac0417a365ec0fe36cce3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 27 Sep 2021 01:07:33 +0100 Subject: [PATCH 103/504] VST3: full state save/restore support, update TODO items Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 280 ++++++++++++++++++++++-------- distrho/src/DistrhoUIVST3.cpp | 14 +- examples/Meters/Makefile | 3 +- 3 files changed, 214 insertions(+), 83 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index e4fab400..17fb2207 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -47,10 +47,7 @@ * - parameter enumeration as lists * - hide parameter outputs? * - hide program parameter? - * - save and restore state - * - save and restore current program * - deal with parameter triggers - * - send current state to UI on request * - midi cc parameter mapping * - full MIDI1 encode and decode * - decode version number (0x102030 -> 1.2.3) @@ -58,7 +55,7 @@ * - optional audio buses, create dummy buffer of max_block_size length for them * - routing info, do we care? * - set sidechain bus name from port group - * - implement getParameterValueForString + * - implement getParameterValueForString (use names from enumeration if available, fallback to std::atof) * - set factory class_flags * - set factory sub_categories * - set factory email (needs new DPF API, useful for LV2 as well) @@ -692,82 +689,194 @@ public: } /* state: we pack pairs of key-value strings each separated by a null/zero byte. - * states come first, and then parameters. parameters are simply converted to/from strings and floats. + * states come first, then current-program and then parameters. + * parameters are simply converted to/from strings and floats. * the parameter symbol is used as the "key", so it is possible to reorder them or even remove and add safely. - * the number of states must remain constant though. + * there are markers for begin and end of state and parameters, so they never conflict. */ v3_result setState(v3_bstream** const stream) { -#if DISTRHO_PLUGIN_WANT_STATE - // TODO -#endif -#if DISTRHO_PLUGIN_WANT_PROGRAMS - // TODO +#if DISTRHO_PLUGIN_HAS_UI + const bool connectedToUI = fConnection != nullptr && fConnectedToUI; #endif + const uint32_t paramCount = fPlugin.getParameterCount(); - if (const uint32_t paramCount = fPlugin.getParameterCount()) - { - char buffer[32], orig; - String key, value; - v3_result res; - bool fillingKey = true; + String key, value; + bool fillingKey = true; // if filling key or value + char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters) - // temporarily set locale to "C" while converting floats - const ScopedSafeLocale ssl; + char buffer[512], orig; + buffer[sizeof(buffer)-1] = '\xff'; + v3_result res; - for (int32_t pos = 0, read;; pos += read) + for (int32_t pos = 0, term = 0, read; term == 0; pos += read) + { + res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); + + for (int32_t i = 0; i < read; ++i) { - std::memset(buffer, '\xff', sizeof(buffer)); - res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); + // found terminator, stop here + if (buffer[i] == '\xfe') + { + term = 1; + break; + } + + // store character at read position + orig = buffer[read]; + + // place null character to create valid string + buffer[read] = '\0'; + + // append to temporary vars + if (fillingKey) + key += buffer + i; + else + value += buffer + i; + + // increase buffer offset by length of string + i += std::strlen(buffer + i); + + // restore read character + buffer[read] = orig; - for (int32_t i = 0; i < read; ++i) + // if buffer offset points to null, we found the end of a string, lets check + if (buffer[i] == '\0') { - if (buffer[i] == '\0' && pos == 0 && i == 0) + // special keys + if (key == "__dpf_state_begin__") + { + DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n', + queryingType, V3_INTERNAL_ERR); + queryingType = 's'; + key.clear(); + value.clear(); + continue; + } + if (key == "__dpf_state_end__") + { + DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 's', queryingType, V3_INTERNAL_ERR); + queryingType = 'n'; + key.clear(); + value.clear(); + continue; + } + if (key == "__dpf_parameters_begin__") + { + DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n', + queryingType, V3_INTERNAL_ERR); + queryingType = 'p'; + key.clear(); + value.clear(); + continue; + } + if (key == "__dpf_parameters_end__") + { + DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'p', queryingType, V3_INTERNAL_ERR); + queryingType = 'x'; + key.clear(); + value.clear(); + continue; + } + + // no special key, swap between reading real key and value + fillingKey = !fillingKey; + + // if there is no value yet keep reading until we have one (TODO check empty values on purpose) + if (value.isEmpty()) continue; - orig = buffer[read]; - buffer[read] = '\0'; + if (key == "__dpf_program__") + { + DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i', queryingType, V3_INTERNAL_ERR); + queryingType = 'n'; + + d_stdout("found program '%s'", value.buffer()); - if (fillingKey) - key += buffer + i; - else - value += buffer + i; +#if DISTRHO_PLUGIN_WANT_PROGRAMS + const int program = std::atoi(value.buffer()); + DISTRHO_SAFE_ASSERT_CONTINUE(program >= 0); - i += std::strlen(buffer + i); - buffer[read] = orig; + fCurrentProgram = static_cast(program); + fPlugin.loadProgram(fCurrentProgram); - if (buffer[i] == '\0') +# if DISTRHO_PLUGIN_HAS_UI + if (connectedToUI) + { + fChangedParameterValues[0] = false; + sendParameterChangeToUI(0, fCurrentProgram); + } +# endif +#endif + } + else if (queryingType == 's') { - fillingKey = !fillingKey; + d_stdout("found state '%s' '%s'", key.buffer(), value.buffer()); - if (value.isNotEmpty()) +#if DISTRHO_PLUGIN_WANT_STATE + if (fPlugin.wantStateKey(key)) { - // find parameter with this symbol, and set its value - for (uint32_t j=0; jrestart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED); + +#if DISTRHO_PLUGIN_HAS_UI + if (connectedToUI) + { + for (uint32_t i=0; i < paramCount; ++i) + { + if (fPlugin.isParameterOutputOrTrigger(i)) + continue; + fChangedParameterValues[i + fParameterOffset] = false; + sendParameterChangeToUI(i + fParameterOffset, fParameterValues[i]); + } } +#endif } return V3_OK; @@ -789,42 +898,54 @@ public: return v3_cpp_obj(stream)->write(stream, &buffer, 1, &ignored); } - String state; - #if DISTRHO_PLUGIN_WANT_FULL_STATE - /* // Update current state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; fStateMap[key] = fPlugin.getState(key); } - */ #endif -#if DISTRHO_PLUGIN_WANT_STATE - /* - for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) - { - const String& key = cit->first; - const String& value = cit->second; + String state; - // join key and value - String tmpStr; - tmpStr = key; - tmpStr += "\xff"; - tmpStr += value; +#if DISTRHO_PLUGIN_WANT_PROGRAMS + { + String tmpStr("__dpf_program__\xff"); + tmpStr += String(fCurrentProgram); tmpStr += "\xff"; state += tmpStr; } - */ +#endif + +#if DISTRHO_PLUGIN_WANT_STATE + if (stateCount != 0) + { + state += "__dpf_state_begin__\xff"; + + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) + { + const String& key = cit->first; + const String& value = cit->second; + + // join key and value + String tmpStr; + tmpStr = key; + tmpStr += "\xff"; + tmpStr += value; + tmpStr += "\xff"; + + state += tmpStr; + } + + state += "__dpf_state_end__\xff"; + } #endif if (paramCount != 0) { - // add another separator - state += "\xff"; + state += "__dpf_parameters_begin__\xff"; for (uint32_t i=0; iget_int(attrs, "rindex", &rindex); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - DISTRHO_SAFE_ASSERT_INT_RETURN(rindex > 0, rindex, V3_INTERNAL_ERR); + DISTRHO_SAFE_ASSERT_INT_RETURN(rindex >= fParameterOffset, rindex, V3_INTERNAL_ERR); res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); @@ -1965,7 +2091,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp { query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { - d_stdout("dpf_attribute_list::query_interface => %p %s %p", self, tuid2str(iid), iface); + // d_stdout("dpf_attribute_list::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index bb6b6cd9..373306d8 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -29,6 +29,10 @@ * - mousewheel event * - key down/up events * - size constraints + * - host-side resize + * - oddities with init and size callback (triggered too early?) + * - win/mac native idle loop + * - linux runloop */ START_NAMESPACE_DISTRHO @@ -137,19 +141,19 @@ public: { // TODO return V3_NOT_IMPLEMENTED; - }; + } v3_result onKeyDown(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/) { // TODO return V3_NOT_IMPLEMENTED; - }; + } v3_result onKeyUp(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/) { // TODO return V3_NOT_IMPLEMENTED; - }; + } v3_result getSize(v3_view_rect* const rect) const noexcept { @@ -268,8 +272,6 @@ public: return V3_OK; } - d_stdout("UIVst3 received msg '%s'", msgid); - if (std::strcmp(msgid, "parameter-set") == 0) { int64_t rindex; @@ -342,6 +344,8 @@ public: return V3_OK; } + d_stdout("UIVst3 received unknown msg '%s'", msgid); + return V3_NOT_IMPLEMENTED; } diff --git a/examples/Meters/Makefile b/examples/Meters/Makefile index fbfcc10b..cd325c7c 100644 --- a/examples/Meters/Makefile +++ b/examples/Meters/Makefile @@ -37,7 +37,8 @@ endif # HAVE_LIBLO endif # MACOS_OR_WINDOWS TARGETS += lv2_sep -TARGETS += vst +TARGETS += vst2 +TARGETS += vst3 endif # HAVE_OPENGL From e485b24098c1e8f4f91d2429a00ed7b0cc1314ef Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 27 Sep 2021 01:33:26 +0100 Subject: [PATCH 104/504] Fix some wording Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 17fb2207..0f25aa08 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -689,7 +689,7 @@ public: } /* state: we pack pairs of key-value strings each separated by a null/zero byte. - * states come first, then current-program and then parameters. + * current-program comes first, then dpf key/value states and then parameters. * parameters are simply converted to/from strings and floats. * the parameter symbol is used as the "key", so it is possible to reorder them or even remove and add safely. * there are markers for begin and end of state and parameters, so they never conflict. From 67c8f57f5066d95639710263b3a448a337a33d57 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 27 Sep 2021 01:35:14 +0100 Subject: [PATCH 105/504] Correct a typo Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 0f25aa08..994cc8f7 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -50,7 +50,7 @@ * - deal with parameter triggers * - midi cc parameter mapping * - full MIDI1 encode and decode - * - decode version number (0x102030 -> 1.2.3) + * - decode version number (0x010203 -> 1.2.3) * - bus arrangements * - optional audio buses, create dummy buffer of max_block_size length for them * - routing info, do we care? From 87a195a26b9b92b0c330d59921880f74ba1cca47 Mon Sep 17 00:00:00 2001 From: JP Cimalando Date: Wed, 29 Sep 2021 12:26:40 +0200 Subject: [PATCH 106/504] cmake: support building VST3 (#330) * cmake: support building VST3 * cmake: let VST3 use the same bundle files as VST2 * Reposition the V3_API specifier for MSVC * Disable VST3 on MSVC until fixed --- cmake/DPF-plugin.cmake | 91 +++++++++++++++++++++----- distrho/src/DistrhoPluginVST3.cpp | 4 +- distrho/src/DistrhoUIVST3.cpp | 12 ++-- distrho/src/travesty/audio_processor.h | 32 ++++----- distrho/src/travesty/base.h | 10 +-- distrho/src/travesty/bstream.h | 8 +-- distrho/src/travesty/component.h | 18 ++--- distrho/src/travesty/edit_controller.h | 34 +++++----- distrho/src/travesty/events.h | 6 +- distrho/src/travesty/factory.h | 14 ++-- distrho/src/travesty/message.h | 28 ++++---- distrho/src/travesty/view.h | 30 ++++----- examples/Info/CMakeLists.txt | 2 +- examples/Latency/CMakeLists.txt | 2 +- examples/Meters/CMakeLists.txt | 2 +- examples/MidiThrough/CMakeLists.txt | 2 +- examples/Parameters/CMakeLists.txt | 2 +- examples/States/CMakeLists.txt | 2 +- 18 files changed, 180 insertions(+), 119 deletions(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 38d92845..8f458fa3 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -154,7 +154,11 @@ function(dpf_add_plugin NAME) elseif(_target STREQUAL "vst2") dpf__build_vst2("${NAME}" "${_dgl_library}") elseif(_target STREQUAL "vst3") - dpf__build_vst3("${NAME}" "${_dgl_library}") + if(MSVC) + message(WARNING "TODO: VST3 is not supported on MSVC yet") + else() + dpf__build_vst3("${NAME}" "${_dgl_library}") + endif() else() message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}") endif() @@ -341,12 +345,61 @@ function(dpf__build_vst2 NAME DGL_LIBRARY) endif() endfunction() +# dpf__determine_vst3_package_architecture +# ------------------------------------------------------------------------------ +# +# Determines the package architecture for a VST3 plugin target. +# +function(dpf__determine_vst3_package_architecture OUTPUT_VARIABLE) + # if set by variable, override the detection + if(DPF_VST3_ARCHITECTURE) + set("${OUTPUT_VARIABLE}" "${DPF_VST3_ARCHITECTURE}" PARENT_SCOPE) + return() + endif() + + # not used on Apple, which supports universal binary + if(APPLE) + set("${OUTPUT_VARIABLE}" "universal" PARENT_SCOPE) + return() + endif() + + # identify the target processor (special case of MSVC, problematic sometimes) + if(MSVC) + set(vst3_system_arch "${MSVC_CXX_ARCHITECTURE_ID}") + else() + set(vst3_system_arch "${CMAKE_SYSTEM_PROCESSOR}") + endif() + + # transform the processor name to a format that VST3 recognizes + if(vst3_system_arch MATCHES "^(x86_64|amd64|AMD64|x64|X64)$") + set(vst3_package_arch "x86_64") + elseif(vst3_system_arch MATCHES "^(i.86|x86|X86)$") + if(WIN32) + set(vst3_package_arch "x86") + else() + set(vst3_package_arch "i386") + endif() + elseif(vst3_system_arch MATCHES "^(armv[3-8][a-z]*)$") + set(vst3_package_arch "${vst3_system_arch}") + elseif(vst3_system_arch MATCHES "^(aarch64)$") + set(vst3_package_arch "aarch64") + else() + message(FATAL_ERROR "We don't know this architecture for VST3: ${vst3_system_arch}.") + endif() + + # TODO: the detections for Windows arm/arm64 when supported + + set("${OUTPUT_VARIABLE}" "${vst3_package_arch}" PARENT_SCOPE) +endfunction() + # dpf__build_vst3 # ------------------------------------------------------------------------------ # # Add build rules for a VST3 plugin. # function(dpf__build_vst3 NAME DGL_LIBRARY) + dpf__determine_vst3_package_architecture(vst3_arch) + dpf__create_dummy_source_list(_no_srcs) dpf__add_module("${NAME}-vst3" ${_no_srcs}) @@ -355,22 +408,30 @@ function(dpf__build_vst3 NAME DGL_LIBRARY) dpf__set_module_export_list("${NAME}-vst3" "vst3") target_link_libraries("${NAME}-vst3" PRIVATE "${NAME}-dsp" "${NAME}-ui") set_target_properties("${NAME}-vst3" PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/obj/vst3/$<0:>" - OUTPUT_NAME "${NAME}-vst3" + OUTPUT_NAME "${NAME}" PREFIX "") - # TODO set correct output directory for VST3 packaging - #if(APPLE) - #set_target_properties("${NAME}-vst3" PROPERTIES - #LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/MacOS/$<0:>" - #OUTPUT_NAME "${NAME}" - #SUFFIX "") - #set(INFO_PLIST_PROJECT_NAME "${NAME}") - #configure_file("${DPF_ROOT_DIR}/utils/plugin.vst3/Contents/Info.plist" - #"${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/Info.plist" @ONLY) - #file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst3/Contents/PkgInfo" - #DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents") - #endif() + + if(APPLE) + set_target_properties("${NAME}-vst3" PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/MacOS/$<0:>" + SUFFIX "") + elseif(WIN32) + set_target_properties("${NAME}-vst3" PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/${vst3_arch}-win/$<0:>") + else() + set_target_properties("${NAME}-vst3" PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/${vst3_arch}-linux/$<0:>") + endif() + + 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" + "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/Info.plist" @ONLY) + file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst/Contents/PkgInfo" + DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents") + endif() endfunction() # dpf__add_dgl_cairo diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 994cc8f7..10cfc1bd 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -106,8 +106,8 @@ static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; struct v3_attribute_list_utf8 { struct v3_funknown; - V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); - V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); + v3_result (V3_API *set_string_utf8)(void* self, const char* id, const char* string); + v3_result (V3_API *get_string_utf8)(void* self, const char* id, char* string, uint32_t size); }; static constexpr const v3_tuid v3_attribute_list_utf8_iid = diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 373306d8..56bf441d 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -58,8 +58,8 @@ v3_message** dpf_message_create(const char* id); struct v3_attribute_list_utf8 { struct v3_funknown; - V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string); - V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size); + v3_result (V3_API *set_string_utf8)(void* self, const char* id, const char* string); + v3_result (V3_API *get_string_utf8)(void* self, const char* id, char* string, uint32_t size); }; static constexpr const v3_tuid v3_attribute_list_utf8_iid = @@ -542,7 +542,7 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_fn(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_fn(void* self, const v3_tuid iid, void** iface) { d_stdout("dpf_plugin_view_content_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; @@ -566,13 +566,13 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { } // there is only a single instance of this, so we don't have to care here - static V3_API uint32_t ref_fn(void*) { return 1; }; - static V3_API uint32_t unref_fn(void*) { return 0; }; + static uint32_t V3_API ref_fn(void*) { return 1; }; + static uint32_t V3_API unref_fn(void*) { return 0; }; // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view_content_scale_steinberg - static V3_API v3_result set_content_scale_factor_fn(void* self, float factor) + static v3_result V3_API set_content_scale_factor_fn(void* self, float factor) { d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); dpf_plugin_view_content_scale* const scale = *(dpf_plugin_view_content_scale**)self; diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h index 02c0ca06..29c07e87 100644 --- a/distrho/src/travesty/audio_processor.h +++ b/distrho/src/travesty/audio_processor.h @@ -92,10 +92,10 @@ struct v3_process_setup { struct v3_param_value_queue { struct v3_funknown; - V3_API v3_param_id (*get_param_id)(void* self); - V3_API int32_t (*get_point_count)(void* self); - V3_API v3_result (*get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); - V3_API v3_result (*add_point)(void* self, int32_t sample_offset, double value, int32_t* idx); + v3_param_id (V3_API *get_param_id)(void* self); + int32_t (V3_API *get_point_count)(void* self); + v3_result (V3_API *get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); + v3_result (V3_API *add_point)(void* self, int32_t sample_offset, double value, int32_t* idx); }; static constexpr const v3_tuid v3_param_value_queue_iid = @@ -104,9 +104,9 @@ static constexpr const v3_tuid v3_param_value_queue_iid = struct v3_param_changes { struct v3_funknown; - V3_API int32_t (*get_param_count)(void* self); - V3_API struct v3_param_value_queue** (*get_param_data)(void* self, int32_t idx); - V3_API struct v3_param_value_queue** (*add_param_data)(void* self, v3_param_id* id, int32_t* index); + int32_t (V3_API *get_param_count)(void* self); + struct v3_param_value_queue** (V3_API *get_param_data)(void* self, int32_t idx); + struct v3_param_value_queue** (V3_API *add_param_data)(void* self, v3_param_id* id, int32_t* index); }; static constexpr const v3_tuid v3_param_changes_iid = @@ -183,7 +183,7 @@ enum { struct v3_process_context_requirements { struct v3_funknown; - V3_API uint32_t (*get_process_context_requirements)(void* self); + uint32_t (V3_API *get_process_context_requirements)(void* self); }; static constexpr const v3_tuid v3_process_context_requirements_iid = @@ -224,15 +224,15 @@ struct v3_process_data { struct v3_audio_processor { struct v3_funknown; - V3_API v3_result (*set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, + v3_result (V3_API *set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs); - V3_API v3_result (*get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); - V3_API v3_result (*can_process_sample_size)(void* self, int32_t symbolic_sample_size); - V3_API uint32_t (*get_latency_samples)(void* self); - V3_API v3_result (*setup_processing)(void* self, struct v3_process_setup* setup); - V3_API v3_result (*set_processing)(void* self, v3_bool state); - V3_API v3_result (*process)(void* self, struct v3_process_data* data); - V3_API uint32_t (*get_tail_samples)(void* self); + v3_result (V3_API *get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); + v3_result (V3_API *can_process_sample_size)(void* self, int32_t symbolic_sample_size); + uint32_t (V3_API *get_latency_samples)(void* self); + v3_result (V3_API *setup_processing)(void* self, struct v3_process_setup* setup); + v3_result (V3_API *set_processing)(void* self, v3_bool state); + v3_result (V3_API *process)(void* self, struct v3_process_data* data); + uint32_t (V3_API *get_tail_samples)(void* self); }; static constexpr const v3_tuid v3_audio_processor_iid = diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index a6b9dea0..43b66291 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -160,9 +160,9 @@ enum { */ struct v3_funknown { - V3_API v3_result (*query_interface)(void* self, const v3_tuid iid, void** obj); - V3_API uint32_t (*ref)(void* self); - V3_API uint32_t (*unref)(void* self); + v3_result (V3_API *query_interface)(void* self, const v3_tuid iid, void** obj); + uint32_t (V3_API *ref)(void* self); + uint32_t (V3_API *unref)(void* self); }; static constexpr const v3_tuid v3_funknown_iid = @@ -175,8 +175,8 @@ static constexpr const v3_tuid v3_funknown_iid = struct v3_plugin_base { struct v3_funknown; - V3_API v3_result (*initialise)(void* self, struct v3_funknown* context); - V3_API v3_result (*terminate)(void* self); + v3_result (V3_API *initialise)(void* self, struct v3_funknown* context); + v3_result (V3_API *terminate)(void* self); }; static constexpr const v3_tuid v3_plugin_base_iid = diff --git a/distrho/src/travesty/bstream.h b/distrho/src/travesty/bstream.h index e0db5eb6..a1bfcd3f 100644 --- a/distrho/src/travesty/bstream.h +++ b/distrho/src/travesty/bstream.h @@ -27,10 +27,10 @@ enum v3_seek_mode { struct v3_bstream { struct v3_funknown; - V3_API v3_result (*read)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_read); - V3_API v3_result (*write)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_written); - V3_API v3_result (*seek)(void* self, int64_t pos, int32_t seek_mode, int64_t* result); - V3_API v3_result (*tell)(void* self, int64_t* pos); + v3_result (V3_API *read)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_read); + v3_result (V3_API *write)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_written); + v3_result (V3_API *seek)(void* self, int64_t pos, int32_t seek_mode, int64_t* result); + v3_result (V3_API *tell)(void* self, int64_t* pos); }; static constexpr const v3_tuid v3_bstream_iid = diff --git a/distrho/src/travesty/component.h b/distrho/src/travesty/component.h index f90b79c8..7d17bf09 100644 --- a/distrho/src/travesty/component.h +++ b/distrho/src/travesty/component.h @@ -91,17 +91,17 @@ struct v3_routing_info; struct v3_component { struct v3_plugin_base; - V3_API v3_result (*get_controller_class_id)(void* self, v3_tuid class_id); - V3_API v3_result (*set_io_mode)(void* self, int32_t io_mode); - V3_API int32_t (*get_bus_count)(void* self, int32_t media_type, int32_t bus_direction); - V3_API v3_result (*get_bus_info)(void* self, int32_t media_type, int32_t bus_direction, + v3_result (V3_API *get_controller_class_id)(void* self, v3_tuid class_id); + v3_result (V3_API *set_io_mode)(void* self, int32_t io_mode); + int32_t (V3_API *get_bus_count)(void* self, int32_t media_type, int32_t bus_direction); + v3_result (V3_API *get_bus_info)(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, struct v3_bus_info* bus_info); - V3_API v3_result (*get_routing_info)(void* self, struct v3_routing_info* input, struct v3_routing_info* output); - V3_API v3_result (*activate_bus)(void* self, int32_t media_type, int32_t bus_direction, + v3_result (V3_API *get_routing_info)(void* self, struct v3_routing_info* input, struct v3_routing_info* output); + v3_result (V3_API *activate_bus)(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bool state); - V3_API v3_result (*set_active)(void* self, v3_bool state); - V3_API v3_result (*set_state)(void* self, struct v3_bstream **); - V3_API v3_result (*get_state)(void* self, struct v3_bstream **); + v3_result (V3_API *set_active)(void* self, v3_bool state); + v3_result (V3_API *set_state)(void* self, struct v3_bstream **); + v3_result (V3_API *get_state)(void* self, struct v3_bstream **); }; static constexpr const v3_tuid v3_component_iid = diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index a5facf02..438a515c 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -42,10 +42,10 @@ enum { struct v3_component_handler { struct v3_funknown; - V3_API v3_result (*begin_edit)(void* self, v3_param_id); - V3_API v3_result (*perform_edit)(void* self, v3_param_id, double value_normalised); - V3_API v3_result (*end_edit)(void* self, v3_param_id); - V3_API v3_result (*restart_component)(void* self, int32_t flags); + v3_result (V3_API *begin_edit)(void* self, v3_param_id); + v3_result (V3_API *perform_edit)(void* self, v3_param_id, double value_normalised); + v3_result (V3_API *end_edit)(void* self, v3_param_id); + v3_result (V3_API *restart_component)(void* self, int32_t flags); }; static constexpr const v3_tuid v3_component_handler_iid = @@ -79,19 +79,19 @@ struct v3_param_info { struct v3_edit_controller { struct v3_plugin_base; - V3_API v3_result (*set_component_state)(void* self, struct v3_bstream*); - V3_API v3_result (*set_state)(void* self, struct v3_bstream*); - V3_API v3_result (*get_state)(void* self, struct v3_bstream*); - V3_API int32_t (*get_parameter_count)(void* self); - V3_API v3_result (*get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); - V3_API v3_result (*get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); - V3_API v3_result (*get_parameter_value_for_string)(void* self, v3_param_id, int16_t* input, double* output); - V3_API double (*normalised_parameter_to_plain)(void* self, v3_param_id, double normalised); - V3_API double (*plain_parameter_to_normalised)(void* self, v3_param_id, double plain); - V3_API double (*get_parameter_normalised)(void* self, v3_param_id); - V3_API v3_result (*set_parameter_normalised)(void* self, v3_param_id, double normalised); - V3_API v3_result (*set_component_handler)(void* self, struct v3_component_handler**); - V3_API struct v3_plugin_view **(*create_view)(void* self, const char* name); + v3_result (V3_API *set_component_state)(void* self, struct v3_bstream*); + v3_result (V3_API *set_state)(void* self, struct v3_bstream*); + v3_result (V3_API *get_state)(void* self, struct v3_bstream*); + int32_t (V3_API *get_parameter_count)(void* self); + v3_result (V3_API *get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); + v3_result (V3_API *get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); + v3_result (V3_API *get_parameter_value_for_string)(void* self, v3_param_id, int16_t* input, double* output); + double (V3_API *normalised_parameter_to_plain)(void* self, v3_param_id, double normalised); + double (V3_API *plain_parameter_to_normalised)(void* self, v3_param_id, double plain); + double (V3_API *get_parameter_normalised)(void* self, v3_param_id); + v3_result (V3_API *set_parameter_normalised)(void* self, v3_param_id, double normalised); + v3_result (V3_API *set_component_handler)(void* self, struct v3_component_handler**); + struct v3_plugin_view** (V3_API *create_view)(void* self, const char* name); }; static constexpr const v3_tuid v3_edit_controller_iid = diff --git a/distrho/src/travesty/events.h b/distrho/src/travesty/events.h index 95fed04d..52cf78c1 100644 --- a/distrho/src/travesty/events.h +++ b/distrho/src/travesty/events.h @@ -137,9 +137,9 @@ struct v3_event { struct v3_event_list { struct v3_funknown; - V3_API uint32_t (*get_event_count)(void* self); - V3_API v3_result (*get_event)(void* self, int32_t idx, struct v3_event* event); - V3_API v3_result (*add_event)(void* self, struct v3_event* event); + uint32_t (V3_API *get_event_count)(void* self); + v3_result (V3_API *get_event)(void* self, int32_t idx, struct v3_event* event); + v3_result (V3_API *add_event)(void* self, struct v3_event* event); }; static constexpr const v3_tuid v3_event_list_iid = diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index 1cf233e0..db3f46be 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -39,10 +39,10 @@ struct v3_class_info { struct v3_plugin_factory { struct v3_funknown; - V3_API v3_result (*get_factory_info)(void* self, struct v3_factory_info*); - V3_API int32_t (*num_classes)(void* self); - V3_API v3_result (*get_class_info)(void* self, int32_t idx, struct v3_class_info*); - V3_API v3_result (*create_instance)(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance); + v3_result (V3_API *get_factory_info)(void* self, struct v3_factory_info*); + int32_t (V3_API *num_classes)(void* self); + v3_result (V3_API *get_class_info)(void* self, int32_t idx, struct v3_class_info*); + v3_result (V3_API *create_instance)(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance); }; static constexpr const v3_tuid v3_plugin_factory_iid = @@ -67,7 +67,7 @@ struct v3_class_info_2 { struct v3_plugin_factory_2 { struct v3_plugin_factory; - V3_API v3_result (*get_class_info_2)(void* self, int32_t idx, struct v3_class_info_2*); + v3_result (V3_API *get_class_info_2)(void* self, int32_t idx, struct v3_class_info_2*); }; static constexpr const v3_tuid v3_plugin_factory_2_iid = @@ -95,8 +95,8 @@ struct v3_class_info_3 { struct v3_plugin_factory_3 { struct v3_plugin_factory_2; - V3_API v3_result (*get_class_info_utf16)(void* self, int32_t idx, struct v3_class_info_3*); - V3_API v3_result (*set_host_context)(void* self, struct v3_funknown* host); + v3_result (V3_API *get_class_info_utf16)(void* self, int32_t idx, struct v3_class_info_3*); + v3_result (V3_API *set_host_context)(void* self, struct v3_funknown* host); }; static constexpr const v3_tuid v3_plugin_factory_3_iid = diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h index 6f66adff..fa013198 100644 --- a/distrho/src/travesty/message.h +++ b/distrho/src/travesty/message.h @@ -27,14 +27,14 @@ struct v3_attribute_list { struct v3_funknown; - V3_API v3_result (*set_int)(void* self, const char* id, int64_t value); - V3_API v3_result (*get_int)(void* self, const char* id, int64_t* value); - V3_API v3_result (*set_float)(void* self, const char* id, double value); - V3_API v3_result (*get_float)(void* self, const char* id, double* value); - V3_API v3_result (*set_string)(void* self, const char* id, const int16_t* string); - V3_API v3_result (*get_string)(void* self, const char* id, int16_t* string, uint32_t size); - V3_API v3_result (*set_binary)(void* self, const char* id, const void* data, uint32_t size); - V3_API v3_result (*get_binary)(void* self, const char* id, const void** data, uint32_t* size); + v3_result (V3_API *set_int)(void* self, const char* id, int64_t value); + v3_result (V3_API *get_int)(void* self, const char* id, int64_t* value); + v3_result (V3_API *set_float)(void* self, const char* id, double value); + v3_result (V3_API *get_float)(void* self, const char* id, double* value); + v3_result (V3_API *set_string)(void* self, const char* id, const int16_t* string); + v3_result (V3_API *get_string)(void* self, const char* id, int16_t* string, uint32_t size); + v3_result (V3_API *set_binary)(void* self, const char* id, const void* data, uint32_t size); + v3_result (V3_API *get_binary)(void* self, const char* id, const void** data, uint32_t* size); }; static constexpr const v3_tuid v3_attribute_list_iid = @@ -47,9 +47,9 @@ static constexpr const v3_tuid v3_attribute_list_iid = struct v3_message { struct v3_funknown; - V3_API const char* (*get_message_id)(void* self); - V3_API void (*set_message_id)(void* self, const char* id); - V3_API v3_attribute_list** (*get_attributes)(void* self); + const char* (V3_API *get_message_id)(void* self); + void (V3_API *set_message_id)(void* self, const char* id); + v3_attribute_list** (V3_API *get_attributes)(void* self); }; static constexpr const v3_tuid v3_message_iid = @@ -62,9 +62,9 @@ static constexpr const v3_tuid v3_message_iid = struct v3_connection_point { struct v3_funknown; - V3_API v3_result (*connect)(void* self, struct v3_connection_point** other); - V3_API v3_result (*disconnect)(void* self, struct v3_connection_point** other); - V3_API v3_result (*notify)(void* self, struct v3_message** message); + v3_result (V3_API *connect)(void* self, struct v3_connection_point** other); + v3_result (V3_API *disconnect)(void* self, struct v3_connection_point** other); + v3_result (V3_API *notify)(void* self, struct v3_message** message); }; static constexpr const v3_tuid v3_connection_point_iid = diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index 264b830e..fc7b2ddb 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -50,18 +50,18 @@ struct v3_plugin_frame; struct v3_plugin_view { struct v3_funknown; - V3_API v3_result (*is_platform_type_supported)(void* self, const char* platform_type); - V3_API v3_result (*attached)(void* self, void* parent, const char* platform_type); - V3_API v3_result (*removed)(void* self); - V3_API v3_result (*on_wheel)(void* self, float distance); - V3_API v3_result (*on_key_down)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); - V3_API v3_result (*on_key_up)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); - V3_API v3_result (*get_size)(void* self, struct v3_view_rect*); - V3_API v3_result (*on_size)(void* self, struct v3_view_rect*); - V3_API v3_result (*on_focus)(void* self, v3_bool state); - V3_API v3_result (*set_frame)(void* self, struct v3_plugin_frame**); - V3_API v3_result (*can_resize)(void* self); - V3_API v3_result (*check_size_constraint)(void* self, struct v3_view_rect*); + v3_result (V3_API *is_platform_type_supported)(void* self, const char* platform_type); + v3_result (V3_API *attached)(void* self, void* parent, const char* platform_type); + v3_result (V3_API *removed)(void* self); + v3_result (V3_API *on_wheel)(void* self, float distance); + v3_result (V3_API *on_key_down)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); + v3_result (V3_API *on_key_up)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); + v3_result (V3_API *get_size)(void* self, struct v3_view_rect*); + v3_result (V3_API *on_size)(void* self, struct v3_view_rect*); + v3_result (V3_API *on_focus)(void* self, v3_bool state); + v3_result (V3_API *set_frame)(void* self, struct v3_plugin_frame**); + v3_result (V3_API *can_resize)(void* self); + v3_result (V3_API *check_size_constraint)(void* self, struct v3_view_rect*); }; static constexpr const v3_tuid v3_plugin_view_iid = @@ -74,7 +74,7 @@ static constexpr const v3_tuid v3_plugin_view_iid = struct v3_plugin_frame { struct v3_funknown; - V3_API v3_result (*resize_view)(void* self, struct v3_plugin_view**, struct v3_view_rect*); + v3_result (V3_API *resize_view)(void* self, struct v3_plugin_view**, struct v3_view_rect*); }; static constexpr const v3_tuid v3_plugin_frame_iid = @@ -88,7 +88,7 @@ static constexpr const v3_tuid v3_plugin_frame_iid = struct v3_plugin_view_content_scale { struct v3_funknown; - V3_API v3_result (*set_content_scale_factor)(void* self, float factor); + v3_result (V3_API *set_content_scale_factor)(void* self, float factor); }; static constexpr const v3_tuid v3_plugin_view_content_scale_iid = @@ -101,7 +101,7 @@ static constexpr const v3_tuid v3_plugin_view_content_scale_iid = struct v3_plugin_view_parameter_finder { struct v3_funknown; - V3_API v3_result (*find_parameter)(void* self, int32_t x, int32_t y, v3_param_id *); + v3_result (V3_API *find_parameter)(void* self, int32_t x, int32_t y, v3_param_id *); }; static constexpr const v3_tuid v3_plugin_view_parameter_finder_iid = diff --git a/examples/Info/CMakeLists.txt b/examples/Info/CMakeLists.txt index 9efc9620..b82b15dc 100644 --- a/examples/Info/CMakeLists.txt +++ b/examples/Info/CMakeLists.txt @@ -2,7 +2,7 @@ # ------------------------------ # dpf_add_plugin(d_info - TARGETS jack lv2 vst2 + TARGETS jack lv2 vst2 vst3 FILES_DSP InfoExamplePlugin.cpp FILES_UI diff --git a/examples/Latency/CMakeLists.txt b/examples/Latency/CMakeLists.txt index fd0ddcb8..b7ea09e6 100644 --- a/examples/Latency/CMakeLists.txt +++ b/examples/Latency/CMakeLists.txt @@ -2,7 +2,7 @@ # ------------------------------ # dpf_add_plugin(d_latency - TARGETS ladspa lv2 vst2 + TARGETS ladspa lv2 vst2 vst3 FILES_DSP LatencyExamplePlugin.cpp) diff --git a/examples/Meters/CMakeLists.txt b/examples/Meters/CMakeLists.txt index 0d7807a4..325d5038 100644 --- a/examples/Meters/CMakeLists.txt +++ b/examples/Meters/CMakeLists.txt @@ -2,7 +2,7 @@ # ------------------------------ # dpf_add_plugin(d_meters - TARGETS jack dssi lv2 vst2 + TARGETS jack dssi lv2 vst2 vst3 FILES_DSP ExamplePluginMeters.cpp FILES_UI diff --git a/examples/MidiThrough/CMakeLists.txt b/examples/MidiThrough/CMakeLists.txt index 38facaa5..6c86c7c5 100644 --- a/examples/MidiThrough/CMakeLists.txt +++ b/examples/MidiThrough/CMakeLists.txt @@ -2,7 +2,7 @@ # ------------------------------ # dpf_add_plugin(d_midiThrough - TARGETS jack lv2 vst2 + TARGETS jack lv2 vst2 vst3 FILES_DSP MidiThroughExamplePlugin.cpp) diff --git a/examples/Parameters/CMakeLists.txt b/examples/Parameters/CMakeLists.txt index 4d1e77e9..98cc1eab 100644 --- a/examples/Parameters/CMakeLists.txt +++ b/examples/Parameters/CMakeLists.txt @@ -2,7 +2,7 @@ # ------------------------------ # dpf_add_plugin(d_parameters - TARGETS jack ladspa dssi lv2 vst2 + TARGETS jack ladspa dssi lv2 vst2 vst3 FILES_DSP ExamplePluginParameters.cpp FILES_UI diff --git a/examples/States/CMakeLists.txt b/examples/States/CMakeLists.txt index fdbb2f25..8fb4bfc1 100644 --- a/examples/States/CMakeLists.txt +++ b/examples/States/CMakeLists.txt @@ -2,7 +2,7 @@ # ------------------------------ # dpf_add_plugin(d_states - TARGETS jack lv2 vst2 + TARGETS jack lv2 vst2 vst3 FILES_DSP ExamplePluginStates.cpp FILES_UI From decc551c2815bfed86d3657d2542b019e50faebd Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:10:24 +0100 Subject: [PATCH 107/504] Start some files to describe features and licensing Signed-off-by: falkTX --- FEATURES.md | 24 ++++++++++++++++++++++++ LICENSING.md | 16 ++++++++++++++++ distrho/src/dssi/seq_event-compat.h | 10 ++++++++++ 3 files changed, 50 insertions(+) create mode 100644 FEATURES.md create mode 100644 LICENSING.md diff --git a/FEATURES.md b/FEATURES.md new file mode 100644 index 00000000..e160a58a --- /dev/null +++ b/FEATURES.md @@ -0,0 +1,24 @@ +# DPF - DISTRHO Plugin Framework + +This file describes the available features for each plugin format. +The limitations could be due to the plugin format itself or within DPF. + +Format | Audio IO | Parameters | Parameter outputs | Programs | States | UI->DSP sendNote | CV ports | MIDI input | MIDI output | Port groups | Special notes +JACK | Yes | Yes* | Yes* | Yes* | Yes | Yes | Yes | Yes | Yes | Yes* | Parameters have programs mapped to MIDI CC, there is no generic plugin editor; Port groups as JACK metadata +LADSPA | Yes | Yes | Yes | No* | No | No | No | No | No | No | LADSPA only supports basic parameters and audio; Programs could be done via LRDF but not supported in DPF +DSSI | Yes | Yes | Yes | Yes* | Yes* | Yes | No | Yes | No | No | DSSI only supports States via UI, no "full state" possible +LV2 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Everything supported :) +VST2 | Yes | Yes | No | No* | Yes | Yes | No | Yes | Yes | Yes* | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF; Parameter groups are supported, but not for audio ports (per VST2 spec limitations) +VST3 | Yes | Yes | Yes? | Yes | Yes | Yes | NO* | Yes | Yes | No* | Not sure if parameter outputs work (aka "read-only" on VST3); CV ports do not support custom ranges, not implemented yet; Port groups not implemented yet + +A few notes for things to add to the table: + - Custom UI (embed, external, both or none) + - Host/user-side UI resize + - Parameter inputs change from plugin-side + - Sidechain tagged audio ports + - Trigger parameters + - UI background/foreground color + - Host-side file browser + - Remote/Instance access + - Host-mapped bypass parameter + - Time position diff --git a/LICENSING.md b/LICENSING.md new file mode 100644 index 00000000..82c2d5ab --- /dev/null +++ b/LICENSING.md @@ -0,0 +1,16 @@ +# DPF - DISTRHO Plugin Framework + +Even though DPF is quite liberally licensed, not all plugin formats follow the same ideals. +This usually due to plugin APIs/headers being tied to a specific license or having commercial restrictions. +This file describes the licensing that applies to each individual plugin format as a way to make it clear what is possible and compatible. +Note that if you are making GPLv2+ licensed plugins this does not apply to you, as so far everything is GPLv2+ compatible. + +Target | License(s) | License restrictions | Attribution +JACK/Standalone | ISC (JACK bridge methods) + ?? (RtAudio) | Copyright attribution | RtAudio: 2001-2019 Gary P. Scavone +LADSPA | LGPLv2.1+ | ??? | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld +DSSI | LGPLv2.1+ | ??? | ALSA: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai; DSSI: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton +LV2 | ISC + GPL? (atom-helpers.h not sure if used) | Copyright attribution | TODO, check all headers used +VST2 | GPLv2+ or commercial* | Must also be GPLv2+ or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | Same or later license if GPL; Alternatively, custom agreement with Steingberg +VST3 | ISC | Copyright attribution + +Regardless of target format, DPF itself needs to be mentioned in attribution. See LICENSE file for copyright details. diff --git a/distrho/src/dssi/seq_event-compat.h b/distrho/src/dssi/seq_event-compat.h index 6619c23e..ec75f70c 100644 --- a/distrho/src/dssi/seq_event-compat.h +++ b/distrho/src/dssi/seq_event-compat.h @@ -1,3 +1,13 @@ +/** + * \file include/seq_event.h + * \brief Application interface library for the ALSA driver + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \author Takashi Iwai + * \date 1998-2001 + * + * Application interface library for the ALSA driver + */ /* * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as From b934f54d50a358293b3783d64b012fcc30a57c55 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:17:06 +0100 Subject: [PATCH 108/504] Fix formatting Signed-off-by: falkTX --- FEATURES.md | 15 ++++++++------- LICENSING.md | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index e160a58a..d43faaba 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -3,13 +3,14 @@ This file describes the available features for each plugin format. The limitations could be due to the plugin format itself or within DPF. -Format | Audio IO | Parameters | Parameter outputs | Programs | States | UI->DSP sendNote | CV ports | MIDI input | MIDI output | Port groups | Special notes -JACK | Yes | Yes* | Yes* | Yes* | Yes | Yes | Yes | Yes | Yes | Yes* | Parameters have programs mapped to MIDI CC, there is no generic plugin editor; Port groups as JACK metadata -LADSPA | Yes | Yes | Yes | No* | No | No | No | No | No | No | LADSPA only supports basic parameters and audio; Programs could be done via LRDF but not supported in DPF -DSSI | Yes | Yes | Yes | Yes* | Yes* | Yes | No | Yes | No | No | DSSI only supports States via UI, no "full state" possible -LV2 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Everything supported :) -VST2 | Yes | Yes | No | No* | Yes | Yes | No | Yes | Yes | Yes* | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF; Parameter groups are supported, but not for audio ports (per VST2 spec limitations) -VST3 | Yes | Yes | Yes? | Yes | Yes | Yes | NO* | Yes | Yes | No* | Not sure if parameter outputs work (aka "read-only" on VST3); CV ports do not support custom ranges, not implemented yet; Port groups not implemented yet +| Format | Audio IO | Parameters | Parameter outputs | Programs | States | UI->DSP sendNote | CV ports | MIDI input | MIDI output | Port groups | Special notes | +|--------|----------|------------|-------------------|----------|--------|------------------|----------|------------|-------------|-------------|---------------| +| JACK/Standalone | Yes | Yes* | Yes* | Yes* | Yes | Yes | Yes | Yes | Yes | Yes* | Parameters have programs mapped to MIDI CC, there is no generic plugin editor; Port groups as JACK metadata | +| LADSPA | Yes | Yes | Yes | No* | No | No | No | No | No | No | LADSPA only supports basic parameters and audio; Programs could be done via LRDF but not supported in DPF | +| DSSI | Yes | Yes | Yes | Yes* | Yes* | Yes | No | Yes | No | No | DSSI only supports States via UI, no "full state" possible | +| LV2 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Everything supported :) | +| VST2 | Yes | Yes | No | No* | Yes | Yes | No | Yes | Yes | Yes* | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF; Parameter groups are supported, but not for audio ports (per VST2 spec limitations) | +| VST3 | Yes | Yes | Yes? | Yes | Yes | Yes | NO* | Yes | Yes | No* | Not sure if parameter outputs work (aka "read-only" on VST3); CV ports do not support custom ranges, not implemented yet; Port groups not implemented yet | A few notes for things to add to the table: - Custom UI (embed, external, both or none) diff --git a/LICENSING.md b/LICENSING.md index 82c2d5ab..87a35435 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -5,12 +5,13 @@ This usually due to plugin APIs/headers being tied to a specific license or havi This file describes the licensing that applies to each individual plugin format as a way to make it clear what is possible and compatible. Note that if you are making GPLv2+ licensed plugins this does not apply to you, as so far everything is GPLv2+ compatible. -Target | License(s) | License restrictions | Attribution -JACK/Standalone | ISC (JACK bridge methods) + ?? (RtAudio) | Copyright attribution | RtAudio: 2001-2019 Gary P. Scavone -LADSPA | LGPLv2.1+ | ??? | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld -DSSI | LGPLv2.1+ | ??? | ALSA: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai; DSSI: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton -LV2 | ISC + GPL? (atom-helpers.h not sure if used) | Copyright attribution | TODO, check all headers used -VST2 | GPLv2+ or commercial* | Must also be GPLv2+ or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | Same or later license if GPL; Alternatively, custom agreement with Steingberg -VST3 | ISC | Copyright attribution +| Target | License(s) | License restrictions | Attribution | +|--------|------------|----------------------|-------------| +| JACK/Standalone | ISC (JACK bridge methods) + ?? (RtAudio) | Copyright attribution | RtAudio: 2001-2019 Gary P. Scavone | +| LADSPA | LGPLv2.1+ | ??? | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | +| DSSI | LGPLv2.1+ | ??? | ALSA: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai; DSSI: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton | +| LV2 | ISC + GPL? (atom-helpers.h not sure if used) | Copyright attribution | TODO, check all headers used | +| VST2 | GPLv2+ or commercial* | Must also be GPLv2+ or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | Same or later license if GPL; Alternatively, custom agreement with Steingberg | +| VST3 | ISC | Copyright attribution | DPF | Regardless of target format, DPF itself needs to be mentioned in attribution. See LICENSE file for copyright details. From 99b5d507925f6ac9d3d4cc7796e5ece78af612e3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:27:08 +0100 Subject: [PATCH 109/504] Cleanup FEATURES.md, still WIP Signed-off-by: falkTX --- FEATURES.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index d43faaba..25b05b51 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -3,16 +3,28 @@ This file describes the available features for each plugin format. The limitations could be due to the plugin format itself or within DPF. -| Format | Audio IO | Parameters | Parameter outputs | Programs | States | UI->DSP sendNote | CV ports | MIDI input | MIDI output | Port groups | Special notes | -|--------|----------|------------|-------------------|----------|--------|------------------|----------|------------|-------------|-------------|---------------| -| JACK/Standalone | Yes | Yes* | Yes* | Yes* | Yes | Yes | Yes | Yes | Yes | Yes* | Parameters have programs mapped to MIDI CC, there is no generic plugin editor; Port groups as JACK metadata | -| LADSPA | Yes | Yes | Yes | No* | No | No | No | No | No | No | LADSPA only supports basic parameters and audio; Programs could be done via LRDF but not supported in DPF | -| DSSI | Yes | Yes | Yes | Yes* | Yes* | Yes | No | Yes | No | No | DSSI only supports States via UI, no "full state" possible | -| LV2 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Everything supported :) | -| VST2 | Yes | Yes | No | No* | Yes | Yes | No | Yes | Yes | Yes* | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF; Parameter groups are supported, but not for audio ports (per VST2 spec limitations) | -| VST3 | Yes | Yes | Yes? | Yes | Yes | Yes | NO* | Yes | Yes | No* | Not sure if parameter outputs work (aka "read-only" on VST3); CV ports do not support custom ranges, not implemented yet; Port groups not implemented yet | +| Format | Param outs | Programs | States | UI sendNote | CV | MIDI in | MIDI out | Port groups | +|--------|------------|----------|--------|-------------|----|---------|----------|-------------| +| JACK | Yes* | Yes* | Yes | Yes | Yes | Yes | Yes | Yes* | +| LADSPA | Yes | No* | No | No | No | No | No | No | +| DSSI | Yes | Yes* | Yes* | Yes | No | Yes | No | No | +| LV2 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| VST2 | No | No* | Yes | Yes | No | Yes | Yes | Yes* | +| VST3 | Yes? | Yes | Yes | Yes | NO* | Yes | Yes | No* | + +Special notes: + +| Format | Notes | +|--------|-------| +| JACK | Parameters have programs mapped to MIDI CC, there is no generic plugin editor; Port groups as JACK metadata | +| LADSPA | LADSPA only supports basic parameters and audio;
Programs could be done via LRDF but not supported in DPF | +| DSSI | DSSI only supports States via UI, no "full state" possible | +| LV2 | Everything supported :) | +| VST2 | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF;
Parameter groups are supported, but not for audio ports (per VST2 spec limitations) | +| VST3 | Not sure if parameter outputs work (aka "read-only" on VST3);
CV ports do not support custom ranges, not implemented yet;
Port groups not implemented yet | A few notes for things to add to the table: + - Custom UI (embed, external, both or none) - Host/user-side UI resize - Parameter inputs change from plugin-side From d46109f4817b420c9f10299e3ce475a0d5aa3e0e Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:36:23 +0100 Subject: [PATCH 110/504] Remove unused LV2 headers, they are GPL licensed Signed-off-by: falkTX --- distrho/src/lv2/atom-helpers.h | 249 ---------------------------- distrho/src/lv2/lv2-midifunctions.h | 98 ----------- distrho/src/lv2/lv2-miditype.h | 175 ------------------- 3 files changed, 522 deletions(-) delete mode 100644 distrho/src/lv2/atom-helpers.h delete mode 100644 distrho/src/lv2/lv2-midifunctions.h delete mode 100644 distrho/src/lv2/lv2-miditype.h diff --git a/distrho/src/lv2/atom-helpers.h b/distrho/src/lv2/atom-helpers.h deleted file mode 100644 index 97cef2c3..00000000 --- a/distrho/src/lv2/atom-helpers.h +++ /dev/null @@ -1,249 +0,0 @@ -// lv2_atom_helpers.h -// -/**************************************************************************** - Copyright (C) 2005-2013, rncbc aka Rui Nuno Capela. All rights reserved. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -*****************************************************************************/ - -/* Helper functions for LV2 atom:Sequence event buffer. - * - * tentatively adapted from: - * - * - lv2_evbuf.h,c - An abstract/opaque LV2 event buffer implementation. - * - * - event-helpers.h - Helper functions for the LV2 Event extension. - * - * - * Copyright 2008-2012 David Robillard - */ - -#ifndef LV2_ATOM_HELPERS_H -#define LV2_ATOM_HELPERS_H - -#include -#include -#include -#include -#include - -#include "atom-util.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// An abstract/opaque LV2 atom:Sequence buffer. -// -typedef -struct _LV2_Atom_Buffer -{ - uint32_t capacity; - uint32_t chunk_type; - uint32_t sequence_type; - LV2_Atom_Sequence atoms; - -} LV2_Atom_Buffer; - - -// Clear and initialize an existing LV2 atom:Sequenece buffer. -// -static inline -void lv2_atom_buffer_reset ( LV2_Atom_Buffer *buf, bool input ) -{ - if (input) { - buf->atoms.atom.size = sizeof(LV2_Atom_Sequence_Body); - buf->atoms.atom.type = buf->sequence_type; - } else { - buf->atoms.atom.size = buf->capacity; - buf->atoms.atom.type = buf->chunk_type; - } -} - - -// Allocate a new, empty LV2 atom:Sequence buffer. -// -static inline -LV2_Atom_Buffer *lv2_atom_buffer_new ( - uint32_t capacity, uint32_t chunk_type, uint32_t sequence_type, bool input ) -{ - LV2_Atom_Buffer *buf = (LV2_Atom_Buffer *) - malloc(sizeof(LV2_Atom_Buffer) + sizeof(LV2_Atom_Sequence) + capacity); - - buf->capacity = capacity; - buf->chunk_type = chunk_type; - buf->sequence_type = sequence_type; - - lv2_atom_buffer_reset(buf, input); - - return buf; -} - - -// Free an LV2 atom:Sequenece buffer allocated with lv2_atome_buffer_new. -// -static inline -void lv2_atom_buffer_free ( LV2_Atom_Buffer *buf ) -{ - free(buf); -} - - -// Return the total padded size of events stored in a LV2 atom:Sequence buffer. -// -static inline -uint32_t lv2_atom_buffer_get_size ( LV2_Atom_Buffer *buf ) -{ - if (buf->atoms.atom.type == buf->sequence_type) - return buf->atoms.atom.size - uint32_t(sizeof(LV2_Atom_Sequence_Body)); - else - return 0; -} - - -// Return the actual LV2 atom:Sequence implementation. -// -static inline -LV2_Atom_Sequence *lv2_atom_buffer_get_sequence ( LV2_Atom_Buffer *buf ) -{ - return &buf->atoms; -} - - -// An iterator over an atom:Sequence buffer. -// -typedef -struct _LV2_Atom_Buffer_Iterator -{ - LV2_Atom_Buffer *buf; - uint32_t offset; - -} LV2_Atom_Buffer_Iterator; - - -// Reset an iterator to point to the start of an LV2 atom:Sequence buffer. -// -static inline -bool lv2_atom_buffer_begin ( - LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *buf ) -{ - iter->buf = buf; - iter->offset = 0; - - return (buf->atoms.atom.size > 0); -} - - -// Reset an iterator to point to the end of an LV2 atom:Sequence buffer. -// -static inline -bool lv2_atom_buffer_end ( - LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *buf ) -{ - iter->buf = buf; - iter->offset = lv2_atom_pad_size(lv2_atom_buffer_get_size(buf)); - - return (iter->offset < buf->capacity - sizeof(LV2_Atom_Event)); -} - - -// Check if a LV2 atom:Sequenece buffer iterator is valid. -// -static inline -bool lv2_atom_buffer_is_valid ( LV2_Atom_Buffer_Iterator *iter ) -{ - return iter->offset < lv2_atom_buffer_get_size(iter->buf); -} - - -// Advance a LV2 atom:Sequenece buffer iterator forward one event. -// -static inline -bool lv2_atom_buffer_increment ( LV2_Atom_Buffer_Iterator *iter ) -{ - if (!lv2_atom_buffer_is_valid(iter)) - return false; - - LV2_Atom_Buffer *buf = iter->buf; - LV2_Atom_Sequence *atoms = &buf->atoms; - uint32_t size = ((LV2_Atom_Event *) ((char *) - LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset))->body.size; - iter->offset += lv2_atom_pad_size(uint32_t(sizeof(LV2_Atom_Event)) + size); - - return true; -} - - -// Get the event currently pointed at a LV2 atom:Sequence buffer iterator. -// -static inline -LV2_Atom_Event *lv2_atom_buffer_get ( - LV2_Atom_Buffer_Iterator *iter, uint8_t **data ) -{ - if (!lv2_atom_buffer_is_valid(iter)) - return NULL; - - LV2_Atom_Buffer *buf = iter->buf; - LV2_Atom_Sequence *atoms = &buf->atoms; - LV2_Atom_Event *ev = (LV2_Atom_Event *) ((char *) - LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset); - - *data = (uint8_t *) LV2_ATOM_BODY(&ev->body); - - return ev; -} - - -// Write an event at a LV2 atom:Sequence buffer iterator. - -static inline -bool lv2_atom_buffer_write ( - LV2_Atom_Buffer_Iterator *iter, - uint32_t frames, - uint32_t /*subframes*/, - uint32_t type, - uint32_t size, - const uint8_t *data ) -{ - LV2_Atom_Buffer *buf = iter->buf; - LV2_Atom_Sequence *atoms = &buf->atoms; - if (buf->capacity - sizeof(LV2_Atom) - atoms->atom.size - < sizeof(LV2_Atom_Event) + size) - return false; - - LV2_Atom_Event *ev = (LV2_Atom_Event*) ((char *) - LV2_ATOM_CONTENTS(LV2_Atom_Sequence, atoms) + iter->offset); - - ev->time.frames = frames; - ev->body.type = type; - ev->body.size = size; - - memcpy(LV2_ATOM_BODY(&ev->body), data, size); - - size = lv2_atom_pad_size(uint32_t(sizeof(LV2_Atom_Event)) + size); - atoms->atom.size += size; - iter->offset += size; - - return true; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif // LV2_ATOM_HELPERS_H - -// end of lv2_atom_helpers.h diff --git a/distrho/src/lv2/lv2-midifunctions.h b/distrho/src/lv2/lv2-midifunctions.h deleted file mode 100644 index d068f498..00000000 --- a/distrho/src/lv2/lv2-midifunctions.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** - - lv2-midifunctions.h - support file for using MIDI in LV2 plugins - - Copyright (C) 2006 Lars Luthman - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - -****************************************************************************/ - -#ifndef LV2_MIDIFUNCTIONS -#define LV2_MIDIFUNCTIONS - -#include "lv2-miditype.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - LV2_MIDI* midi; - uint32_t frame_count; - uint32_t position; -} LV2_MIDIState; - - -inline double lv2midi_get_event(LV2_MIDIState* state, - double* timestamp, - uint32_t* size, - unsigned char** data) { - - if (state->position >= state->midi->size) { - state->position = state->midi->size; - *timestamp = state->frame_count; - *size = 0; - *data = NULL; - return *timestamp; - } - - *timestamp = *(double*)(state->midi->data + state->position); - *size = (uint32_t)*(size_t*)(state->midi->data + state->position + sizeof(double)); - *data = state->midi->data + state->position + - sizeof(double) + sizeof(size_t); - return *timestamp; -} - - -inline double lv2midi_step(LV2_MIDIState* state) { - - if (state->position >= state->midi->size) { - state->position = state->midi->size; - return state->frame_count; - } - - state->position += (uint32_t)sizeof(double); - size_t size = *(size_t*)(state->midi->data + state->position); - state->position += (uint32_t)sizeof(size_t); - state->position += (uint32_t)size; - return *(double*)(state->midi->data + state->position); -} - - -inline void lv2midi_put_event(LV2_MIDIState* state, - double timestamp, - uint32_t size, - const unsigned char* data) { - - if (state->midi->size + sizeof(double) + sizeof(size_t) + size < state->midi->capacity) - { - *((double*)(state->midi->data + state->midi->size)) = timestamp; - state->midi->size += (uint32_t)sizeof(double); - *((size_t*)(state->midi->data + state->midi->size)) = size; - state->midi->size += (uint32_t)sizeof(size_t); - memcpy(state->midi->data + state->midi->size, data, size); - - state->midi->size += size; - state->midi->event_count++; - } -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif - diff --git a/distrho/src/lv2/lv2-miditype.h b/distrho/src/lv2/lv2-miditype.h deleted file mode 100644 index 7afcae8b..00000000 --- a/distrho/src/lv2/lv2-miditype.h +++ /dev/null @@ -1,175 +0,0 @@ -/**************************************************************************** - - lv2-miditype.h - header file for using MIDI in LV2 plugins - - Copyright (C) 2006 Lars Luthman - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - -****************************************************************************/ - -#ifndef LV2_MIDITYPE_H -#define LV2_MIDITYPE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** This data structure is used to contain the MIDI events for one run() - cycle. The port buffer for a LV2 port that has the datatype - should be a pointer - to an instance of this struct. - - To store two Note On events on MIDI channel 0 in a buffer, with timestamps - 12 and 35.5, you could use something like this code (assuming that - midi_data is a variable of type LV2_MIDI): - @code - - size_t buffer_offset = 0; - *(double*)(midi_data->data + buffer_offset) = 12; - buffer_offset += sizeof(double); - *(size_t*)(midi_data->data + buffer_offset) = 3; - buffer_offset += sizeof(size_t); - midi_data->data[buffer_offset++] = 0x90; - midi_data->data[buffer_offset++] = 0x48; - midi_data->data[buffer_offset++] = 0x64; - ++midi_data->event_count; - - *(double*)(midi_data->data + buffer_offset) = 35.5; - buffer_offset += sizeof(double); - *(size_t*)(midi_data->data + buffer_offset) = 3; - buffer_offset += sizeof(size_t); - midi_data->data[buffer_offset++] = 0x90; - midi_data->data[buffer_offset++] = 0x55; - midi_data->data[buffer_offset++] = 0x64; - ++midi_data->event_count; - - midi_data->size = buffer_offset; - - @endcode - - This would be done by the host in the case of an input port, and by the - plugin in the case of an output port. Whoever is writing events to the - buffer must also take care not to exceed the capacity of the data buffer. - - To read events from a buffer, you could do something like this: - @code - - size_t buffer_offset = 0; - uint32_t i; - for (i = 0; i < midi_data->event_count; ++i) { - double timestamp = *(double*)(midi_data->data + buffer_offset); - buffer_offset += sizeof(double); - size_t size = *(size_t*)(midi_data->data + buffer_offset); - buffer_offset += sizeof(size_t); - do_something_with_event(timestamp, size, - midi_data->data + buffer_offset); - buffer_offset += size; - } - - @endcode -*/ -typedef struct { - - /** The number of MIDI events in the data buffer. - INPUT PORTS: It's the host's responsibility to set this field to the - number of MIDI events contained in the data buffer before calling the - plugin's run() function. The plugin may not change this field. - OUTPUT PORTS: It's the plugin's responsibility to set this field to the - number of MIDI events it has stored in the data buffer before returning - from the run() function. Any initial value should be ignored by the - plugin. - */ - uint32_t event_count; - - /** The size of the data buffer in bytes. It is set by the host and may not - be changed by the plugin. The host is allowed to change this between - run() calls. - */ - uint32_t capacity; - - /** The size of the initial part of the data buffer that actually contains - data. - INPUT PORTS: It's the host's responsibility to set this field to the - number of bytes used by all MIDI events it has written to the buffer - (including timestamps and size fields) before calling the plugin's - run() function. The plugin may not change this field. - OUTPUT PORTS: It's the plugin's responsibility to set this field to - the number of bytes used by all MIDI events it has written to the - buffer (including timestamps and size fields) before returning from - the run() function. Any initial value should be ignored by the plugin. - */ - uint32_t size; - - /** The data buffer that is used to store MIDI events. The events are packed - after each other, and the format of each event is as follows: - - First there is a timestamp, which should have the type "double", - i.e. have the same bit size as a double and the same bit layout as a - double (whatever that is on the current platform). This timestamp gives - the offset from the beginning of the current cycle, in frames, that - the MIDI event occurs on. It must be strictly smaller than the 'nframes' - parameter to the current run() call. The MIDI events in the buffer must - be ordered by their timestamp, e.g. an event with a timestamp of 123.23 - must be stored after an event with a timestamp of 65.0. - - The second part of the event is a size field, which should have the type - "size_t" (as defined in the standard C header stddef.h). It should - contain the size of the MIDI data for this event, i.e. the number of - bytes used to store the actual MIDI event. The bytes used by the - timestamp and the size field should not be counted. - - The third part of the event is the actual MIDI data. There are some - requirements that must be followed: - - * Running status is not allowed. Every event must have its own status - byte. - * Note On events with velocity 0 are not allowed. These events are - equivalent to Note Off in standard MIDI streams, but in order to make - plugins and hosts easier to write, as well as more efficient, only - proper Note Off events are allowed as Note Off. - * "Realtime events" (status bytes 0xF8 to 0xFF) are allowed, but may not - occur inside other events like they are allowed to in hardware MIDI - streams. - * All events must be fully contained in a single data buffer, i.e. events - may not "wrap around" by storing the first few bytes in one buffer and - then wait for the next run() call to store the rest of the event. If - there isn't enough space in the current data buffer to store an event, - the event will either have to wait until next run() call, be ignored, - or compensated for in some more clever way. - * All events must be valid MIDI events. This means for example that - only the first byte in each event (the status byte) may have the eighth - bit set, that Note On and Note Off events are always 3 bytes long etc. - The MIDI writer (host or plugin) is responsible for writing valid MIDI - events to the buffer, and the MIDI reader (plugin or host) can assume - that all events are valid. - - On a platform where double is 8 bytes and size_t is 4 bytes, the data - buffer layout for a 3-byte event followed by a 4-byte event may look - something like this: - _______________________________________________________________ - | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ... - |TIMESTAMP 1 |SIZE 1 |DATA |TIMESTAMP 2 |SIZE 2 |DATA | ... - - */ - unsigned char* data; - -} LV2_MIDI; - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif From e96c8543a9e51546b9b71d0cca112743a5e0b952 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:39:32 +0100 Subject: [PATCH 111/504] Update LICENSING.md with more clear LV2 details Signed-off-by: falkTX --- LICENSING.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/LICENSING.md b/LICENSING.md index 87a35435..9c7df231 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -5,13 +5,14 @@ This usually due to plugin APIs/headers being tied to a specific license or havi This file describes the licensing that applies to each individual plugin format as a way to make it clear what is possible and compatible. Note that if you are making GPLv2+ licensed plugins this does not apply to you, as so far everything is GPLv2+ compatible. -| Target | License(s) | License restrictions | Attribution | -|--------|------------|----------------------|-------------| -| JACK/Standalone | ISC (JACK bridge methods) + ?? (RtAudio) | Copyright attribution | RtAudio: 2001-2019 Gary P. Scavone | -| LADSPA | LGPLv2.1+ | ??? | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | -| DSSI | LGPLv2.1+ | ??? | ALSA: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai; DSSI: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton | -| LV2 | ISC + GPL? (atom-helpers.h not sure if used) | Copyright attribution | TODO, check all headers used | -| VST2 | GPLv2+ or commercial* | Must also be GPLv2+ or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | Same or later license if GPL; Alternatively, custom agreement with Steingberg | -| VST3 | ISC | Copyright attribution | DPF | +Regardless of target format, DPF itself needs to be mentioned in attribution. +See the [LICENSE](LICENSE) file for copyright details. -Regardless of target format, DPF itself needs to be mentioned in attribution. See LICENSE file for copyright details. +| Target | License(s) | License restrictions | Attribution | +|-----------------|----------------------|-----------------------|-------------| +| JACK/Standalone | MIT (RtAudio) | Copyright attribution | RtAudio: 2001-2019 Gary P. Scavone | +| LADSPA | LGPLv2.1+ | ??? | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | +| DSSI | LGPLv2.1+ | ??? | DSSI: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
ALSA: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | +| LV2 | ISC | Copyright attribution | 2006-2020 Steve Harris, David Robillard;
2000-2002 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld | +| VST2 | GPLv2+ or commercial | Must be GPLv2+ compatible or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | GPLv2+ compatible license or custom agreement with Steingberg | +| VST3 | ISC | Copyright attribution | (none, only DPF files used) | From 26d558812fdad97213a313b9294f6818a379ebf9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:40:51 +0100 Subject: [PATCH 112/504] Minor styling changes Signed-off-by: falkTX --- LICENSING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSING.md b/LICENSING.md index 9c7df231..f54a4c6a 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -10,9 +10,9 @@ See the [LICENSE](LICENSE) file for copyright details. | Target | License(s) | License restrictions | Attribution | |-----------------|----------------------|-----------------------|-------------| -| JACK/Standalone | MIT (RtAudio) | Copyright attribution | RtAudio: 2001-2019 Gary P. Scavone | +| JACK/Standalone | MIT (RtAudio) | Copyright attribution | **RtAudio**: 2001-2019 Gary P. Scavone | | LADSPA | LGPLv2.1+ | ??? | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | -| DSSI | LGPLv2.1+ | ??? | DSSI: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
ALSA: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | +| DSSI | LGPLv2.1+ | ??? | **DSSI**: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
**ALSA**: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | | LV2 | ISC | Copyright attribution | 2006-2020 Steve Harris, David Robillard;
2000-2002 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld | | VST2 | GPLv2+ or commercial | Must be GPLv2+ compatible or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | GPLv2+ compatible license or custom agreement with Steingberg | | VST3 | ISC | Copyright attribution | (none, only DPF files used) | From a41c97e507d4d4fbd4582e9d67d8ba540ca0ee4e Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:47:57 +0100 Subject: [PATCH 113/504] Add special licensing note for LADSPA and DSSI Signed-off-by: falkTX --- LICENSING.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/LICENSING.md b/LICENSING.md index f54a4c6a..3e9f1f86 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -11,8 +11,21 @@ See the [LICENSE](LICENSE) file for copyright details. | Target | License(s) | License restrictions | Attribution | |-----------------|----------------------|-----------------------|-------------| | JACK/Standalone | MIT (RtAudio) | Copyright attribution | **RtAudio**: 2001-2019 Gary P. Scavone | -| LADSPA | LGPLv2.1+ | ??? | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | -| DSSI | LGPLv2.1+ | ??? | **DSSI**: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
**ALSA**: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | +| LADSPA | LGPLv2.1+ | ??? (*) | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | +| DSSI | LGPLv2.1+ | ??? (*) | **DSSI**: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
**ALSA**: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | | LV2 | ISC | Copyright attribution | 2006-2020 Steve Harris, David Robillard;
2000-2002 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld | | VST2 | GPLv2+ or commercial | Must be GPLv2+ compatible or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | GPLv2+ compatible license or custom agreement with Steingberg | | VST3 | ISC | Copyright attribution | (none, only DPF files used) | + +### LADSPA and DSSI special note + +The header files on LADSPA and DSSI are LGPLv2.1+ licensed, which is unusual for pure APIs without libraries. +LADSPA authors mention this on ladspa.org homepage: + +> LADSPA has been released under LGPL (GNU Lesser General Public License). +> This is not intended to be the final license for LADSPA. +> In the long term it is hoped that LADSPA will have a public license that is even less restrictive, so that commercial applications can use it (in a protected way) without having to use a derived LGPL library. +> It may be that LGPL is already free enough for this, but we aren't sure. + +So the situation for LADSPA/DSSI plugins is unclear for commercial plugins. +These formats are very limited and not much used anymore anyway, feel free to skip them if this situation is a potential issue for you. From f94b49f6592d96ab4cdbb02397f0a1ae01d3089e Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 12:54:15 +0100 Subject: [PATCH 114/504] Mention the new LICENSING.md file in the README Signed-off-by: falkTX --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0392faa0..18dbc657 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ Getting time information from the host is possible.
It uses the same format as the JACK Transport API, making porting some code easier.
+## Licensing + +DPF is released under ISC, which basically means you can do whatever you want as long as you credit the original authors. +Some plugin formats may have additional restrictions, see [LICENSING.md](LICENSING.md) for details. + + ## Help and documentation Bug reports happen on the [DPF github project](https://github.com/DISTRHO/DPF/issues). From ba2c5db4bbdc0da84f81ce745ce7861fb6a3b722 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 13:03:45 +0100 Subject: [PATCH 115/504] Be clear on the (small) imported juce code license Signed-off-by: falkTX --- distrho/extra/LeakDetector.hpp | 20 +++++++++++++++++++- distrho/extra/ScopedPointer.hpp | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/distrho/extra/LeakDetector.hpp b/distrho/extra/LeakDetector.hpp index 765e4676..11ede473 100644 --- a/distrho/extra/LeakDetector.hpp +++ b/distrho/extra/LeakDetector.hpp @@ -23,7 +23,25 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- // The following code was based from juce-core LeakDetector class -// Copyright (C) 2013 Raw Material Software Ltd. + +/** + Copyright (C) 2013 Raw Material Software Ltd. + + Permission is granted to use this software under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license/ + + 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 ISC DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS. IN NO EVENT SHALL ISC 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. +*/ /** A good old-fashioned C macro concatenation helper. This combines two items (which may themselves be macros) into a single string, diff --git a/distrho/extra/ScopedPointer.hpp b/distrho/extra/ScopedPointer.hpp index c756a1a8..6349199c 100644 --- a/distrho/extra/ScopedPointer.hpp +++ b/distrho/extra/ScopedPointer.hpp @@ -25,7 +25,25 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- // The following code was based from juce-core ScopedPointer class -// Copyright (C) 2013 Raw Material Software Ltd. + +/** + Copyright (C) 2013 Raw Material Software Ltd. + + Permission is granted to use this software under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license/ + + 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 ISC DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS. IN NO EVENT SHALL ISC 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. +*/ //============================================================================== /** From d9d016da8e3f85f962feb601324090f84c0c7a48 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 13:23:08 +0100 Subject: [PATCH 116/504] Add notes about VST2 and VST3 licensing Signed-off-by: falkTX --- LICENSING.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/LICENSING.md b/LICENSING.md index 3e9f1f86..bf2dadf2 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -8,8 +8,8 @@ Note that if you are making GPLv2+ licensed plugins this does not apply to you, Regardless of target format, DPF itself needs to be mentioned in attribution. See the [LICENSE](LICENSE) file for copyright details. -| Target | License(s) | License restrictions | Attribution | -|-----------------|----------------------|-----------------------|-------------| +| Target | License(s) | License restrictions | Additional attribution | +|-----------------|----------------------|-----------------------|------------------------| | JACK/Standalone | MIT (RtAudio) | Copyright attribution | **RtAudio**: 2001-2019 Gary P. Scavone | | LADSPA | LGPLv2.1+ | ??? (*) | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | | DSSI | LGPLv2.1+ | ??? (*) | **DSSI**: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
**ALSA**: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | @@ -29,3 +29,19 @@ LADSPA authors mention this on ladspa.org homepage: So the situation for LADSPA/DSSI plugins is unclear for commercial plugins. These formats are very limited and not much used anymore anyway, feel free to skip them if this situation is a potential issue for you. + +### VST2 special note + +By default DPF uses the free reverse-engineered [vestige header](distrho/src/vestige/vestige.h) file. +This file is GPLv2+ licensed, so that applies to plugins built with it as well. +You can alternatively build DPF-based VST2 plugins using the official VST2 SDK, +simply set the `VESTIGE_HEADER` compiler macro to `0` during build. +You will need to provide your own VST2 SDK files then, as DPF does not ship with them. +Note there are legal issues surrounding releasing new VST2 plugins using the official SDK, as that is no longer supported by the vendor. + +### VST3 special note + +Contrary to most plugins, DPF does not use the official VST3 SDK. +Instead, the API definitions are provided by the [travesty](distrho/src/travesty/) sub-project, licensed in the same way as DPF. +This allows us to freely build plugins without being encumbered by restrictive licensing deals. +It makes the internal implementation much harder for DPF, but this is not an issue for external developers. From cb1185d56dcaa4aefcf02e21129a0d86a5424aa7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 13:26:09 +0100 Subject: [PATCH 117/504] Fix a typo Signed-off-by: falkTX --- LICENSING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSING.md b/LICENSING.md index bf2dadf2..0c51af72 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -14,7 +14,7 @@ See the [LICENSE](LICENSE) file for copyright details. | LADSPA | LGPLv2.1+ | ??? (*) | 2000-2002 Richard W. E. Furse, Paul Barton-Davis, Stefan Westerfeld | | DSSI | LGPLv2.1+ | ??? (*) | **DSSI**: 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton;
**ALSA**: 1998-2001 Jaroslav Kysela, Abramo Bagnara, Takashi Iwai | | LV2 | ISC | Copyright attribution | 2006-2020 Steve Harris, David Robillard;
2000-2002 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld | -| VST2 | GPLv2+ or commercial | Must be GPLv2+ compatible or alternatively use Steingberg VST2 SDK (no longer available for new plugins) | GPLv2+ compatible license or custom agreement with Steingberg | +| VST2 | GPLv2+ or commercial | Must be GPLv2+ compatible or alternatively use Steinberg VST2 SDK (no longer available for new plugins) | GPLv2+ compatible license or custom agreement with Steinberg | | VST3 | ISC | Copyright attribution | (none, only DPF files used) | ### LADSPA and DSSI special note @@ -34,10 +34,10 @@ These formats are very limited and not much used anymore anyway, feel free to sk By default DPF uses the free reverse-engineered [vestige header](distrho/src/vestige/vestige.h) file. This file is GPLv2+ licensed, so that applies to plugins built with it as well. -You can alternatively build DPF-based VST2 plugins using the official VST2 SDK, +You can alternatively build DPF-based VST2 plugins using the official Steinberg VST2 SDK, simply set the `VESTIGE_HEADER` compiler macro to `0` during build. You will need to provide your own VST2 SDK files then, as DPF does not ship with them. -Note there are legal issues surrounding releasing new VST2 plugins using the official SDK, as that is no longer supported by the vendor. +Note there are legal issues surrounding releasing new VST2 plugins using the official SDK, as that is no longer supported by Steinberg. ### VST3 special note From 1ec105cbc420eeee93db1c0e99101b0b83880486 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 13:31:57 +0100 Subject: [PATCH 118/504] Grammar is hard Signed-off-by: falkTX --- LICENSING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSING.md b/LICENSING.md index 0c51af72..ad0a4c40 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -1,7 +1,7 @@ # DPF - DISTRHO Plugin Framework Even though DPF is quite liberally licensed, not all plugin formats follow the same ideals. -This usually due to plugin APIs/headers being tied to a specific license or having commercial restrictions. +This is usually due to plugin APIs/headers being tied to a specific license or having commercial restrictions. This file describes the licensing that applies to each individual plugin format as a way to make it clear what is possible and compatible. Note that if you are making GPLv2+ licensed plugins this does not apply to you, as so far everything is GPLv2+ compatible. From db30d87c48eb52803a2bf82b1a2d5b0cf4c2976d Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 13:42:27 +0100 Subject: [PATCH 119/504] Rework feature table Signed-off-by: falkTX --- FEATURES.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 25b05b51..e46d5b0c 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -3,20 +3,22 @@ This file describes the available features for each plugin format. The limitations could be due to the plugin format itself or within DPF. -| Format | Param outs | Programs | States | UI sendNote | CV | MIDI in | MIDI out | Port groups | -|--------|------------|----------|--------|-------------|----|---------|----------|-------------| -| JACK | Yes* | Yes* | Yes | Yes | Yes | Yes | Yes | Yes* | -| LADSPA | Yes | No* | No | No | No | No | No | No | -| DSSI | Yes | Yes* | Yes* | Yes | No | Yes | No | No | -| LV2 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | -| VST2 | No | No* | Yes | Yes | No | Yes | Yes | Yes* | -| VST3 | Yes? | Yes | Yes | Yes | NO* | Yes | Yes | No* | +| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | +|-------------|-----------------|--------|------|-----|------|------| +| Param outs | Yes* | Yes | Yes | Yes | No | Yes? | +| Programs | Yes* | No* | Yes* | Yes | No* | Yes | +| States | Yes | No | Yes* | Yes | Yes | Yes | +| UI sendNote | Yes | No | Yes | Yes | Yes | Yes | +| CV | Yes | No | No | Yes | No | No* | +| MIDI in | Yes | No | Yes | Yes | Yes | Yes | +| MIDI out | Yes | No | No | Yes | Yes | Yes | +| Port groups | Yes* | No | No | Yes | Yes* | No* | Special notes: | Format | Notes | |--------|-------| -| JACK | Parameters have programs mapped to MIDI CC, there is no generic plugin editor; Port groups as JACK metadata | +| JACK | Parameters and programs are mapped to MIDI events, there is no generic plugin editor; Audio port groups are set as JACK metadata | | LADSPA | LADSPA only supports basic parameters and audio;
Programs could be done via LRDF but not supported in DPF | | DSSI | DSSI only supports States via UI, no "full state" possible | | LV2 | Everything supported :) | From a471d29df2247c984378fd94bbf36d7818ce948a Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 15:04:20 +0100 Subject: [PATCH 120/504] Try to improve feature table Signed-off-by: falkTX --- FEATURES.md | 71 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index e46d5b0c..a7eb2649 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -3,33 +3,47 @@ This file describes the available features for each plugin format. The limitations could be due to the plugin format itself or within DPF. -| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | -|-------------|-----------------|--------|------|-----|------|------| -| Param outs | Yes* | Yes | Yes | Yes | No | Yes? | -| Programs | Yes* | No* | Yes* | Yes | No* | Yes | -| States | Yes | No | Yes* | Yes | Yes | Yes | -| UI sendNote | Yes | No | Yes | Yes | Yes | Yes | -| CV | Yes | No | No | Yes | No | No* | -| MIDI in | Yes | No | Yes | Yes | Yes | Yes | -| MIDI out | Yes | No | No | Yes | Yes | Yes | -| Port groups | Yes* | No | No | Yes | Yes* | No* | - -Special notes: +| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | +|-------------------|-----------------|--------|------|---------------|--------|--------| +| Audio port groups | [Yes*](FEATURES.md#JACK parameters and programs) | No | No | Yes | No | No* | +| Audio port as CV | Yes | No | No | Yes | No | [No*](FEATURES.md#VST3 is work in progress) | +| MIDI input | Yes | No | Yes | Yes | Yes | Yes | +| MIDI output | Yes | No | No | Yes | Yes | Yes | +| Parameter changes | Yes | No | No | [No*](FEATURES.md#LV2 parameter changes) | Yes | Yes | +| Parameter groups | No | No | No | Yes | Yes* | [No*](FEATURES.md#VST3 is work in progress) | +| Parameter outs | No | No | No | Yes | Yes | [No*](FEATURES.md#VST3 is work in progress) | +| Programs | Yes* | No* | Yes* | Yes | No* | Yes | +| States | Yes | No | Yes* | Yes | Yes | Yes | +| UI | Yes* | No | Ext. | Embed or Ext. | Embed | Embed | +| UI host-resize | Yes | No | Yes | Yes | No | [No*](FEATURES.md#VST3 is work in progress) | +| UI sendNote | Yes | No | Yes | Yes | Yes | Yes | -| Format | Notes | -|--------|-------| -| JACK | Parameters and programs are mapped to MIDI events, there is no generic plugin editor; Audio port groups are set as JACK metadata | -| LADSPA | LADSPA only supports basic parameters and audio;
Programs could be done via LRDF but not supported in DPF | -| DSSI | DSSI only supports States via UI, no "full state" possible | -| LV2 | Everything supported :) | -| VST2 | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF;
Parameter groups are supported, but not for audio ports (per VST2 spec limitations) | -| VST3 | Not sure if parameter outputs work (aka "read-only" on VST3);
CV ports do not support custom ranges, not implemented yet;
Port groups not implemented yet | +# Special notes + +## JACK parameters and programs + +Under JACK/Stanlone mode, MIDI input events will trigger program and parameter changes. +MIDI program change events work as expected (that is, MIDI program change 0 will load 1st plugin program). +MIDI CCs are used for parameter changes (matching the `midiCC` value you set on each parameter). + +## JACK custom UI only + +There is no generic plugin editor view. +If your plugin has no custom UI, the standalone executable will run but not show any window. + +## LV2 parameter changes + +Possible through a custom extension, not implemented on most hosts. +For now you can pretty much assume it is not supported. + +## VST3 is work in progress + +Feature is possible, just not implemented yet in DPF. + +### TODO A few notes for things to add to the table: - - Custom UI (embed, external, both or none) - - Host/user-side UI resize - - Parameter inputs change from plugin-side - Sidechain tagged audio ports - Trigger parameters - UI background/foreground color @@ -37,3 +51,14 @@ A few notes for things to add to the table: - Remote/Instance access - Host-mapped bypass parameter - Time position + +# Extra notes + +| Format | Notes | +|--------|-------| +| JACK | Parameters and programs are mapped to MIDI events, ; Audio port groups are set as JACK metadata | +| LADSPA | LADSPA only supports basic parameters and audio;
Programs could be done via LRDF but not supported in DPF | +| DSSI | DSSI only supports States via UI, no "full state" possible | +| LV2 | Everything supported :) | +| VST2 | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF;
Parameter groups are supported, but not for audio ports (per VST2 spec limitations) | +| VST3 | Not sure if parameter outputs work (aka "read-only" on VST3);
CV ports do not support custom ranges, not implemented yet;
Port groups not implemented yet | From 2a95a65ad6b8531f977e25a3c6e494f719c93f55 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 15:06:07 +0100 Subject: [PATCH 121/504] Fix feature links Signed-off-by: falkTX --- FEATURES.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index a7eb2649..cf122e05 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -5,17 +5,17 @@ The limitations could be due to the plugin format itself or within DPF. | Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | |-------------------|-----------------|--------|------|---------------|--------|--------| -| Audio port groups | [Yes*](FEATURES.md#JACK parameters and programs) | No | No | Yes | No | No* | -| Audio port as CV | Yes | No | No | Yes | No | [No*](FEATURES.md#VST3 is work in progress) | +| Audio port groups | [Yes*](FEATURES.md#jack-parameters-and-programs) | No | No | Yes | No | No* | +| Audio port as CV | Yes | No | No | Yes | No | [No*](FEATURES.md#vst3-is-work-in-progress) | | MIDI input | Yes | No | Yes | Yes | Yes | Yes | | MIDI output | Yes | No | No | Yes | Yes | Yes | -| Parameter changes | Yes | No | No | [No*](FEATURES.md#LV2 parameter changes) | Yes | Yes | -| Parameter groups | No | No | No | Yes | Yes* | [No*](FEATURES.md#VST3 is work in progress) | -| Parameter outs | No | No | No | Yes | Yes | [No*](FEATURES.md#VST3 is work in progress) | +| Parameter changes | Yes | No | No | [No*](FEATURES.md#lv2-parameter-changes) | Yes | Yes | +| Parameter groups | No | No | No | Yes | Yes* | [No*](FEATURES.md#vst3-is-work-in-progress) | +| Parameter outs | No | No | No | Yes | Yes | [No*](FEATURES.md#vst3-is-work-in-progress) | | Programs | Yes* | No* | Yes* | Yes | No* | Yes | | States | Yes | No | Yes* | Yes | Yes | Yes | -| UI | Yes* | No | Ext. | Embed or Ext. | Embed | Embed | -| UI host-resize | Yes | No | Yes | Yes | No | [No*](FEATURES.md#VST3 is work in progress) | +| UI | [Yes*](FEATURES.md#jack-custom-ui-only) | No | Ext. | Embed or Ext. | Embed | Embed | +| UI host-resize | Yes | No | Yes | Yes | No | [No*](FEATURES.md#vst3-is-work-in-progress) | | UI sendNote | Yes | No | Yes | Yes | Yes | Yes | # Special notes From 1b408abe9505cc38e6a7f809b0aa14fec5779601 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 15:26:12 +0100 Subject: [PATCH 122/504] Testing some changes to the feature table Signed-off-by: falkTX --- FEATURES.md | 86 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index cf122e05..f0384b52 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -3,23 +3,44 @@ This file describes the available features for each plugin format. The limitations could be due to the plugin format itself or within DPF. -| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | -|-------------------|-----------------|--------|------|---------------|--------|--------| -| Audio port groups | [Yes*](FEATURES.md#jack-parameters-and-programs) | No | No | Yes | No | No* | -| Audio port as CV | Yes | No | No | Yes | No | [No*](FEATURES.md#vst3-is-work-in-progress) | -| MIDI input | Yes | No | Yes | Yes | Yes | Yes | -| MIDI output | Yes | No | No | Yes | Yes | Yes | -| Parameter changes | Yes | No | No | [No*](FEATURES.md#lv2-parameter-changes) | Yes | Yes | -| Parameter groups | No | No | No | Yes | Yes* | [No*](FEATURES.md#vst3-is-work-in-progress) | -| Parameter outs | No | No | No | Yes | Yes | [No*](FEATURES.md#vst3-is-work-in-progress) | -| Programs | Yes* | No* | Yes* | Yes | No* | Yes | -| States | Yes | No | Yes* | Yes | Yes | Yes | -| UI | [Yes*](FEATURES.md#jack-custom-ui-only) | No | Ext. | Embed or Ext. | Embed | Embed | -| UI host-resize | Yes | No | Yes | Yes | No | [No*](FEATURES.md#vst3-is-work-in-progress) | -| UI sendNote | Yes | No | Yes | Yes | Yes | Yes | +| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | +|---------------------|--------------------------------------------------|------------------------------------|------|---------------|--------|--------| +| Audio port groups | [Yes*](FEATURES.md#jack-audio-port-groups) | No | No | Yes | No | No* | +| Audio port as CV | Yes | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | +| Audio sidechan | Yes | No | No | Yes | [No*](#vst2-potential-support) | [No*](FEATURES.md#vst3-is-work-in-progress) | +| Bypass control +| MIDI input | Yes | No | Yes | Yes | Yes | Yes | +| MIDI output | Yes | No | No | Yes | Yes | Yes | +| Parameter changes | Yes | No | No | [No*](FEATURES.md#lv2-parameter-changes) | Yes | Yes | +| Parameter groups | No | No | No | Yes | Yes* | [No*](FEATURES.md#vst3-is-work-in-progress) | +| Parameter outputs | No | No | No | Yes | Yes | [No*](FEATURES.md#vst3-is-work-in-progress) | +| Parameter triggers | +| Programs | [Yes*](FEATURES.md#jack-parameters-and-programs) | [No*](FEATURES.md#ladspa-programs) | Yes* | Yes | No* | Yes | +| States | Yes | No | Yes* | Yes | Yes | Yes | +| Full/internal state +| Time position +| UI | [Yes*](FEATURES.md#jack-custom-ui-only) | No | Ext. | Embed or Ext. | Embed | Embed | +| UI bg/fg colors +| UI direct access +| UI host-filebrowser +| UI host-resize | Yes | No | Yes | Yes | No | [No*](FEATURES.md#vst3-is-work-in-progress) | +| UI remote control +| UI sendNote | Yes | No | Yes | Yes | Yes | Yes | + +For things that could be unclear: + +- "States" refers to DPF API support, supporting key-value string pairs for internal state saving +- "Full state" refers to plugins updating their state internally without outside intervention (like host or UI) +- "UI direct access" means `DISTRHO_PLUGIN_WANT_DIRECT_ACCESS` is possible, that is, running DSP and UI on the same process +- "UI remote control" means running the UI on a separate machine (for example over the network) # Special notes +## JACK audio port groups + +DPF will set JACK metadata information for grouping audio ports. +This is not supported by most JACK applications at the moment. + ## JACK parameters and programs Under JACK/Stanlone mode, MIDI input events will trigger program and parameter changes. @@ -31,34 +52,29 @@ MIDI CCs are used for parameter changes (matching the `midiCC` value you set on There is no generic plugin editor view. If your plugin has no custom UI, the standalone executable will run but not show any window. +## LADSPA programs + +Programs for LADSPA could be done via LRDF but this is not supported in DPF. + +## DSSI State + +DSSI only supports states when called via UI, no "full state" possible + ## LV2 parameter changes Possible through a custom extension, not implemented on most hosts. For now you can pretty much assume it is not supported. -## VST3 is work in progress - -Feature is possible, just not implemented yet in DPF. +## VST2 potential support -### TODO +Not supported in DPF at the moment. +It could eventually be, but likely not due to VST2 being phased out by Steinberg. +Contact DPF authors if you require such a feature. -A few notes for things to add to the table: +## VST2 programs - - Sidechain tagged audio ports - - Trigger parameters - - UI background/foreground color - - Host-side file browser - - Remote/Instance access - - Host-mapped bypass parameter - - Time position +VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF. -# Extra notes +## VST3 is work in progress -| Format | Notes | -|--------|-------| -| JACK | Parameters and programs are mapped to MIDI events, ; Audio port groups are set as JACK metadata | -| LADSPA | LADSPA only supports basic parameters and audio;
Programs could be done via LRDF but not supported in DPF | -| DSSI | DSSI only supports States via UI, no "full state" possible | -| LV2 | Everything supported :) | -| VST2 | VST2 program support requires saving state of all programs in memory, which is very expensive and thus not done in DPF;
Parameter groups are supported, but not for audio ports (per VST2 spec limitations) | -| VST3 | Not sure if parameter outputs work (aka "read-only" on VST3);
CV ports do not support custom ranges, not implemented yet;
Port groups not implemented yet | +Feature is possible, just not implemented yet in DPF. From 7b080d498b64730b8eab373268c2271e9438d650 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 16:01:02 +0100 Subject: [PATCH 123/504] Complete the feature table Signed-off-by: falkTX --- FEATURES.md | 69 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index f0384b52..0e7d833b 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,31 +1,32 @@ # DPF - DISTRHO Plugin Framework This file describes the available features for each plugin format. -The limitations could be due to the plugin format itself or within DPF. - -| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | -|---------------------|--------------------------------------------------|------------------------------------|------|---------------|--------|--------| -| Audio port groups | [Yes*](FEATURES.md#jack-audio-port-groups) | No | No | Yes | No | No* | -| Audio port as CV | Yes | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | -| Audio sidechan | Yes | No | No | Yes | [No*](#vst2-potential-support) | [No*](FEATURES.md#vst3-is-work-in-progress) | -| Bypass control -| MIDI input | Yes | No | Yes | Yes | Yes | Yes | -| MIDI output | Yes | No | No | Yes | Yes | Yes | -| Parameter changes | Yes | No | No | [No*](FEATURES.md#lv2-parameter-changes) | Yes | Yes | -| Parameter groups | No | No | No | Yes | Yes* | [No*](FEATURES.md#vst3-is-work-in-progress) | -| Parameter outputs | No | No | No | Yes | Yes | [No*](FEATURES.md#vst3-is-work-in-progress) | -| Parameter triggers | -| Programs | [Yes*](FEATURES.md#jack-parameters-and-programs) | [No*](FEATURES.md#ladspa-programs) | Yes* | Yes | No* | Yes | -| States | Yes | No | Yes* | Yes | Yes | Yes | -| Full/internal state -| Time position -| UI | [Yes*](FEATURES.md#jack-custom-ui-only) | No | Ext. | Embed or Ext. | Embed | Embed | -| UI bg/fg colors -| UI direct access -| UI host-filebrowser -| UI host-resize | Yes | No | Yes | Yes | No | [No*](FEATURES.md#vst3-is-work-in-progress) | -| UI remote control -| UI sendNote | Yes | No | Yes | Yes | Yes | Yes | +The limitations could be due to the plugin format itself or within DPF. +If the limitation is within DPF, a link is provided to a description below on the reason for it. + +| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | +|---------------------|---------------------------------------|-------------------------|---------------------|-------------------------------|--------------------------------|----------------------------------| +| Audio port groups | [Yes*](#jack-audio-port-groups) | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | +| Audio port as CV | Yes | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | +| Audio sidechan | Yes | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | +| Bypass control | No | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | +| MIDI input | Yes | No | Yes | Yes | Yes | Yes | +| MIDI output | Yes | No | No | Yes | Yes | Yes | +| Parameter changes | Yes | No | No | [No*](#lv2-parameter-changes) | Yes | Yes | +| Parameter groups | No | No | No | Yes | Yes | [No*](#vst3-is-work-in-progress) | +| Parameter outputs | No | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | +| Parameter triggers | Yes | No | No | Yes | [No*](#parameter-triggers) | [No*](#parameter-triggers) | +| Programs | [Yes*](#jack-parameters-and-programs) | [No*](#ladspa-programs) | [Yes*](#dssi-state) | Yes | [No*](#vst2-programs) | Yes | +| States | Yes | No | [Yes*](#dssi-state) | Yes | Yes | Yes | +| Full/internal state | Yes | No | No | Yes | Yes | Yes | +| Time position | Yes | No | No | Yes | Yes | Yes | +| UI | [Yes*](#jack-custom-ui-only) | No | External only | Yes | Embed only | Embed only | +| UI bg/fg colors | No | No | No | Yes | No | No? | +| UI direct access | Yes | No | No | Yes | Yes | Yes | +| UI host-filebrowser | No | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | +| UI host-resize | Yes | No | Yes | Yes | No | [No*](#vst3-is-work-in-progress) | +| UI remote control | No | No | Yes | Yes | No | Yes | +| UI send midi note | Yes | No | Yes | Yes | Yes | Yes | For things that could be unclear: @@ -33,9 +34,15 @@ For things that could be unclear: - "Full state" refers to plugins updating their state internally without outside intervention (like host or UI) - "UI direct access" means `DISTRHO_PLUGIN_WANT_DIRECT_ACCESS` is possible, that is, running DSP and UI on the same process - "UI remote control" means running the UI on a separate machine (for example over the network) +- An external UI on this table means that it cannot be embed into the host window, but the plugin can still provide one # Special notes +## Parameter triggers + +Trigger-style parameters (parameters which value is reset to its default every run) are only supported in JACK and LV2. +For all other plugin formats DPF will simulate the behaviour through a parameter change request. + ## JACK audio port groups DPF will set JACK metadata information for grouping audio ports. @@ -58,12 +65,18 @@ Programs for LADSPA could be done via LRDF but this is not supported in DPF. ## DSSI State -DSSI only supports states when called via UI, no "full state" possible +DSSI only supports state changes when called via UI, no "full state" possible. +This also makes it impossibe to use programs and state at the same time with DSSI, +because in DPF changing programs can lead to state changes but there is no way to fetch this information on DSSI plugins. + +To make it simpler to understand, think of DSSI programs and states as UI properties. +Because in DPF changing the state happens from UI to DSP side, regular DSSI can be supported. +But if we involve programs, they would need to pass through the UI in order to work. Which goes against DPF's design. ## LV2 parameter changes -Possible through a custom extension, not implemented on most hosts. -For now you can pretty much assume it is not supported. +Although this is already implemented in DPF (through a custom extension), this is not implemented on most hosts. +So for now you can pretty much treat it as if not supported. ## VST2 potential support From 7c9f6304d1e18cc73005717b854d846e43d79d52 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 16:03:39 +0100 Subject: [PATCH 124/504] Add feature text on both sides Signed-off-by: falkTX --- FEATURES.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 0e7d833b..3bba8f14 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -4,29 +4,29 @@ This file describes the available features for each plugin format. The limitations could be due to the plugin format itself or within DPF. If the limitation is within DPF, a link is provided to a description below on the reason for it. -| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | -|---------------------|---------------------------------------|-------------------------|---------------------|-------------------------------|--------------------------------|----------------------------------| -| Audio port groups | [Yes*](#jack-audio-port-groups) | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | -| Audio port as CV | Yes | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | -| Audio sidechan | Yes | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | -| Bypass control | No | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | -| MIDI input | Yes | No | Yes | Yes | Yes | Yes | -| MIDI output | Yes | No | No | Yes | Yes | Yes | -| Parameter changes | Yes | No | No | [No*](#lv2-parameter-changes) | Yes | Yes | -| Parameter groups | No | No | No | Yes | Yes | [No*](#vst3-is-work-in-progress) | -| Parameter outputs | No | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | -| Parameter triggers | Yes | No | No | Yes | [No*](#parameter-triggers) | [No*](#parameter-triggers) | -| Programs | [Yes*](#jack-parameters-and-programs) | [No*](#ladspa-programs) | [Yes*](#dssi-state) | Yes | [No*](#vst2-programs) | Yes | -| States | Yes | No | [Yes*](#dssi-state) | Yes | Yes | Yes | -| Full/internal state | Yes | No | No | Yes | Yes | Yes | -| Time position | Yes | No | No | Yes | Yes | Yes | -| UI | [Yes*](#jack-custom-ui-only) | No | External only | Yes | Embed only | Embed only | -| UI bg/fg colors | No | No | No | Yes | No | No? | -| UI direct access | Yes | No | No | Yes | Yes | Yes | -| UI host-filebrowser | No | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | -| UI host-resize | Yes | No | Yes | Yes | No | [No*](#vst3-is-work-in-progress) | -| UI remote control | No | No | Yes | Yes | No | Yes | -| UI send midi note | Yes | No | Yes | Yes | Yes | Yes | +| Feature | JACK/Standalone | LADSPA | DSSI | LV2 | VST2 | VST3 | Feature | +|---------------------|---------------------------------------|-------------------------|---------------------|-------------------------------|--------------------------------|----------------------------------|---------------------| +| Audio port groups | [Yes*](#jack-audio-port-groups) | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | Audio port groups | +| Audio port as CV | Yes | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | Audio port as CV | +| Audio sidechan | Yes | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | Audio sidechan | +| Bypass control | No | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | Bypass control | +| MIDI input | Yes | No | Yes | Yes | Yes | Yes | MIDI input | +| MIDI output | Yes | No | No | Yes | Yes | Yes | MIDI output | +| Parameter changes | Yes | No | No | [No*](#lv2-parameter-changes) | Yes | Yes | Parameter changes | +| Parameter groups | No | No | No | Yes | Yes | [No*](#vst3-is-work-in-progress) | Parameter groups | +| Parameter outputs | No | No | No | Yes | No | [No*](#vst3-is-work-in-progress) | Parameter outputs | +| Parameter triggers | Yes | No | No | Yes | [No*](#parameter-triggers) | [No*](#parameter-triggers) | Parameter triggers | +| Programs | [Yes*](#jack-parameters-and-programs) | [No*](#ladspa-programs) | [Yes*](#dssi-state) | Yes | [No*](#vst2-programs) | Yes | Programs | +| States | Yes | No | [Yes*](#dssi-state) | Yes | Yes | Yes | States | +| Full/internal state | Yes | No | No | Yes | Yes | Yes | Full/internal state | +| Time position | Yes | No | No | Yes | Yes | Yes | Time position | +| UI | [Yes*](#jack-custom-ui-only) | No | External only | Yes | Embed only | Embed only | UI | +| UI bg/fg colors | No | No | No | Yes | No | No? | UI bg/fg colors | +| UI direct access | Yes | No | No | Yes | Yes | Yes | UI direct access | +| UI host-filebrowser | No | No | No | Yes | [No*](#vst2-potential-support) | [No*](#vst3-is-work-in-progress) | UI host-filebrowser | +| UI host-resize | Yes | No | Yes | Yes | No | [No*](#vst3-is-work-in-progress) | UI host-resize | +| UI remote control | No | No | Yes | Yes | No | Yes | UI remote control | +| UI send midi note | Yes | No | Yes | Yes | Yes | Yes | UI send midi note | For things that could be unclear: From b3b08825f0051c2c8197f157fd3da8c3229b31dd Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Sep 2021 21:35:27 +0100 Subject: [PATCH 125/504] Fix compiler warnings --- dgl/Widget.hpp | 9 +++++++-- distrho/src/travesty/align_pop.h | 17 ++++++++++++++++- distrho/src/travesty/align_push.h | 13 ++++++++++--- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 8a95dfce..463f7c9f 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -427,12 +427,17 @@ protected: A function called when a special key is pressed or released. DEPRECATED use onKeyboard or onCharacterInput */ -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif virtual bool onSpecial(const SpecialEvent&) { return false; } -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 # pragma GCC diagnostic pop #endif diff --git a/distrho/src/travesty/align_pop.h b/distrho/src/travesty/align_pop.h index bf88a3f6..50eaf26f 100644 --- a/distrho/src/travesty/align_pop.h +++ b/distrho/src/travesty/align_pop.h @@ -14,10 +14,25 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#if defined(__APPLE__) +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpragma-pack" +# elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunknown-warning-option" +# pragma GCC diagnostic ignored "-Wpragma-pack" +# endif +#endif + #if defined(__APPLE__) || defined(_WIN32) # pragma pack(pop) #endif #if defined(__APPLE__) -# pragma GCC diagnostic pop +# if defined(__clang__) +# pragma clang diagnostic pop +# elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 +# pragma GCC diagnostic pop +# endif #endif diff --git a/distrho/src/travesty/align_push.h b/distrho/src/travesty/align_push.h index 4a7ea9a9..fb6d91fb 100644 --- a/distrho/src/travesty/align_push.h +++ b/distrho/src/travesty/align_push.h @@ -16,9 +16,16 @@ #if defined(__APPLE__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunknown-warning-option" -# pragma GCC diagnostic ignored "-Wpragma-pack" +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-warning-option" +# pragma clang diagnostic ignored "-Wpragma-pack" +# elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 460 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunknown-warning-option" +# pragma GCC diagnostic ignored "-Wpragma-pack" +# endif + # if defined(__LP64__) || defined(_LP64) # pragma pack(push, 16) # else From c8745536300aabb3e330da4d0641b2583f2757b6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 30 Sep 2021 00:25:01 +0100 Subject: [PATCH 126/504] Fix demo tools under high dpi --- tests/Demo.cpp | 41 +++++++++++++++++------------ tests/FileBrowserDialog.cpp | 21 ++++++++++----- tests/NanoImage.cpp | 2 +- tests/widgets/ExampleTextWidget.hpp | 31 +++++++++++++++++++--- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 744044a3..41759d24 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -103,12 +103,13 @@ protected: void onDisplay() override { const GraphicsContext& context(getGraphicsContext()); + const double scaleFactor = getWindow().getScaleFactor(); const int iconSize = bgIcon.getWidth(); Color(0.027f, 0.027f, 0.027f).setFor(context); Rectangle(0, 0, getSize()).draw(context); - bgIcon.setY(curPage*iconSize + curPage*3); + bgIcon.setY(curPage * iconSize + curPage * 3 * scaleFactor); Color(0.129f, 0.129f, 0.129f).setFor(context); bgIcon.draw(context); @@ -118,7 +119,8 @@ protected: if (curHover != curPage && curHover != -1) { - Rectangle rHover(1, curHover*iconSize + curHover*3, iconSize-2, iconSize-2); + Rectangle rHover(1 * scaleFactor, curHover * iconSize + curHover * 3 * scaleFactor, + iconSize - 2 * scaleFactor, iconSize - 2 * scaleFactor); Color(0.071f, 0.071f, 0.071f).setFor(context); rHover.draw(context); @@ -146,13 +148,13 @@ protected: // draw some text nvg.beginFrame(this); - nvg.fontSize(23.0f); + nvg.fontSize(23.0f * scaleFactor); nvg.textAlign(NanoVG::ALIGN_LEFT|NanoVG::ALIGN_TOP); //nvg.textLineHeight(20.0f); nvg.fillColor(220,220,220,220); - nvg.textBox(10, 420, iconSize, "Haha,", nullptr); - nvg.textBox(15, 440, iconSize, "Look!", nullptr); + nvg.textBox(10 * scaleFactor, 420 * scaleFactor, iconSize, "Haha,", nullptr); + nvg.textBox(15 * scaleFactor, 440 * scaleFactor, iconSize, "Look!", nullptr); nvg.endFrame(); #endif @@ -226,9 +228,10 @@ protected: { const uint width = ev.size.getWidth(); const uint height = ev.size.getHeight(); + const double scaleFactor = getWindow().getScaleFactor(); - bgIcon.setWidth(width-4); - bgIcon.setHeight(width-4); + bgIcon.setWidth(width - 4 * scaleFactor); + bgIcon.setHeight(width - 4 * scaleFactor); lineSep.setStartPos(width, 0); lineSep.setEndPos(width, height); @@ -420,30 +423,31 @@ public: curWidget(nullptr) { const ScopedGraphicsContext sgc(*this); + const double scaleFactor = getScaleFactor(); wColor = new ExampleColorSubWidget(this); wColor->hide(); - wColor->setAbsoluteX(kSidebarWidth); + wColor->setAbsoluteX(kSidebarWidth * scaleFactor); wImages = new ExampleImagesSubWidget(this); wImages->hide(); - wImages->setAbsoluteX(kSidebarWidth); + wImages->setAbsoluteX(kSidebarWidth * scaleFactor); wRects = new ExampleRectanglesSubWidget(this); wRects->hide(); - wRects->setAbsoluteX(kSidebarWidth); + wRects->setAbsoluteX(kSidebarWidth * scaleFactor); wShapes = new ExampleShapesSubWidget(this); wShapes->hide(); - wShapes->setAbsoluteX(kSidebarWidth); + wShapes->setAbsoluteX(kSidebarWidth * scaleFactor); #ifdef DGL_OPENGL wText = new ExampleTextSubWidget(this), wText->hide(); - wText->setAbsoluteX(kSidebarWidth); + wText->setAbsoluteX(kSidebarWidth * scaleFactor); #endif wLeft = new LeftSideWidget(this, this), - wLeft->setAbsolutePos(2, 2); + wLeft->setAbsolutePos(2 * scaleFactor, 2 * scaleFactor); resizer = new ResizeHandle(this); @@ -493,10 +497,12 @@ protected: { StandaloneWindow::onReshape(width, height); - if (width < kSidebarWidth) + const double scaleFactor = getScaleFactor(); + + if (width < kSidebarWidth * scaleFactor) return; - Size size(width-kSidebarWidth, height); + const Size size(width - kSidebarWidth * scaleFactor, height); wColor->setSize(size); wImages->setSize(size); wRects->setSize(size); @@ -504,7 +510,7 @@ protected: #ifdef DGL_OPENGL wText->setSize(size); #endif - wLeft->setSize(kSidebarWidth-4, height-4); + wLeft->setSize((kSidebarWidth - 4) * scaleFactor, (height - 4) * scaleFactor); } private: @@ -528,8 +534,9 @@ template void createAndShowExampleWidgetStandaloneWindow(Application& app) { ExampleWidgetStandaloneWindow swin(app); + const double scaleFactor = swin.getScaleFactor(); swin.setResizable(true); - swin.setSize(600, 500); + swin.setSize(600 * scaleFactor, 500 * scaleFactor); swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); swin.show(); app.exec(); diff --git a/tests/FileBrowserDialog.cpp b/tests/FileBrowserDialog.cpp index 3a9b4ab2..3733c385 100644 --- a/tests/FileBrowserDialog.cpp +++ b/tests/FileBrowserDialog.cpp @@ -38,21 +38,26 @@ public: loadSharedResources(); #endif setResizable(true); - setSize(500, 200); - setGeometryConstraints(500, 200, true); setTitle("FileBrowserDialog"); + + const double scaleFactor = getScaleFactor(); + setGeometryConstraints(500 * scaleFactor, 200 * scaleFactor, true); + setSize(500 * scaleFactor, 200 * scaleFactor); + done(); } protected: void onNanoDisplay() override { + const double scaleFactor = getScaleFactor(); + // Selected file beginPath(); - fontSize(14); + fontSize(14 * scaleFactor); textAlign(ALIGN_LEFT | ALIGN_MIDDLE); fillColor(255, 255, 255, 255); - text(20, getHeight()/2, selectedFile, NULL); + text(20 * scaleFactor, getHeight()/2, selectedFile, NULL); closePath(); // Button background @@ -66,7 +71,7 @@ protected: // Button label beginPath(); - fontSize(14); + fontSize(14 * scaleFactor); Rectangle buttonTextBounds; textBounds(0, 0, "Press me", NULL, buttonTextBounds); textAlign(ALIGN_CENTER | ALIGN_MIDDLE); @@ -138,8 +143,12 @@ protected: { const uint width = ev.size.getWidth(); const uint height = ev.size.getHeight(); + const double scaleFactor = getScaleFactor(); - buttonBounds = Rectangle(width - 120, height/2 - 20, 100, 40); + buttonBounds = Rectangle(width - 120 * scaleFactor, + height/2 - 20 * scaleFactor, + 100 * scaleFactor, + 40 * scaleFactor); } void onFocus(const bool focus, CrossingMode) override diff --git a/tests/NanoImage.cpp b/tests/NanoImage.cpp index 5140bfd4..9b719d1f 100644 --- a/tests/NanoImage.cpp +++ b/tests/NanoImage.cpp @@ -85,7 +85,7 @@ public: setResizable(true); setSize(500, 500); - // setGeometryConstraints(500, 500, false); + setGeometryConstraints(500, 500, false, true); setTitle("NanoImage"); done(); diff --git a/tests/widgets/ExampleTextWidget.hpp b/tests/widgets/ExampleTextWidget.hpp index b7062e69..60faae29 100644 --- a/tests/widgets/ExampleTextWidget.hpp +++ b/tests/widgets/ExampleTextWidget.hpp @@ -42,23 +42,28 @@ public: // StandaloneWindow explicit ExampleTextWidget(Application& app); + // helper + double getScaleFactor(); + protected: void onNanoDisplay() override { const int width = BaseWidget::getWidth(); const int height = BaseWidget::getHeight(); + const double scaleFactor = getScaleFactor(); - NanoVG::fontSize(40.0f); + NanoVG::fontSize(40.0f * scaleFactor); NanoVG::textAlign(NanoVG::Align(NanoVG::ALIGN_CENTER|NanoVG::ALIGN_MIDDLE)); - NanoVG::textLineHeight(20.0f); + NanoVG::textLineHeight(20.0f * scaleFactor); NanoVG::beginPath(); NanoVG::fillColor(220,220,220,255); - NanoVG::roundedRect(10, height/4+10, width-20, height/2-20, 3); + NanoVG::roundedRect(10 * scaleFactor, height/4 + 10 * scaleFactor, + width - 20 * scaleFactor, height/2 - 20 * scaleFactor, 3 * scaleFactor); NanoVG::fill(); NanoVG::fillColor(0,150,0,220); - NanoVG::textBox(10, height/2, width-20, "Hello World!", nullptr); + NanoVG::textBox(10 * scaleFactor, height/2, width - 20 * scaleFactor, "Hello World!", nullptr); } }; @@ -71,6 +76,12 @@ ExampleTextWidget::ExampleTextWidget(Widget* const parent) setSize(500, 300); } +template<> inline +double ExampleTextWidget::getScaleFactor() +{ + return getWindow().getScaleFactor(); +} + // TopLevelWidget template<> inline ExampleTextWidget::ExampleTextWidget(Window& windowToMapTo) @@ -80,6 +91,12 @@ ExampleTextWidget::ExampleTextWidget(Window& windowToMapTo) setSize(500, 300); } +template<> inline +double ExampleTextWidget::getScaleFactor() +{ + return NanoTopLevelWidget::getScaleFactor(); +} + // StandaloneWindow template<> inline ExampleTextWidget::ExampleTextWidget(Application& app) @@ -90,6 +107,12 @@ ExampleTextWidget::ExampleTextWidget(Application& app) done(); } +template<> inline +double ExampleTextWidget::getScaleFactor() +{ + return Window::getScaleFactor(); +} + template class ExampleTextWidget; template class ExampleTextWidget; template class ExampleTextWidget; From 8d0f5600c79e36e95d794e9edecc5deea821188f Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Oct 2021 17:22:29 +0100 Subject: [PATCH 127/504] VST3: Add 130*16 parameters for MIDI input, WIP Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 3 + distrho/src/DistrhoPluginVST3.cpp | 169 +++++++++++++++++++------- distrho/src/DistrhoUIPrivateData.hpp | 3 + distrho/src/travesty/events.h | 15 +-- 4 files changed, 135 insertions(+), 55 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 5b50682a..753d3e08 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -173,6 +173,9 @@ struct Plugin::PrivateData { #endif #ifdef DISTRHO_PLUGIN_TARGET_VST3 +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + parameterOffset += 130 * 16; // all MIDI CCs plus aftertouch and pitchbend +# endif # if DISTRHO_PLUGIN_WANT_PROGRAMS parameterOffset += 1; # endif diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 10cfc1bd..2726dcd9 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -331,10 +331,10 @@ public: #endif fParameterOffset(fPlugin.getParameterOffset()), fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), - fParameterValues(nullptr) + fParameterValues(nullptr), + fChangedParameterValues(nullptr) #if DISTRHO_PLUGIN_HAS_UI , fConnectedToUI(false) - , fChangedParameterValues(nullptr) , fNextSampleRate(0.0) #endif #if DISTRHO_PLUGIN_WANT_LATENCY @@ -421,13 +421,11 @@ public: fParameterValues[i] = fPlugin.getParameterDefault(i); } -#if DISTRHO_PLUGIN_HAS_UI if (fRealParameterCount != 0) { fChangedParameterValues = new bool[fRealParameterCount]; std::memset(fChangedParameterValues, 0, sizeof(bool)*fRealParameterCount); } -#endif #if DISTRHO_PLUGIN_WANT_STATE for (uint32_t i=0, count=fPlugin.getStateCount(); i= 0, V3_INVALID_ARG); @@ -1309,9 +1305,26 @@ public: strncpy_utf16(info->short_title, "Program", 128); return V3_OK; } + --rindex; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex <= 130*16) + { + std::memset(info, 0, sizeof(v3_param_info)); + info->param_id = rindex; + info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN; + info->step_count = 127; + char ccstr[24]; + snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", rindex / 130 + 1, rindex % 130); + strncpy_utf16(info->title, ccstr, 128); + snprintf(ccstr, sizeof(ccstr)-1, "Ch.%d CC%d", rindex / 130 + 1, rindex % 130); + strncpy_utf16(info->short_title, ccstr+5, 128); + return V3_OK; + } + rindex -= 130*16; #endif - const uint32_t index = static_cast(rindex) - fParameterOffset; + const uint32_t index = static_cast(rindex); DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fPlugin.getParameterCount(), index, V3_INVALID_ARG); // set up flags @@ -1346,7 +1359,7 @@ public: step_count = ranges.max - ranges.min - 1; std::memset(info, 0, sizeof(v3_param_info)); - info->param_id = rindex; + info->param_id = index + fParameterOffset; info->flags = flags; info->step_count = step_count; info->default_normalised_value = ranges.getNormalizedValue(ranges.def); @@ -1357,27 +1370,36 @@ public: return V3_OK; } - v3_result getParameterStringForValue(const v3_param_id rindex, const double normalised, v3_str_128 output) + v3_result getParameterStringForValue(v3_param_id rindex, const double normalised, v3_str_128 output) { DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(normalised >= 0.0 && normalised <= 1.0, V3_INVALID_ARG); + #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) { - DISTRHO_SAFE_ASSERT_RETURN(normalised >= 0.0 && normalised <= 1.0, V3_INVALID_ARG); - const uint32_t program = std::round(normalised * fProgramCountMinusOne); strncpy_utf16(output, fPlugin.getProgramName(program), 128); return V3_OK; } + --rindex; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex <= 130*16) + { + snprintf_f32_utf16(output, std::round(normalised * 127), 128); + return V3_OK; + } + rindex -= 130*16; #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); snprintf_f32_utf16(output, ranges.getUnnormalizedValue(normalised), 128); return V3_OK; } - v3_result getParameterValueForString(const v3_param_id rindex, int16_t*, double*) + v3_result getParameterValueForString(v3_param_id rindex, int16_t*, double*) { DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); @@ -1387,57 +1409,91 @@ public: // TODO find program index based on name return V3_NOT_IMPLEMENTED; } + --rindex; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex <= 130*16) + { + // TODO + return V3_NOT_IMPLEMENTED; + } + rindex -= 130*16; #endif + // TODO return V3_NOT_IMPLEMENTED; } - double normalisedParameterToPlain(const v3_param_id rindex, const double normalised) + double normalisedParameterToPlain(v3_param_id rindex, const double normalised) { DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) return std::round(normalised * fProgramCountMinusOne); + --rindex; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex <= 130*16) + return std::round(normalised * 127); + rindex -= 130*16; #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); return ranges.getUnnormalizedValue(normalised); } - double plainParameterToNormalised(const v3_param_id rindex, const double plain) + double plainParameterToNormalised(v3_param_id rindex, const double plain) { DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) return std::max(0.0, std::min(1.0, plain / fProgramCountMinusOne)); + --rindex; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex <= 130*16) + return std::max(0.0, std::min(1.0, plain / 127)); + rindex -= 130*16; #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); return ranges.getNormalizedValue(plain); } - double getParameterNormalized(const v3_param_id rindex) + double getParameterNormalized(v3_param_id rindex) { DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) return std::max(0.0, std::min(1.0, (double)fCurrentProgram / fProgramCountMinusOne)); + --rindex; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex <= 130*16) + { + // TODO + return 0.0; + } + rindex -= 130*16; #endif - const float value = fPlugin.getParameterValue(rindex - fParameterOffset); - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex - fParameterOffset)); + const float value = fPlugin.getParameterValue(rindex); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); return ranges.getNormalizedValue(value); } - v3_result setParameterNormalized(const v3_param_id rindex, const double value) + v3_result setParameterNormalized(v3_param_id rindex, const double value) { DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); +#if DISTRHO_PLUGIN_HAS_UI + const uint32_t orindex = rindex; +#endif #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) { @@ -1453,33 +1509,44 @@ public: if (fComponentHandler != nullptr) v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED); + +# if DISTRHO_PLUGIN_HAS_UI + fChangedParameterValues[rindex] = true; +# endif + return V3_OK; } - else + --rindex; #endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex <= 130*16) { - const uint32_t index = rindex - fParameterOffset; - const uint32_t hints = fPlugin.getParameterHints(index); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + // TODO + fChangedParameterValues[rindex] = true; + return V3_NOT_IMPLEMENTED; + } + rindex -= 130*16; +#endif - float realValue = ranges.getUnnormalizedValue(value); + const uint32_t hints = fPlugin.getParameterHints(rindex); + const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); - if (hints & kParameterIsBoolean) - { - const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; - realValue = realValue > midRange ? ranges.max : ranges.min; - } + float realValue = ranges.getUnnormalizedValue(value); - if (hints & kParameterIsInteger) - { - realValue = std::round(realValue); - } + if (hints & kParameterIsBoolean) + { + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + realValue = realValue > midRange ? ranges.max : ranges.min; + } - fParameterValues[index] = realValue; - fPlugin.setParameterValue(index, realValue); + if (hints & kParameterIsInteger) + { + realValue = std::round(realValue); } + fParameterValues[rindex] = realValue; + fPlugin.setParameterValue(rindex, realValue); #if DISTRHO_PLUGIN_HAS_UI - fChangedParameterValues[rindex] = true; + fChangedParameterValues[orindex] = true; #endif return V3_OK; } @@ -1584,19 +1651,29 @@ public: DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR); DISTRHO_SAFE_ASSERT_RETURN(fConnectedToUI, V3_INTERNAL_ERR); - for (uint32_t i=0; iget_float(attrs, "value", &value); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - const uint32_t index = static_cast(rindex -= fParameterOffset); + const uint32_t index = static_cast(rindex - fParameterOffset); return requestParameterValueChange(index, value) ? V3_OK : V3_INTERNAL_ERR; } @@ -1714,9 +1791,9 @@ private: const uint32_t fParameterOffset; const uint32_t fRealParameterCount; // regular parameters + current program float* fParameterValues; + bool* fChangedParameterValues; #if DISTRHO_PLUGIN_HAS_UI bool fConnectedToUI; - bool* fChangedParameterValues; double fNextSampleRate; // if not zero, report to UI #endif #if DISTRHO_PLUGIN_WANT_LATENCY diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 79c7deb2..ca8a96f9 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -334,6 +334,9 @@ struct UI::PrivateData { #endif #ifdef DISTRHO_PLUGIN_TARGET_VST3 +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + parameterOffset += 130 * 16; // all MIDI CCs plus aftertouch and pitchbend +# endif # if DISTRHO_PLUGIN_WANT_PROGRAMS parameterOffset += 1; # endif diff --git a/distrho/src/travesty/events.h b/distrho/src/travesty/events.h index 52cf78c1..72a4830a 100644 --- a/distrho/src/travesty/events.h +++ b/distrho/src/travesty/events.h @@ -22,9 +22,6 @@ /** * note events - * - * i know there's others, but we don't need them right now. whatever. - * handle them later. */ struct v3_event_note_on { @@ -62,14 +59,14 @@ struct v3_event_chord { int16_t bass_note; int16_t mask; uint16_t text_len; - const int16_t *text; + const int16_t* text; }; struct v3_event_scale { int16_t root; int16_t mask; uint16_t text_len; - const int16_t *text; + const int16_t* text; }; struct v3_event_legacy_midi_cc_out { @@ -88,7 +85,7 @@ struct v3_event_note_expression_value { struct v3_event_note_expression_text { int32_t note_id; uint32_t text_len; - const int16_t *text; + const int16_t* text; }; /** @@ -137,9 +134,9 @@ struct v3_event { struct v3_event_list { struct v3_funknown; - uint32_t (V3_API *get_event_count)(void* self); - v3_result (V3_API *get_event)(void* self, int32_t idx, struct v3_event* event); - v3_result (V3_API *add_event)(void* self, struct v3_event* event); + uint32_t (V3_API* get_event_count)(void* self); + v3_result (V3_API* get_event)(void* self, int32_t idx, struct v3_event* event); + v3_result (V3_API* add_event)(void* self, struct v3_event* event); }; static constexpr const v3_tuid v3_event_list_iid = From de29a1ad9723db9bc1c9932ab60e5b4e553c72aa Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Oct 2021 18:50:58 +0100 Subject: [PATCH 128/504] VST3: Add MIDI CC, channel pressure and pitchbend support Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 188 ++++++++++++++++++++++--- distrho/src/travesty/audio_processor.h | 32 ++--- distrho/src/travesty/edit_controller.h | 62 +++++--- 3 files changed, 230 insertions(+), 52 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 2726dcd9..05d45bb9 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -48,8 +48,8 @@ * - hide parameter outputs? * - hide program parameter? * - deal with parameter triggers - * - midi cc parameter mapping - * - full MIDI1 encode and decode + * - MIDI program changes + * - append MIDI input events in a sorted way * - decode version number (0x010203 -> 1.2.3) * - bus arrangements * - optional audio buses, create dummy buffer of max_block_size length for them @@ -130,7 +130,6 @@ const char* tuid2str(const v3_tuid iid) { V3_ID(0xF040B4B3,0xA36045EC,0xABCDC045,0xB4D5A2CC), "{v3_component_handler2|NOT}" }, { V3_ID(0x7F4EFE59,0xF3204967,0xAC27A3AE,0xAFB63038), "{v3_edit_controller2|NOT}" }, { V3_ID(0x067D02C1,0x5B4E274D,0xA92D90FD,0x6EAF7240), "{v3_component_handler_bus_activation|NOT}" }, - { V3_ID(0xDF0FF9F7,0x49B74669,0xB63AB732,0x7ADBF5E5), "{v3_midi_mapping|NOT}" }, { V3_ID(0xC1271208,0x70594098,0xB9DD34B3,0x6BB0195E), "{v3_edit_controller_host_editing|NOT}" }, // units { V3_ID(0x8683B01F,0x7B354F70,0xA2651DEC,0x353AF4FF), "{v3_program_list_data|NOT}" }, @@ -164,6 +163,8 @@ const char* tuid2str(const v3_tuid iid) return "{v3_funknown}"; if (v3_tuid_match(iid, v3_message_iid)) return "{v3_message_iid}"; + if (v3_tuid_match(iid, v3_midi_mapping_iid)) + return "{v3_midi_mapping_iid}"; if (v3_tuid_match(iid, v3_param_value_queue_iid)) return "{v3_param_value_queue}"; if (v3_tuid_match(iid, v3_param_changes_iid)) @@ -1189,12 +1190,12 @@ public: case V3_EVENT_NOTE_OFF: // case V3_EVENT_DATA: case V3_EVENT_POLY_PRESSURE: + break; // case V3_EVENT_NOTE_EXP_VALUE: // case V3_EVENT_NOTE_EXP_TEXT: // case V3_EVENT_CHORD: // case V3_EVENT_SCALE: - case V3_EVENT_LEGACY_MIDI_CC_OUT: - break; + // case V3_EVENT_LEGACY_MIDI_CC_OUT: default: continue; } @@ -1226,14 +1227,6 @@ public: midiEvent.data[2] = std::max(0, std::min(127, (int)(event.poly_pressure.pressure * 127))); midiEvent.data[3] = 0; break; - case V3_EVENT_LEGACY_MIDI_CC_OUT: - midiEvent.size = 3; - midiEvent.data[0] = 0xB0 | (event.midi_cc_out.channel & 0xf); - midiEvent.data[1] = event.midi_cc_out.cc_number; - midiEvent.data[2] = event.midi_cc_out.value; - midiEvent.data[3] = 0; - // midiEvent.data[3] = event.midi_cc_out.value2; // TODO check when size should be 4 - break; default: midiEvent.size = 0; break; @@ -1244,6 +1237,75 @@ public: } } + // TODO append MIDI events in a sorted way + if (v3_param_changes** const inparamsptr = data->input_params) + { + v3_param_value_queue** queue; + int32_t offset; + double value; + + for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i) + { + queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i); + DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr); + + v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); + DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fRealParameterCount, rindex); + + // not supported yet + if (rindex >= fParameterOffset) + continue; + +# if DISTRHO_PLUGIN_WANT_PROGRAMS + if (rindex == 0) + continue; + --rindex; +# endif + + for (int32_t j = 0, pcount = v3_cpp_obj(queue)->get_point_count(queue); j < pcount; ++j) + { + if (v3_cpp_obj(queue)->get_point(queue, j, &offset, &value) != V3_OK) + break; + + DISTRHO_SAFE_ASSERT_BREAK(offset < data->nframes); + + MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); + midiEvent.frame = offset; + midiEvent.size = 3; + midiEvent.data[0] = (rindex / 130) & 0xf; + + switch (rindex) + { + case 128: // channel pressure + midiEvent.data[0] |= 0xD0; + midiEvent.data[1] = std::max(0, std::min(127, (int)(value * 127))); + midiEvent.data[2] = 0; + midiEvent.data[3] = 0; + break; + case 129: // pitchbend + midiEvent.data[0] |= 0xE0; + midiEvent.data[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f; + midiEvent.data[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7; + midiEvent.data[3] = 0; + break; + default: + midiEvent.data[0] |= 0xB0; + midiEvent.data[1] = rindex % 130; + midiEvent.data[2] = std::max(0, std::min(127, (int)(value * 127))); + midiEvent.data[3] = 0; + break; + } + + if (midiEventCount == kMaxMidiEvents) + break; + } + + if (midiEventCount == kMaxMidiEvents) + break; + } + } + + fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount); #else fPlugin.run(inputs, outputs, data->nframes); @@ -2018,7 +2080,25 @@ private: event.midi_cc_out.channel = data[0] & 0xf; event.midi_cc_out.cc_number = data[1]; event.midi_cc_out.value = data[2]; - event.midi_cc_out.value2 = midiEvent.size == 4 ? data[3] : 0; + if (midiEvent.size == 4) + event.midi_cc_out.value2 = midiEvent.size == 4; + break; + /* TODO how do we deal with program changes?? + case 0xC0: + break; + */ + case 0xD0: + event.type = V3_EVENT_LEGACY_MIDI_CC_OUT; + event.midi_cc_out.channel = data[0] & 0xf; + event.midi_cc_out.cc_number = 128; + event.midi_cc_out.value = data[1]; + break; + case 0xE0: + event.type = V3_EVENT_LEGACY_MIDI_CC_OUT; + event.midi_cc_out.channel = data[0] & 0xf; + event.midi_cc_out.cc_number = 129; + event.midi_cc_out.value = data[1]; + event.midi_cc_out.value2 = data[2]; break; default: return true; @@ -2594,18 +2674,78 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { }; #endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT // -------------------------------------------------------------------------------------------------------------------- -// dpf_edit_controller +// dpf_midi_mapping -struct v3_edit_controller_cpp : v3_funknown { - v3_plugin_base base; - v3_edit_controller controller; +struct dpf_midi_mapping : v3_midi_mapping_cpp { + ScopedPointer& vst3; + + dpf_midi_mapping(ScopedPointer& v) + : vst3(v) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_midi_mapping_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_midi_mapping::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_midi_mapping + + map.get_midi_controller_assignment = []V3_API(void*, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id) -> v3_result + { + DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE); + DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE); + DISTRHO_SAFE_ASSERT_INT_RETURN(cc >= 0 && cc < 130, cc, V3_FALSE); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + static constexpr const v3_param_id offset = 1; +#else + static const constexpr v3_param_id offset = 0; +#endif + + *id = offset + channel * 130 + cc; + return V3_OK; + }; + } }; +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_edit_controller struct dpf_edit_controller : v3_edit_controller_cpp { #if DISTRHO_PLUGIN_HAS_UI ScopedPointer connectionComp; // kConnectionPointController ScopedPointer connectionBridge; // kConnectionPointBridge +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + ScopedPointer midiMapping; #endif ScopedPointer& vst3; bool initialized; @@ -2640,10 +2780,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } } -#if DISTRHO_PLUGIN_HAS_UI dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NO_INTERFACE); +#if DISTRHO_PLUGIN_HAS_UI if (v3_tuid_match(v3_connection_point_iid, iid)) { if (controller->connectionComp == nullptr) @@ -2654,6 +2794,16 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } #endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (v3_tuid_match(v3_midi_mapping_iid, iid)) + { + if (controller->midiMapping == nullptr) + controller->midiMapping = new dpf_midi_mapping(controller->vst3); + *iface = &controller->midiMapping; + return V3_OK; + } +#endif + return V3_NO_INTERFACE; }; diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h index 29c07e87..6f00d89b 100644 --- a/distrho/src/travesty/audio_processor.h +++ b/distrho/src/travesty/audio_processor.h @@ -92,10 +92,10 @@ struct v3_process_setup { struct v3_param_value_queue { struct v3_funknown; - v3_param_id (V3_API *get_param_id)(void* self); - int32_t (V3_API *get_point_count)(void* self); - v3_result (V3_API *get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); - v3_result (V3_API *add_point)(void* self, int32_t sample_offset, double value, int32_t* idx); + v3_param_id (V3_API* get_param_id)(void* self); + int32_t (V3_API* get_point_count)(void* self); + v3_result (V3_API* get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); + v3_result (V3_API* add_point)(void* self, int32_t sample_offset, double value, int32_t* idx); }; static constexpr const v3_tuid v3_param_value_queue_iid = @@ -104,9 +104,9 @@ static constexpr const v3_tuid v3_param_value_queue_iid = struct v3_param_changes { struct v3_funknown; - int32_t (V3_API *get_param_count)(void* self); - struct v3_param_value_queue** (V3_API *get_param_data)(void* self, int32_t idx); - struct v3_param_value_queue** (V3_API *add_param_data)(void* self, v3_param_id* id, int32_t* index); + int32_t (V3_API* get_param_count)(void* self); + struct v3_param_value_queue** (V3_API* get_param_data)(void* self, int32_t idx); + struct v3_param_value_queue** (V3_API* add_param_data)(void* self, v3_param_id* id, int32_t* index); }; static constexpr const v3_tuid v3_param_changes_iid = @@ -183,7 +183,7 @@ enum { struct v3_process_context_requirements { struct v3_funknown; - uint32_t (V3_API *get_process_context_requirements)(void* self); + uint32_t (V3_API* get_process_context_requirements)(void* self); }; static constexpr const v3_tuid v3_process_context_requirements_iid = @@ -224,15 +224,15 @@ struct v3_process_data { struct v3_audio_processor { struct v3_funknown; - v3_result (V3_API *set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, + v3_result (V3_API* set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs); - v3_result (V3_API *get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); - v3_result (V3_API *can_process_sample_size)(void* self, int32_t symbolic_sample_size); - uint32_t (V3_API *get_latency_samples)(void* self); - v3_result (V3_API *setup_processing)(void* self, struct v3_process_setup* setup); - v3_result (V3_API *set_processing)(void* self, v3_bool state); - v3_result (V3_API *process)(void* self, struct v3_process_data* data); - uint32_t (V3_API *get_tail_samples)(void* self); + v3_result (V3_API* get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); + v3_result (V3_API* can_process_sample_size)(void* self, int32_t symbolic_sample_size); + uint32_t (V3_API* get_latency_samples)(void* self); + v3_result (V3_API* setup_processing)(void* self, struct v3_process_setup* setup); + v3_result (V3_API* set_processing)(void* self, v3_bool state); + v3_result (V3_API* process)(void* self, struct v3_process_data* data); + uint32_t (V3_API* get_tail_samples)(void* self); }; static constexpr const v3_tuid v3_audio_processor_iid = diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 438a515c..642e2080 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -42,10 +42,10 @@ enum { struct v3_component_handler { struct v3_funknown; - v3_result (V3_API *begin_edit)(void* self, v3_param_id); - v3_result (V3_API *perform_edit)(void* self, v3_param_id, double value_normalised); - v3_result (V3_API *end_edit)(void* self, v3_param_id); - v3_result (V3_API *restart_component)(void* self, int32_t flags); + v3_result (V3_API* begin_edit)(void* self, v3_param_id); + v3_result (V3_API* perform_edit)(void* self, v3_param_id, double value_normalised); + v3_result (V3_API* end_edit)(void* self, v3_param_id); + v3_result (V3_API* restart_component)(void* self, int32_t flags); }; static constexpr const v3_tuid v3_component_handler_iid = @@ -79,25 +79,52 @@ struct v3_param_info { struct v3_edit_controller { struct v3_plugin_base; - v3_result (V3_API *set_component_state)(void* self, struct v3_bstream*); - v3_result (V3_API *set_state)(void* self, struct v3_bstream*); - v3_result (V3_API *get_state)(void* self, struct v3_bstream*); - int32_t (V3_API *get_parameter_count)(void* self); - v3_result (V3_API *get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); - v3_result (V3_API *get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); - v3_result (V3_API *get_parameter_value_for_string)(void* self, v3_param_id, int16_t* input, double* output); - double (V3_API *normalised_parameter_to_plain)(void* self, v3_param_id, double normalised); - double (V3_API *plain_parameter_to_normalised)(void* self, v3_param_id, double plain); - double (V3_API *get_parameter_normalised)(void* self, v3_param_id); - v3_result (V3_API *set_parameter_normalised)(void* self, v3_param_id, double normalised); - v3_result (V3_API *set_component_handler)(void* self, struct v3_component_handler**); - struct v3_plugin_view** (V3_API *create_view)(void* self, const char* name); + v3_result (V3_API* set_component_state)(void* self, struct v3_bstream*); + v3_result (V3_API* set_state)(void* self, struct v3_bstream*); + v3_result (V3_API* get_state)(void* self, struct v3_bstream*); + int32_t (V3_API* get_parameter_count)(void* self); + v3_result (V3_API* get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); + v3_result (V3_API* get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); + v3_result (V3_API* get_parameter_value_for_string)(void* self, v3_param_id, int16_t* input, double* output); + double (V3_API* normalised_parameter_to_plain)(void* self, v3_param_id, double normalised); + double (V3_API* plain_parameter_to_normalised)(void* self, v3_param_id, double plain); + double (V3_API* get_parameter_normalised)(void* self, v3_param_id); + v3_result (V3_API* set_parameter_normalised)(void* self, v3_param_id, double normalised); + v3_result (V3_API* set_component_handler)(void* self, struct v3_component_handler**); + struct v3_plugin_view** (V3_API* create_view)(void* self, const char* name); }; static constexpr const v3_tuid v3_edit_controller_iid = V3_ID(0xDCD7BBE3, 0x7742448D, 0xA874AACC, 0x979C759E); +/** + * midi mapping + */ + +struct v3_midi_mapping { + struct v3_funknown; + + v3_result (V3_API* get_midi_controller_assignment)(void* self, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id); +}; + +static constexpr const v3_tuid v3_midi_mapping_iid = + V3_ID(0xDF0FF9F7, 0x49B74669, 0xB63AB732, 0x7ADBF5E5); + #ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_edit_controller_cpp : v3_funknown { + v3_plugin_base base; + v3_edit_controller controller; +}; + +struct v3_midi_mapping_cpp : v3_funknown { + v3_midi_mapping map; +}; + template<> inline constexpr v3_edit_controller* v3_cpp_obj(v3_edit_controller** obj) { @@ -108,6 +135,7 @@ constexpr v3_edit_controller* v3_cpp_obj(v3_edit_controller** obj) return static_cast( static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*5)); } + #endif #include "align_pop.h" From b2109439bfc2261e333d1655bd280ebe1c2ac580 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Oct 2021 20:00:12 +0100 Subject: [PATCH 129/504] Replace custom message implementation by host context creation Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 505 ++++--------------------- distrho/src/DistrhoUIVST3.cpp | 55 ++- distrho/src/travesty/base.h | 10 +- distrho/src/travesty/component.h | 24 ++ distrho/src/travesty/edit_controller.h | 2 +- distrho/src/travesty/host.h | 49 +++ distrho/src/travesty/message.h | 28 +- 7 files changed, 208 insertions(+), 465 deletions(-) create mode 100644 distrho/src/travesty/host.h diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 05d45bb9..22c6944a 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -35,7 +35,7 @@ #include "travesty/component.h" #include "travesty/edit_controller.h" #include "travesty/factory.h" -#include "travesty/message.h" +#include "travesty/host.h" #include #include @@ -49,6 +49,7 @@ * - hide program parameter? * - deal with parameter triggers * - MIDI program changes + * - MIDI sysex * - append MIDI input events in a sorted way * - decode version number (0x010203 -> 1.2.3) * - bus arrangements @@ -60,7 +61,6 @@ * - set factory sub_categories * - set factory email (needs new DPF API, useful for LV2 as well) * - do something with get_controller_class_id and set_io_mode? - * - replace dpf_message_create with host-side message creation */ START_NAMESPACE_DISTRHO @@ -161,6 +161,8 @@ const char* tuid2str(const v3_tuid iid) return "{v3_event_list}"; if (v3_tuid_match(iid, v3_funknown_iid)) return "{v3_funknown}"; + if (v3_tuid_match(iid, v3_host_application_iid)) + return "{v3_host_application_iid}"; if (v3_tuid_match(iid, v3_message_iid)) return "{v3_message_iid}"; if (v3_tuid_match(iid, v3_midi_mapping_iid)) @@ -286,15 +288,10 @@ static constexpr void (*const snprintf_u32)(char*, uint32_t, size_t) = snprintf_ static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snprintf_utf16_t; static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t; -// -------------------------------------------------------------------------------------------------------------------- -// create message object (implementation comes later) - -v3_message** dpf_message_create(const char* id); - // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (implemented on UI side) -v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate); +v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate); // -------------------------------------------------------------------------------------------------------------------- @@ -324,12 +321,13 @@ class PluginVst3 } inputBuses, outputBuses; public: - PluginVst3() + PluginVst3(v3_host_application** const context) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), #if DISTRHO_PLUGIN_HAS_UI fConnection(nullptr), #endif + fHostContext(context), fParameterOffset(fPlugin.getParameterOffset()), fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), fParameterValues(nullptr), @@ -1848,6 +1846,7 @@ private: #if DISTRHO_PLUGIN_HAS_UI v3_connection_point** fConnection; #endif + v3_host_application** const fHostContext; // Temporary data const uint32_t fParameterOffset; @@ -1941,9 +1940,24 @@ private: // ---------------------------------------------------------------------------------------------------------------- // helper functions called during message passing, can block - void sendSampleRateToUI(const double sampleRate) + v3_message** createMessage(const char* const id) const + { + DISTRHO_SAFE_ASSERT_RETURN(fHostContext != nullptr, nullptr); + + v3_tuid iid; + memcpy(iid, v3_message_iid, sizeof(v3_tuid)); + v3_message** msg = nullptr; + const v3_result res = v3_cpp_obj(fHostContext)->create_instance(fHostContext, iid, iid, (void**)&msg); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr); + + v3_cpp_obj(msg)->set_message_id(msg, id); + return msg; + } + + void sendSampleRateToUI(const double sampleRate) const { - v3_message** const message = dpf_message_create("sample-rate"); + v3_message** const message = createMessage("sample-rate"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -1956,9 +1970,9 @@ private: v3_cpp_obj_unref(message); } - void sendParameterChangeToUI(const v3_param_id rindex, const double value) + void sendParameterChangeToUI(const v3_param_id rindex, const double value) const { - v3_message** const message = dpf_message_create("parameter-set"); + v3_message** const message = createMessage("parameter-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -1972,9 +1986,9 @@ private: v3_cpp_obj_unref(message); } - void sendStateChangeToUI(const char* const key, const char* const value) + void sendStateChangeToUI(const char* const key, const char* const value) const { - v3_message** const message = dpf_message_create("state-set"); + v3_message** const message = createMessage("state-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -1992,9 +2006,9 @@ private: v3_cpp_obj_unref(message); } - void sendReadyToUI() + void sendReadyToUI() const { - v3_message** const message = dpf_message_create("ready"); + v3_message** const message = createMessage("ready"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -2121,385 +2135,6 @@ private: * VST3 low-level pointer thingies follow, proceed with care. */ -// -------------------------------------------------------------------------------------------------------------------- - -struct dpf_attribute_value { - char type; // one of: i, f, s, b - union { - int64_t integer; - double v_float; - int16_t* string; - struct { - void* ptr; - uint32_t size; - } binary; - }; -}; - -static void dpf_attribute_list_free(std::map& attrs) -{ - for (auto& it : attrs) - { - dpf_attribute_value& v(it.second); - - switch (v.type) - { - case 's': - case 'b': - std::free(v.binary.ptr); - break; - } - } -} - -// -------------------------------------------------------------------------------------------------------------------- -// dpf_attribute_list_utf8 (the custom variant) - -struct v3_attribute_list_utf8_cpp : v3_funknown { - v3_attribute_list_utf8 attr; -}; - -struct dpf_attribute_list_utf8 : v3_attribute_list_utf8_cpp { - std::map& attrs; - - dpf_attribute_list_utf8(std::map& a) - : attrs(a) - { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_attribute_list_utf8_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_attribute_list_utf8::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_attribute_list_utf8 - - attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result - { - dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - const uint32_t size = std::strlen(string) + 1; - int16_t* const copy = (int16_t*)std::malloc(sizeof(int16_t) * size); - DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); - - DISTRHO_NAMESPACE::strncpy_utf16(copy, string, size); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 's'; - attrval.binary.ptr = copy; - attrval.binary.size = sizeof(int16_t) * size; - return V3_OK; - }; - - attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result - { - dpf_attribute_list_utf8* const attr = *(dpf_attribute_list_utf8**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - return V3_NOT_IMPLEMENTED; - }; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- -// dpf_attribute_list - -struct dpf_attribute_list : v3_attribute_list_cpp { - ScopedPointer attrutf8; - std::map attrs; - - dpf_attribute_list() - { - static const uint8_t* kSupportedInterfacesBase[] = { - v3_funknown_iid, - v3_attribute_list_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - // d_stdout("dpf_attribute_list::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfacesBase) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NO_INTERFACE); - - if (v3_tuid_match(v3_attribute_list_utf8_iid, iid)) - { - if (attr->attrutf8 == nullptr) - attr->attrutf8 = new dpf_attribute_list_utf8(attr->attrs); - *iface = &attr->attrutf8; - return V3_OK; - } - - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_attribute_list - - attrlist.set_int = []V3_API(void* self, const char* id, int64_t value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 'i'; - attrval.integer = value; - return V3_OK; - }; - - attrlist.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'i', V3_INVALID_ARG); - - *value = attrval.integer; - return V3_OK; - }; - - attrlist.set_float = []V3_API(void* self, const char* id, double value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 'f'; - attrval.v_float = value; - return V3_OK; - }; - - attrlist.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'f', V3_INVALID_ARG); - - *value = attrval.v_float; - return V3_OK; - }; - - attrlist.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 's'; - attrval.binary.ptr = nullptr; - attrval.binary.size = 0; - return V3_NOT_IMPLEMENTED; - }; - - attrlist.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's', V3_INVALID_ARG); - - return V3_NOT_IMPLEMENTED; - }; - - attrlist.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - void* const copy = std::malloc(size); - DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM); - - std::memcpy(copy, data, size); - - dpf_attribute_value& attrval(attr->attrs[id]); - attrval.type = 'b'; - attrval.binary.ptr = copy; - attrval.binary.size = size; - return V3_NOT_IMPLEMENTED; - }; - - attrlist.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result - { - dpf_attribute_list* const attr = *(dpf_attribute_list**)self; - DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); - - if (attr->attrs.find(id) == attr->attrs.end()) - return V3_INVALID_ARG; - - const dpf_attribute_value& attrval(attr->attrs[id]); - DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's' || attrval.type == 'b', V3_INVALID_ARG); - - *data = attrval.binary.ptr; - *size = attrval.binary.size; - return V3_OK; - }; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- -// dpf_message - -struct dpf_message : v3_message_cpp { - std::atomic refcounter; - ScopedPointer* self; - ScopedPointer attrlist; - String id; - - dpf_message(ScopedPointer* const s, const char* const id2) - : self(s), - id(id2) - { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_message_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - ref = []V3_API(void* const self) -> uint32_t - { - d_stdout("dpf_message::ref => %p", self); - dpf_message** const messageptr = static_cast(self); - DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); - dpf_message* const message = *messageptr; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - - return ++message->refcounter; - }; - - unref = []V3_API(void* const self) -> uint32_t - { - dpf_message** const messageptr = static_cast(self); - DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); - dpf_message* const message = *messageptr; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - - if (const int refcounter = --message->refcounter) - return refcounter; - - if (message->attrlist != nullptr) - dpf_attribute_list_free(message->attrlist->attrs); - - *message->self = nullptr; - delete messageptr; - return 0; - }; - - msg.get_message_id = []V3_API(void* const self) -> const char* - { - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); - - return message->id; - }; - - msg.set_message_id = []V3_API(void* const self, const char* const id) -> void - { - d_stdout("dpf_message::set_message_id => %p %s", self, id); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - - message->id = id; - }; - - msg.get_attributes = []V3_API(void* const self) -> v3_attribute_list** - { - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); - - if (message->attrlist == nullptr) - message->attrlist = new dpf_attribute_list(); - - return (v3_attribute_list**)&message->attrlist; - }; - } -}; - -v3_message** dpf_message_create(const char* const id) -{ - ScopedPointer* const messageptr = new ScopedPointer; - *messageptr = new dpf_message(messageptr, id); - return static_cast(static_cast(messageptr)); -} - #if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- // dpf_dsp_connection_point @@ -2561,7 +2196,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_connection_point - point.connect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result { d_stdout("dpf_dsp_connection_point::connect => %p %p", self, other); dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; @@ -2578,7 +2213,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { return V3_OK; }; - point.disconnect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result { d_stdout("dpf_dsp_connection_point::disconnect => %p %p", self, other); dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; @@ -2595,7 +2230,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { return V3_OK; }; - point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result + point.notify = []V3_API(void* self, v3_message** message) -> v3_result { dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); @@ -2751,11 +2386,13 @@ struct dpf_edit_controller : v3_edit_controller_cpp { bool initialized; // cached values v3_component_handler** handler; + v3_plugin_base::v3_funknown** hostContext; dpf_edit_controller(ScopedPointer& v) : vst3(v), initialized(false), - handler(nullptr) + handler(nullptr), + hostContext(nullptr) { static const uint8_t* kSupportedInterfacesBase[] = { v3_funknown_iid, @@ -2814,9 +2451,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base - base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result + base.initialize = []V3_API(void* self, v3_plugin_base::v3_funknown** context) -> v3_result { - d_stdout("dpf_edit_controller::initialise => %p %p", self, context); + d_stdout("dpf_edit_controller::initialize => %p %p", self, context); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); @@ -2824,6 +2461,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(! initialized, V3_INVALID_ARG); controller->initialized = true; + controller->hostContext = context; return V3_OK; }; @@ -2837,13 +2475,14 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); controller->initialized = false; + controller->hostContext = nullptr; return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ // v3_edit_controller - controller.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result + ctrl.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2860,7 +2499,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_OK; }; - controller.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result + ctrl.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2875,7 +2514,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NOT_IMPLEMENTED; }; - controller.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result + ctrl.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result { d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2890,7 +2529,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NOT_IMPLEMENTED; }; - controller.get_parameter_count = []V3_API(void* self) -> int32_t + ctrl.get_parameter_count = []V3_API(void* self) -> int32_t { // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2902,7 +2541,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterCount(); }; - controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result + ctrl.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result { // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2914,7 +2553,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterInfo(param_idx, param_info); }; - controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result + ctrl.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { // NOTE very noisy, called many times // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); @@ -2927,7 +2566,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterStringForValue(index, normalised, output); }; - controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id index, int16_t* input, double* output) -> v3_result + ctrl.get_parameter_value_for_string = []V3_API(void* self, v3_param_id index, int16_t* input, double* output) -> v3_result { d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2939,7 +2578,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterValueForString(index, input, output); }; - controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id index, double normalised) -> double + ctrl.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id index, double normalised) -> double { d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2951,7 +2590,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->normalisedParameterToPlain(index, normalised); }; - controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double + ctrl.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double { d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2963,7 +2602,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->plainParameterToNormalised(index, plain); }; - controller.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double + ctrl.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double { // NOTE very noisy, called many times // d_stdout("dpf_edit_controller::get_parameter_normalised => %p %u", self, index); @@ -2976,7 +2615,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterNormalized(index); }; - controller.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result + ctrl.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result { d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2988,7 +2627,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->setParameterNormalized(index, normalised); }; - controller.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result + ctrl.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result { d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -3002,16 +2641,24 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NOT_INITIALISED; }; - controller.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** + ctrl.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** { d_stdout("dpf_edit_controller::create_view => %p %s", self, name); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); #if DISTRHO_PLUGIN_HAS_UI + // plugin must be initialized PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); + // we require host context for message creation + DISTRHO_SAFE_ASSERT_RETURN(controller->hostContext != nullptr, nullptr); + + v3_host_application** host = nullptr; + v3_cpp_obj_query_interface(controller->hostContext, v3_host_application_iid, &host); + DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); + // if there is a component connection, we require it to be active if (controller->connectionComp != nullptr) { @@ -3025,7 +2672,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller->connectionComp->shortcircuit = true; } - v3_plugin_view** const view = dpf_plugin_view_create(vst3->getInstancePointer(), + v3_plugin_view** const view = dpf_plugin_view_create(host, + vst3->getInstancePointer(), vst3->getSampleRate()); DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr); @@ -3285,11 +2933,6 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_component -struct v3_component_cpp : v3_funknown { - v3_plugin_base base; - v3_component comp; -}; - struct dpf_component : v3_component_cpp { std::atomic refcounter; ScopedPointer* self; @@ -3396,9 +3039,9 @@ struct dpf_component : v3_component_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base - base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown* context) -> v3_result + base.initialize = []V3_API(void* self, v3_plugin_base::v3_funknown** context) -> v3_result { - d_stdout("dpf_component::initialise => %p %p", self, context); + d_stdout("dpf_component::initialize => %p %p", self, context); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); @@ -3413,7 +3056,11 @@ struct dpf_component : v3_component_cpp { if (d_lastSampleRate <= 0.0) d_lastSampleRate = 44100.0; - component->vst3 = new PluginVst3(); + // query for host context + v3_host_application** host = nullptr; + v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + + component->vst3 = new PluginVst3(host); return V3_OK; }; @@ -3612,7 +3259,7 @@ struct dpf_factory : v3_plugin_factory_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory - v1.get_factory_info = []V3_API(void*, struct v3_factory_info* const info) -> v3_result + v1.get_factory_info = []V3_API(void*, v3_factory_info* const info) -> v3_result { std::memset(info, 0, sizeof(*info)); DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); @@ -3626,7 +3273,7 @@ struct dpf_factory : v3_plugin_factory_cpp { return 1; }; - v1.get_class_info = []V3_API(void*, int32_t idx, struct v3_class_info* const info) -> v3_result + v1.get_class_info = []V3_API(void*, int32_t idx, v3_class_info* const info) -> v3_result { std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); @@ -3657,7 +3304,7 @@ struct dpf_factory : v3_plugin_factory_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_2 - v2.get_class_info_2 = []V3_API(void*, int32_t idx, struct v3_class_info_2* info) -> v3_result + v2.get_class_info_2 = []V3_API(void*, int32_t idx, v3_class_info_2* info) -> v3_result { std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); @@ -3678,7 +3325,7 @@ struct dpf_factory : v3_plugin_factory_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_3 - v3.get_class_info_utf16 = []V3_API(void*, int32_t idx, struct v3_class_info_3* info) -> v3_result + v3.get_class_info_utf16 = []V3_API(void*, int32_t idx, v3_class_info_3* info) -> v3_result { std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); @@ -3696,7 +3343,7 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; }; - v3.set_host_context = []V3_API (void* self, struct v3_funknown* host) -> v3_result + v3.set_host_context = []V3_API (void* self, v3_funknown* host) -> v3_result { d_stdout("dpf_factory::set_host_context => %p %p", self, host); return V3_NOT_IMPLEMENTED; diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 56bf441d..eb253135 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -22,7 +22,7 @@ #include "../extra/Thread.hpp" #include "travesty/edit_controller.h" -#include "travesty/message.h" +#include "travesty/host.h" #include "travesty/view.h" /* TODO items: @@ -51,7 +51,6 @@ static constexpr const setStateFunc setStateCallback = nullptr; const char* tuid2str(const v3_tuid iid); void strncpy_utf16(int16_t* dst, const char* src, size_t length); -v3_message** dpf_message_create(const char* id); // -------------------------------------------------------------------------------------------------------------------- // custom attribute list struct, used for sending utf8 strings @@ -86,6 +85,7 @@ class UIVst3 : public Thread { public: UIVst3(v3_plugin_view** const view, + v3_host_application** const host, const intptr_t winId, const float scaleFactor, const double sampleRate, @@ -101,6 +101,7 @@ public: instancePointer, scaleFactor), fView(view), + fHostContext(host), fConnection(nullptr), fFrame(nullptr), fReadyForPluginData(false), @@ -224,7 +225,7 @@ public: d_stdout("requesting current plugin state"); - v3_message** const message = dpf_message_create("init"); + v3_message** const message = createMessage("init"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -243,7 +244,7 @@ public: d_stdout("reporting UI closed"); fReadyForPluginData = false; - v3_message** const message = dpf_message_create("close"); + v3_message** const message = createMessage("close"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -357,6 +358,7 @@ private: // VST3 stuff v3_plugin_view** const fView; + v3_host_application** const fHostContext; v3_connection_point** fConnection; v3_plugin_frame** fFrame; @@ -367,11 +369,26 @@ private: // ---------------------------------------------------------------------------------------------------------------- // helper functions called during message passing + v3_message** createMessage(const char* const id) const + { + DISTRHO_SAFE_ASSERT_RETURN(fHostContext != nullptr, nullptr); + + v3_tuid iid; + memcpy(iid, v3_message_iid, sizeof(v3_tuid)); + v3_message** msg = nullptr; + const v3_result res = v3_cpp_obj(fHostContext)->create_instance(fHostContext, iid, iid, (void**)&msg); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr); + + v3_cpp_obj(msg)->set_message_id(msg, id); + return msg; + } + void requestMorePluginData() const { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - v3_message** const message = dpf_message_create("idle"); + v3_message** const message = createMessage("idle"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -390,7 +407,7 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - v3_message** const message = dpf_message_create("parameter-edit"); + v3_message** const message = createMessage("parameter-edit"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -413,7 +430,7 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - v3_message** const message = dpf_message_create("parameter-set"); + v3_message** const message = createMessage("parameter-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -461,7 +478,7 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - v3_message** const message = dpf_message_create("midi"); + v3_message** const message = createMessage("midi"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -490,7 +507,7 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - v3_message** const message = dpf_message_create("state-set"); + v3_message** const message = createMessage("state-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); @@ -631,7 +648,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_connection_point - point.connect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result { d_stdout("dpf_ui_connection_point::connect => %p %p", self, other); dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; @@ -646,7 +663,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { return V3_OK; }; - point.disconnect = []V3_API(void* self, struct v3_connection_point** other) -> v3_result + point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result { d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other); dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; @@ -661,7 +678,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { return V3_OK; }; - point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result + point.notify = []V3_API(void* self, v3_message** message) -> v3_result { dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); @@ -684,13 +701,16 @@ struct dpf_plugin_view : v3_plugin_view_cpp { ScopedPointer connection; ScopedPointer uivst3; // cached values + v3_host_application** const host; void* const instancePointer; double sampleRate; v3_plugin_frame** frame = nullptr; - dpf_plugin_view(ScopedPointer* selfptr, void* const instance, const double sr) + dpf_plugin_view(ScopedPointer* selfptr, + v3_host_application** const h, void* const instance, const double sr) : refcounter(1), self(selfptr), + host(h), instancePointer(instance), sampleRate(sr), frame(nullptr) @@ -808,6 +828,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)view->self, + view->host, (uintptr_t)parent, scaleFactor, view->sampleRate, @@ -956,12 +977,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (called from plugin side) -v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate); +v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate); -v3_plugin_view** dpf_plugin_view_create(void* const instancePointer, const double sampleRate) +v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host, + void* const instancePointer, + const double sampleRate) { ScopedPointer* const viewptr = new ScopedPointer; - *viewptr = new dpf_plugin_view(viewptr, instancePointer, sampleRate); + *viewptr = new dpf_plugin_view(viewptr, host, instancePointer, sampleRate); return (v3_plugin_view**)static_cast(viewptr); } diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 43b66291..bc7b5fd1 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -160,9 +160,9 @@ enum { */ struct v3_funknown { - v3_result (V3_API *query_interface)(void* self, const v3_tuid iid, void** obj); - uint32_t (V3_API *ref)(void* self); - uint32_t (V3_API *unref)(void* self); + v3_result (V3_API* query_interface)(void* self, const v3_tuid iid, void** obj); + uint32_t (V3_API* ref)(void* self); + uint32_t (V3_API* unref)(void* self); }; static constexpr const v3_tuid v3_funknown_iid = @@ -175,8 +175,8 @@ static constexpr const v3_tuid v3_funknown_iid = struct v3_plugin_base { struct v3_funknown; - v3_result (V3_API *initialise)(void* self, struct v3_funknown* context); - v3_result (V3_API *terminate)(void* self); + v3_result (V3_API* initialize)(void* self, struct v3_funknown** context); + v3_result (V3_API* terminate)(void* self); }; static constexpr const v3_tuid v3_plugin_base_iid = diff --git a/distrho/src/travesty/component.h b/distrho/src/travesty/component.h index 7d17bf09..4e73c477 100644 --- a/distrho/src/travesty/component.h +++ b/distrho/src/travesty/component.h @@ -107,4 +107,28 @@ struct v3_component { static constexpr const v3_tuid v3_component_iid = V3_ID(0xE831FF31, 0xF2D54301, 0x928EBBEE, 0x25697802); +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_component_cpp : v3_funknown { + v3_plugin_base base; + v3_component comp; +}; + +template<> inline +constexpr v3_component* v3_cpp_obj(v3_component** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*5)); +} + +#endif + #include "align_pop.h" diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 642e2080..055e7d71 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -118,7 +118,7 @@ static constexpr const v3_tuid v3_midi_mapping_iid = struct v3_edit_controller_cpp : v3_funknown { v3_plugin_base base; - v3_edit_controller controller; + v3_edit_controller ctrl; }; struct v3_midi_mapping_cpp : v3_funknown { diff --git a/distrho/src/travesty/host.h b/distrho/src/travesty/host.h new file mode 100644 index 00000000..032a7eed --- /dev/null +++ b/distrho/src/travesty/host.h @@ -0,0 +1,49 @@ +/* + * travesty, pure C VST3-compatible interface + * Copyright (C) 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. + */ + +#pragma once + +#include "message.h" + +#include "align_push.h" + +/** + * connection point + */ + +struct v3_host_application { + struct v3_funknown; + + v3_result (V3_API* get_name)(void* self, v3_str_128 name); // wtf? + v3_result (V3_API* create_instance)(void* self, v3_tuid cid, v3_tuid iid, void** obj); +}; + +static constexpr const v3_tuid v3_host_application_iid = + V3_ID(0x58E595CC, 0xDB2D4969, 0x8B6AAF8C, 0x36A664E5); + +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_host_application_cpp : v3_funknown { + v3_host_application app; +}; + +#endif + +#include "align_pop.h" diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h index fa013198..733a0e2b 100644 --- a/distrho/src/travesty/message.h +++ b/distrho/src/travesty/message.h @@ -27,14 +27,14 @@ struct v3_attribute_list { struct v3_funknown; - v3_result (V3_API *set_int)(void* self, const char* id, int64_t value); - v3_result (V3_API *get_int)(void* self, const char* id, int64_t* value); - v3_result (V3_API *set_float)(void* self, const char* id, double value); - v3_result (V3_API *get_float)(void* self, const char* id, double* value); - v3_result (V3_API *set_string)(void* self, const char* id, const int16_t* string); - v3_result (V3_API *get_string)(void* self, const char* id, int16_t* string, uint32_t size); - v3_result (V3_API *set_binary)(void* self, const char* id, const void* data, uint32_t size); - v3_result (V3_API *get_binary)(void* self, const char* id, const void** data, uint32_t* size); + v3_result (V3_API* set_int)(void* self, const char* id, int64_t value); + v3_result (V3_API* get_int)(void* self, const char* id, int64_t* value); + v3_result (V3_API* set_float)(void* self, const char* id, double value); + v3_result (V3_API* get_float)(void* self, const char* id, double* value); + v3_result (V3_API* set_string)(void* self, const char* id, const int16_t* string); + v3_result (V3_API* get_string)(void* self, const char* id, int16_t* string, uint32_t size); + v3_result (V3_API* set_binary)(void* self, const char* id, const void* data, uint32_t size); + v3_result (V3_API* get_binary)(void* self, const char* id, const void** data, uint32_t* size); }; static constexpr const v3_tuid v3_attribute_list_iid = @@ -47,9 +47,9 @@ static constexpr const v3_tuid v3_attribute_list_iid = struct v3_message { struct v3_funknown; - const char* (V3_API *get_message_id)(void* self); - void (V3_API *set_message_id)(void* self, const char* id); - v3_attribute_list** (V3_API *get_attributes)(void* self); + const char* (V3_API* get_message_id)(void* self); + void (V3_API* set_message_id)(void* self, const char* id); + v3_attribute_list** (V3_API* get_attributes)(void* self); }; static constexpr const v3_tuid v3_message_iid = @@ -62,9 +62,9 @@ static constexpr const v3_tuid v3_message_iid = struct v3_connection_point { struct v3_funknown; - v3_result (V3_API *connect)(void* self, struct v3_connection_point** other); - v3_result (V3_API *disconnect)(void* self, struct v3_connection_point** other); - v3_result (V3_API *notify)(void* self, struct v3_message** message); + v3_result (V3_API* connect)(void* self, struct v3_connection_point** other); + v3_result (V3_API* disconnect)(void* self, struct v3_connection_point** other); + v3_result (V3_API* notify)(void* self, struct v3_message** message); }; static constexpr const v3_tuid v3_connection_point_iid = From a1926680fdc76cd72b61e4212f68c9abe202c36a Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Oct 2021 21:18:08 +0100 Subject: [PATCH 130/504] VST3: Fix memory leak and utf16 string messages Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 60 +++++++++++++++++++------------ distrho/src/DistrhoUIVST3.cpp | 36 +++++++++---------- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 22c6944a..69cf01ba 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -101,21 +101,6 @@ static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, 0 }; static dpf_tuid dpf_tuid_processor = { dpf_id_entry, dpf_id_proc, 0, 0 }; static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; -// -------------------------------------------------------------------------------------------------------------------- -// custom attribute list struct, used for sending utf8 strings - -struct v3_attribute_list_utf8 { - struct v3_funknown; - v3_result (V3_API *set_string_utf8)(void* self, const char* id, const char* string); - v3_result (V3_API *get_string_utf8)(void* self, const char* id, char* string, uint32_t size); -}; - -static constexpr const v3_tuid v3_attribute_list_utf8_iid = - V3_ID(d_cconst('D','P','F',' '), - d_cconst('c','l','a','s'), - d_cconst('a','t','t','r'), - d_cconst('u','t','f','8')); - // -------------------------------------------------------------------------------------------------------------------- // Utility functions @@ -145,8 +130,6 @@ const char* tuid2str(const v3_tuid iid) return "{v3_audio_processor}"; if (v3_tuid_match(iid, v3_attribute_list_iid)) return "{v3_attribute_list_iid}"; - if (v3_tuid_match(iid, v3_attribute_list_utf8_iid)) - return "{v3_attribute_list_utf8_iid|CUSTOM}"; if (v3_tuid_match(iid, v3_bstream_iid)) return "{v3_bstream}"; if (v3_tuid_match(iid, v3_component_iid)) @@ -288,6 +271,37 @@ static constexpr void (*const snprintf_u32)(char*, uint32_t, size_t) = snprintf_ static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snprintf_utf16_t; static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t; +// -------------------------------------------------------------------------------------------------------------------- +// handy way to create a utf16 string during the current function scope, used for DSP->UI communication + +struct ScopedUTF16String { + int16_t* str; + ScopedUTF16String(const char* const s) noexcept; + ~ScopedUTF16String() noexcept; + operator int16_t*() const noexcept; +}; + +// -------------------------------------------------------------------------------------------------------------------- + +ScopedUTF16String::ScopedUTF16String(const char* const s) noexcept + : str(nullptr) +{ + const size_t len = strlen(s); + str = (int16_t*)malloc(sizeof(int16_t) * len); + DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,); + strncpy_utf16(str, s, len); +} + +ScopedUTF16String::~ScopedUTF16String() noexcept +{ + std::free(str); +} + +ScopedUTF16String::operator int16_t*() const noexcept +{ + return str; +} + // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (implemented on UI side) @@ -1967,6 +1981,7 @@ private: v3_cpp_obj(attrlist)->set_float(attrlist, "value", sampleRate); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -1983,6 +1998,7 @@ private: v3_cpp_obj(attrlist)->set_float(attrlist, "value", value); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -1994,15 +2010,12 @@ private: v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); - v3_attribute_list_utf8** utf8attrlist = nullptr; - DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrlist, v3_attribute_list_utf8_iid, &utf8attrlist) == V3_OK,); - DISTRHO_SAFE_ASSERT_RETURN(utf8attrlist != nullptr,); - v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); - v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "key", key); - v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "value", 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_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -2017,6 +2030,7 @@ private: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } #endif diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index eb253135..7458251f 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -52,21 +52,13 @@ static constexpr const setStateFunc setStateCallback = nullptr; const char* tuid2str(const v3_tuid iid); void strncpy_utf16(int16_t* dst, const char* src, size_t length); -// -------------------------------------------------------------------------------------------------------------------- -// custom attribute list struct, used for sending utf8 strings - -struct v3_attribute_list_utf8 { - struct v3_funknown; - v3_result (V3_API *set_string_utf8)(void* self, const char* id, const char* string); - v3_result (V3_API *get_string_utf8)(void* self, const char* id, char* string, uint32_t size); +struct ScopedUTF16String { + int16_t* str; + ScopedUTF16String(const char* const s) noexcept; + ~ScopedUTF16String() noexcept; + operator int16_t*() const noexcept; }; -static constexpr const v3_tuid v3_attribute_list_utf8_iid = - V3_ID(d_cconst('D','P','F',' '), - d_cconst('c','l','a','s'), - d_cconst('a','t','t','r'), - d_cconst('u','t','f','8')); - // -------------------------------------------------------------------------------------------------------------------- /** @@ -234,6 +226,7 @@ public: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -253,6 +246,7 @@ public: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); fConnection = nullptr; @@ -397,6 +391,7 @@ private: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -418,6 +413,7 @@ private: v3_cpp_obj(attrlist)->set_int(attrlist, "started", started ? 1 : 0); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -441,6 +437,7 @@ private: v3_cpp_obj(attrlist)->set_float(attrlist, "value", realValue); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -493,6 +490,7 @@ private: v3_cpp_obj(attrlist)->set_binary(attrlist, "data", midiData, sizeof(midiData)); v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -513,15 +511,12 @@ private: v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); - v3_attribute_list_utf8** utf8attrlist = nullptr; - DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrlist, v3_attribute_list_utf8_iid, &utf8attrlist) == V3_OK,); - DISTRHO_SAFE_ASSERT_RETURN(utf8attrlist != nullptr,); - v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); - v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "key", key); - v3_cpp_obj(utf8attrlist)->set_string_utf8(utf8attrlist, "value", 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_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -538,6 +533,9 @@ private: * VST3 low-level pointer thingies follow, proceed with care. */ +// -------------------------------------------------------------------------------------------------------------------- +// dpf_ + // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale From 8904662794f5a201321033ddc305f9211eeb60a7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Oct 2021 21:49:52 +0100 Subject: [PATCH 131/504] VST3: Implement view timer handler, hook into host run loop Signed-off-by: falkTX --- Makefile.plugins.mk | 3 +- distrho/src/DistrhoPluginVST3.cpp | 6 + distrho/src/DistrhoUIVST3.cpp | 284 ++++++++++++++++++------------ distrho/src/travesty/view.h | 84 +++++++-- 4 files changed, 252 insertions(+), 125 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index d6a24653..0ba0ce4d 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -399,8 +399,7 @@ $(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST3 plugin for $(NAME)" - # NOTE TESTING -lpthread is for testing, remove later - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -lpthread $(SYMBOLS_VST3) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 69cf01ba..2dad509f 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -140,6 +140,8 @@ const char* tuid2str(const v3_tuid iid) return "{v3_connection_point_iid}"; if (v3_tuid_match(iid, v3_edit_controller_iid)) return "{v3_edit_controller}"; + if (v3_tuid_match(iid, v3_event_handler_iid)) + return "{v3_event_handler_iid}"; if (v3_tuid_match(iid, v3_event_list_iid)) return "{v3_event_list}"; if (v3_tuid_match(iid, v3_funknown_iid)) @@ -172,6 +174,10 @@ const char* tuid2str(const v3_tuid iid) return "{v3_plugin_view_parameter_finder}"; if (v3_tuid_match(iid, v3_process_context_requirements_iid)) return "{v3_process_context_requirements}"; + if (v3_tuid_match(iid, v3_run_loop_iid)) + return "{v3_run_loop_iid}"; + if (v3_tuid_match(iid, v3_timer_handler_iid)) + return "{v3_timer_handler_iid}"; if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_class}"; diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 7458251f..281dd934 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -18,9 +18,6 @@ #include -// TESTING awful idea dont reuse -#include "../extra/Thread.hpp" - #include "travesty/edit_controller.h" #include "travesty/host.h" #include "travesty/view.h" @@ -32,7 +29,6 @@ * - host-side resize * - oddities with init and size callback (triggered too early?) * - win/mac native idle loop - * - linux runloop */ START_NAMESPACE_DISTRHO @@ -73,7 +69,7 @@ struct ScopedUTF16String { * * The low-level VST3 stuff comes after. */ -class UIVst3 : public Thread +class UIVst3 { public: UIVst3(v3_plugin_view** const view, @@ -99,34 +95,14 @@ public: fReadyForPluginData(false), fScaleFactor(scaleFactor) { - // TESTING awful idea dont reuse - startThread(); } - ~UIVst3() override + ~UIVst3() { - stopThread(5000); - if (fConnection != nullptr) disconnect(); } - // TESTING awful idea dont reuse - void run() override - { - while (! shouldThreadExit()) - { - if (fReadyForPluginData) - { - fReadyForPluginData = false; - requestMorePluginData(); - } - - fUI.plugin_idle(); - d_msleep(50); - } - } - // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view interface calls @@ -193,19 +169,6 @@ public: return V3_NOT_IMPLEMENTED; } - // ---------------------------------------------------------------------------------------------------------------- - // v3_plugin_view_content_scale_steinberg interface calls - - v3_result setContentScaleFactor(const float factor) - { - if (d_isEqual(fScaleFactor, factor)) - return V3_OK; - - fScaleFactor = factor; - fUI.notifyScaleFactorChanged(factor); - return V3_OK; - } - // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point interface calls @@ -345,6 +308,33 @@ public: } // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_view_content_scale_steinberg interface calls + + v3_result setContentScaleFactor(const float factor) + { + if (d_isEqual(fScaleFactor, factor)) + return V3_OK; + + fScaleFactor = factor; + fUI.notifyScaleFactorChanged(factor); + return V3_OK; + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_timer_handler interface calls + + void onTimer() + { + if (fReadyForPluginData) + { + fReadyForPluginData = false; + requestMorePluginData(); + } + + fUI.plugin_idle(); + } + + // ---------------------------------------------------------------------------------------------------------------- private: // Plugin UI @@ -534,7 +524,91 @@ private: */ // -------------------------------------------------------------------------------------------------------------------- -// dpf_ +// dpf_ui_connection_point + +struct dpf_ui_connection_point : v3_connection_point_cpp { + ScopedPointer& uivst3; + v3_connection_point** other; + + dpf_ui_connection_point(ScopedPointer& v) + : uivst3(v), + other(nullptr) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_connection_point_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_ui_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_connection_point + + point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result + { + d_stdout("dpf_ui_connection_point::connect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); + + point->other = other; + + if (UIVst3* const uivst3 = point->uivst3) + uivst3->connect(other); + + return V3_OK; + }; + + point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result + { + d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + + point->other = nullptr; + + if (UIVst3* const uivst3 = point->uivst3) + uivst3->disconnect(); + + return V3_OK; + }; + + point.notify = []V3_API(void* self, v3_message** message) -> v3_result + { + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + + UIVst3* const uivst3 = point->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + + return uivst3->notify(message); + }; + } +}; // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale @@ -603,89 +677,59 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { }; // -------------------------------------------------------------------------------------------------------------------- -// dpf_ui_connection_point +// dpf_timer_handler -struct dpf_ui_connection_point : v3_connection_point_cpp { +struct dpf_timer_handler : v3_timer_handler_cpp { ScopedPointer& uivst3; - v3_connection_point** other; - dpf_ui_connection_point(ScopedPointer& v) - : uivst3(v), - other(nullptr) + dpf_timer_handler(ScopedPointer& v) + : uivst3(v) { + query_interface = query_interface_fn; + ref = ref_fn; + unref = unref_fn; + handler.on_timer = on_timer; + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static v3_result V3_API query_interface_fn(void* self, const v3_tuid iid, void** iface) + { + d_stdout("dpf_plugin_view_content_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + static const uint8_t* kSupportedInterfaces[] = { v3_funknown_iid, - v3_connection_point_iid + v3_plugin_view_content_scale_iid }; - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + for (const uint8_t* interface_iid : kSupportedInterfaces) { - d_stdout("dpf_ui_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) + if (v3_tuid_match(interface_iid, iid)) { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } + *iface = self; + return V3_OK; } + } - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_connection_point - - point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result - { - d_stdout("dpf_ui_connection_point::connect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); - - point->other = other; - - if (UIVst3* const uivst3 = point->uivst3) - uivst3->connect(other); - - return V3_OK; - }; - - point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result - { - d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); - - point->other = nullptr; - - if (UIVst3* const uivst3 = point->uivst3) - uivst3->disconnect(); + return V3_NO_INTERFACE; + } - return V3_OK; - }; + // there is only a single instance of this, so we don't have to care here + static uint32_t V3_API ref_fn(void*) { return 1; }; + static uint32_t V3_API unref_fn(void*) { return 0; }; - point.notify = []V3_API(void* self, v3_message** message) -> v3_result - { - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + // ---------------------------------------------------------------------------------------------------------------- + // v3_timer_handler - UIVst3* const uivst3 = point->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + static void V3_API on_timer(void* self) + { + dpf_timer_handler* const handler = *(dpf_timer_handler**)self; + DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); - return uivst3->notify(message); - }; + handler->uivst3->onTimer(); } }; @@ -695,8 +739,9 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic refcounter; ScopedPointer* self; - ScopedPointer scale; ScopedPointer connection; + ScopedPointer scale; + ScopedPointer timer; ScopedPointer uivst3; // cached values v3_host_application** const host; @@ -824,6 +869,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { + // find host run loop to plug ourselves into (required) + DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG); + + v3_run_loop** runloop = nullptr; + v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); + DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); + const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)view->self, view->host, @@ -837,6 +889,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->uivst3->connect(point->other); view->uivst3->setFrame(view->frame); + + // register a timer host run loop stuff + view->timer = new dpf_timer_handler(view->uivst3); + v3_cpp_obj(runloop)->register_timer(runloop, (v3_timer_handler**)&view->timer, 16); + return V3_OK; } } @@ -850,6 +907,17 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); + + // unregister our timer as needed + if (view->timer != nullptr) + { + v3_run_loop** runloop = nullptr; + if (v3_cpp_obj_query_interface(view->host, v3_run_loop_iid, &runloop) == V3_OK && runloop != nullptr) + v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); + + view->timer = nullptr; + } + view->uivst3 = nullptr; return V3_OK; }; diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index fc7b2ddb..0abbd055 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -50,18 +50,18 @@ struct v3_plugin_frame; struct v3_plugin_view { struct v3_funknown; - v3_result (V3_API *is_platform_type_supported)(void* self, const char* platform_type); - v3_result (V3_API *attached)(void* self, void* parent, const char* platform_type); - v3_result (V3_API *removed)(void* self); - v3_result (V3_API *on_wheel)(void* self, float distance); - v3_result (V3_API *on_key_down)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); - v3_result (V3_API *on_key_up)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); - v3_result (V3_API *get_size)(void* self, struct v3_view_rect*); - v3_result (V3_API *on_size)(void* self, struct v3_view_rect*); - v3_result (V3_API *on_focus)(void* self, v3_bool state); - v3_result (V3_API *set_frame)(void* self, struct v3_plugin_frame**); - v3_result (V3_API *can_resize)(void* self); - v3_result (V3_API *check_size_constraint)(void* self, struct v3_view_rect*); + v3_result (V3_API* is_platform_type_supported)(void* self, const char* platform_type); + v3_result (V3_API* attached)(void* self, void* parent, const char* platform_type); + v3_result (V3_API* removed)(void* self); + v3_result (V3_API* on_wheel)(void* self, float distance); + v3_result (V3_API* on_key_down)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); + v3_result (V3_API* on_key_up)(void* self, int16_t key_char, int16_t key_code, int16_t modifiers); + v3_result (V3_API* get_size)(void* self, struct v3_view_rect*); + v3_result (V3_API* on_size)(void* self, struct v3_view_rect*); + v3_result (V3_API* on_focus)(void* self, v3_bool state); + v3_result (V3_API* set_frame)(void* self, struct v3_plugin_frame**); + v3_result (V3_API* can_resize)(void* self); + v3_result (V3_API* check_size_constraint)(void* self, struct v3_view_rect*); }; static constexpr const v3_tuid v3_plugin_view_iid = @@ -74,7 +74,7 @@ static constexpr const v3_tuid v3_plugin_view_iid = struct v3_plugin_frame { struct v3_funknown; - v3_result (V3_API *resize_view)(void* self, struct v3_plugin_view**, struct v3_view_rect*); + v3_result (V3_API* resize_view)(void* self, struct v3_plugin_view**, struct v3_view_rect*); }; static constexpr const v3_tuid v3_plugin_frame_iid = @@ -88,7 +88,7 @@ static constexpr const v3_tuid v3_plugin_frame_iid = struct v3_plugin_view_content_scale { struct v3_funknown; - v3_result (V3_API *set_content_scale_factor)(void* self, float factor); + v3_result (V3_API* set_content_scale_factor)(void* self, float factor); }; static constexpr const v3_tuid v3_plugin_view_content_scale_iid = @@ -101,12 +101,54 @@ static constexpr const v3_tuid v3_plugin_view_content_scale_iid = struct v3_plugin_view_parameter_finder { struct v3_funknown; - v3_result (V3_API *find_parameter)(void* self, int32_t x, int32_t y, v3_param_id *); + v3_result (V3_API* find_parameter)(void* self, int32_t x, int32_t y, v3_param_id *); }; static constexpr const v3_tuid v3_plugin_view_parameter_finder_iid = V3_ID(0x0F618302, 0x215D4587, 0xA512073C, 0x77B9D383); +/** + * linux event handler + */ + +struct v3_event_handler { + struct v3_funknown; + + void (V3_API* on_fd_is_set)(void* self, int fd); +}; + +static constexpr const v3_tuid v3_event_handler_iid = + V3_ID(0x561E65C9, 0x13A0496F, 0x813A2C35, 0x654D7983); + +/** + * linux timer handler + */ + +struct v3_timer_handler { + struct v3_funknown; + + void (V3_API* on_timer)(void* self); +}; + +static constexpr const v3_tuid v3_timer_handler_iid = + V3_ID(0x10BDD94F, 0x41424774, 0x821FAD8F, 0xECA72CA9); + +/** + * linux host run loop + */ + +struct v3_run_loop { + struct v3_funknown; + + v3_result (V3_API* register_event_handler)(void* self, v3_event_handler** handler, int fd); + v3_result (V3_API* unregister_event_handler)(void* self, v3_event_handler** handler); + v3_result (V3_API* register_timer)(void* self, v3_timer_handler** handler, uint64_t ms); + v3_result (V3_API* unregister_timer)(void* self, v3_timer_handler** handler); +}; + +static constexpr const v3_tuid v3_run_loop_iid = + V3_ID(0x18C35366, 0x97764F1A, 0x9C5B8385, 0x7A871389); + #ifdef __cplusplus /** @@ -129,4 +171,16 @@ struct v3_plugin_view_parameter_finder_cpp : v3_funknown { v3_plugin_view_parameter_finder finder; }; +struct v3_event_handler_cpp : v3_funknown { + v3_event_handler handler; +}; + +struct v3_timer_handler_cpp : v3_funknown { + v3_timer_handler handler; +}; + +struct v3_run_loop_cpp : v3_funknown { + v3_run_loop loop; +}; + #endif From 318c76dd544f6e6b1254053dc3d206420831dccb Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Oct 2021 21:52:40 +0100 Subject: [PATCH 132/504] Fix a compiler warning Signed-off-by: falkTX --- utils/lv2-ttl-generator/lv2_ttl_generator.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils/lv2-ttl-generator/lv2_ttl_generator.c b/utils/lv2-ttl-generator/lv2_ttl_generator.c index 3be02a1c..fb28fb24 100644 --- a/utils/lv2-ttl-generator/lv2_ttl_generator.c +++ b/utils/lv2-ttl-generator/lv2_ttl_generator.c @@ -63,7 +63,14 @@ int main(int argc, char* argv[]) } #ifdef TTL_GENERATOR_WINDOWS +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" +# endif const TTL_Generator_Function ttlFn = (TTL_Generator_Function)GetProcAddress(handle, "lv2_generate_ttl"); +# if defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic pop +# endif #else const TTL_Generator_Function ttlFn = (TTL_Generator_Function)dlsym(handle, "lv2_generate_ttl"); #endif From c22ecc6c57bbb4a383b7aa6b0c9cb3e44bb0aca8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Oct 2021 23:11:37 +0100 Subject: [PATCH 133/504] VST3: Handle UI on macOS and Windows Signed-off-by: falkTX --- dgl/Application.hpp | 1 + dgl/src/ApplicationPrivateData.cpp | 5 +++ dgl/src/ApplicationPrivateData.hpp | 3 ++ distrho/src/DistrhoUIInternal.hpp | 22 ++++++++++++ distrho/src/DistrhoUIPrivateData.hpp | 8 ++++- distrho/src/DistrhoUIVST3.cpp | 50 ++++++++++++++++++++++++++-- 6 files changed, 85 insertions(+), 4 deletions(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 0979987a..87937cc7 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -107,6 +107,7 @@ public: private: struct PrivateData; PrivateData* const pData; + friend class PluginApplication; friend class Window; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 1de1fc76..f573c2f7 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -118,6 +118,11 @@ void Application::PrivateData::idle(const uint timeoutInMs) puglUpdate(world, timeoutInSeconds); } + triggerIdleCallbacks(); +} + +void Application::PrivateData::triggerIdleCallbacks() +{ for (std::list::iterator it = idleCallbacks.begin(), ite = idleCallbacks.end(); it != ite; ++it) { IdleCallback* const idleCallback(*it); diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index e35328d3..0d2993c4 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -84,6 +84,9 @@ struct Application::PrivateData { /** Run Pugl world update for @a timeoutInMs, and then each idle callback in order of registration. */ void idle(uint timeoutInMs); + /** Run each idle callback without updating pugl world. */ + void triggerIdleCallbacks(); + /** Set flag indicating application is quitting, and close all windows in reverse order of registration. For standalone mode only. */ void quit(); diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 46de9858..35606a98 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -234,6 +234,28 @@ public: // ------------------------------------------------------------------- +#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) + void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs) + { + uiData->window->addIdleCallback(cb, timerFrequencyInMs); + } + + void removeIdleCallbackForVST3(IdleCallback* const cb) + { + uiData->window->removeIdleCallback(cb); + } + + void idleForVST3() + { + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); + + uiData->app.triggerIdleCallbacks(); + ui->uiIdle(); + } +#endif + + // ------------------------------------------------------------------- + void setWindowTitle(const char* const uiTitle) { uiData->window->setTitle(uiTitle); diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index ca8a96f9..93a8a89a 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -22,7 +22,7 @@ #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # include "../extra/Sleep.hpp" #else -# include "../../dgl/Application.hpp" +# include "../../dgl/src/ApplicationPrivateData.hpp" # include "../../dgl/src/WindowPrivateData.hpp" # include "../../dgl/src/pugl.hpp" #endif @@ -98,6 +98,7 @@ struct PluginApplication // these are not needed void idle() {} void quit() {} + void triggerIdleCallbacks() {} DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) }; @@ -119,6 +120,11 @@ public: setClassName(className); } + void triggerIdleCallbacks() + { + pData->triggerIdleCallbacks(); + } + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) }; #endif diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 281dd934..42a708de 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -28,9 +28,16 @@ * - size constraints * - host-side resize * - oddities with init and size callback (triggered too early?) - * - win/mac native idle loop */ +#if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) +# define DPF_VST3_USING_HOST_RUN_LOOP +#endif + +#ifndef DPF_VST3_TIMER_INTERVAL +# define DPF_VST3_TIMER_INTERVAL 16 /* ~60 fps */ +#endif + START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- @@ -70,6 +77,9 @@ struct ScopedUTF16String { * The low-level VST3 stuff comes after. */ class UIVst3 +#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + : public IdleCallback +#endif { public: UIVst3(v3_plugin_view** const view, @@ -95,10 +105,16 @@ public: fReadyForPluginData(false), fScaleFactor(scaleFactor) { +#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); +#endif } ~UIVst3() { +#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + fUI.removeIdleCallbackForVST3(this); +#endif if (fConnection != nullptr) disconnect(); } @@ -320,6 +336,21 @@ public: return V3_OK; } + // ---------------------------------------------------------------------------------------------------------------- + // special idle callback on macOS and Windows + +#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + void idleCallback() override + { + if (fReadyForPluginData) + { + fReadyForPluginData = false; + requestMorePluginData(); + } + + fUI.idleForVST3(); + } +#else // ---------------------------------------------------------------------------------------------------------------- // v3_timer_handler interface calls @@ -333,6 +364,7 @@ public: fUI.plugin_idle(); } +#endif // ---------------------------------------------------------------------------------------------------------------- @@ -676,6 +708,7 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { } }; +#ifdef DPF_VST3_USING_HOST_RUN_LOOP // -------------------------------------------------------------------------------------------------------------------- // dpf_timer_handler @@ -732,6 +765,7 @@ struct dpf_timer_handler : v3_timer_handler_cpp { handler->uivst3->onTimer(); } }; +#endif // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view @@ -741,7 +775,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { ScopedPointer* self; ScopedPointer connection; ScopedPointer scale; +#ifdef DPF_VST3_USING_HOST_RUN_LOOP ScopedPointer timer; +#endif ScopedPointer uivst3; // cached values v3_host_application** const host; @@ -869,12 +905,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { - // find host run loop to plug ourselves into (required) + #ifdef DPF_VST3_USING_HOST_RUN_LOOP + // find host run loop to plug ourselves into (required on some systems) DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG); v3_run_loop** runloop = nullptr; v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); + #endif const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)view->self, @@ -890,9 +928,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->uivst3->setFrame(view->frame); + #ifdef DPF_VST3_USING_HOST_RUN_LOOP // register a timer host run loop stuff view->timer = new dpf_timer_handler(view->uivst3); - v3_cpp_obj(runloop)->register_timer(runloop, (v3_timer_handler**)&view->timer, 16); + v3_cpp_obj(runloop)->register_timer(runloop, + (v3_timer_handler**)&view->timer, + DPF_VST3_TIMER_INTERVAL); + #endif return V3_OK; } @@ -908,6 +950,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); + #ifdef DPF_VST3_USING_HOST_RUN_LOOP // unregister our timer as needed if (view->timer != nullptr) { @@ -917,6 +960,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->timer = nullptr; } + #endif view->uivst3 = nullptr; return V3_OK; From 1ce0d89cc0625d25a92bf6f6c4ff3fd4ba404c33 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 03:11:55 +0100 Subject: [PATCH 134/504] Make the VST3 implementation C++98 compatible Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 1955 +++++++++++++----------- distrho/src/DistrhoUIVST3.cpp | 702 +++++---- distrho/src/travesty/audio_processor.h | 24 + distrho/src/travesty/base.h | 6 +- distrho/src/travesty/factory.h | 14 + distrho/src/travesty/message.h | 4 +- 6 files changed, 1441 insertions(+), 1264 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 2dad509f..d92dd48c 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -37,7 +37,21 @@ #include "travesty/factory.h" #include "travesty/host.h" -#include +#ifdef DISTRHO_PROPER_CPP11_SUPPORT +# include +#else +// quick and dirty std::atomic replacement for the things we need +namespace std { + struct atomic_int { + volatile int value; + explicit atomic_int(volatile int v) noexcept : value(v) {} + int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } + int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } + operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); } + }; +} +#endif + #include #include #include @@ -80,7 +94,9 @@ typedef std::map StringMap; // custom v3_tuid compatible type typedef uint32_t dpf_tuid[4]; +#ifdef DISTRHO_PROPER_CPP11_SUPPORT static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch"); +#endif // -------------------------------------------------------------------------------------------------------------------- // custom, constant uids related to DPF @@ -246,16 +262,16 @@ void strncpy_utf16(int16_t* const dst, const char* const src, const size_t lengt // -------------------------------------------------------------------------------------------------------------------- -template -static void snprintf_t(char* const dst, const T value, const size_t size) +template +static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size) { DISTRHO_SAFE_ASSERT_RETURN(size > 0,); std::snprintf(dst, size-1, format, value); dst[size-1] = '\0'; } -template -static void snprintf_utf16_t(int16_t* const dst, const T value, const size_t size) +template +static 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,); @@ -269,16 +285,26 @@ static void snprintf_utf16_t(int16_t* const dst, const T value, const size_t siz std::free(tmpbuf); } -static constexpr const char format_i32[] = "%d"; -static constexpr const char format_f32[] = "%f"; -static constexpr const char format_u32[] = "%u"; +static inline +void snprintf_u32(char* const dst, const uint32_t value, const size_t size) +{ + return snprintf_t(dst, value, "%u", size); +} + +static inline +void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size) +{ + return snprintf_utf16_t(dst, value, "%f", size); +} -static constexpr void (*const snprintf_u32)(char*, uint32_t, size_t) = snprintf_t; -static constexpr void (*const snprintf_f32_utf16)(int16_t*, float, size_t) = snprintf_utf16_t; -static constexpr void (*const snprintf_u32_utf16)(int16_t*, uint32_t, size_t) = snprintf_utf16_t; +static inline +void snprintf_u32_utf16(int16_t* const dst, const uint32_t value, const size_t size) +{ + return snprintf_utf16_t(dst, value, "%u", size); +} // -------------------------------------------------------------------------------------------------------------------- -// handy way to create a utf16 string during the current function scope, used for DSP->UI communication +// handy way to create a utf16 string on the current function scope, used for message strings struct ScopedUTF16String { int16_t* str; @@ -333,11 +359,18 @@ class PluginVst3 * MIDI will have a single bus, nothing special there. */ struct BusInfo { - uint8_t audio = 0; // either 0 or 1 - uint8_t sidechain = 0; // either 0 or 1 - uint32_t numMainAudio = 0; - uint32_t numSidechain = 0; - uint32_t numCV = 0; + uint8_t audio; // either 0 or 1 + uint8_t sidechain; // either 0 or 1 + uint32_t numMainAudio; + uint32_t numSidechain; + uint32_t numCV; + + BusInfo() + : audio(0), + sidechain(0), + numMainAudio(0), + numSidechain(0), + numCV(0) {} } inputBuses, outputBuses; public: @@ -1410,12 +1443,10 @@ public: // set up flags int32_t flags = 0; - const auto desig = fPlugin.getParameterDesignation(index); - const auto hints = fPlugin.getParameterHints(index); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + const uint32_t hints = fPlugin.getParameterHints(index); - switch (desig) + switch (fPlugin.getParameterDesignation(index)) { case kParameterDesignationNull: break; @@ -2155,6 +2186,65 @@ private: * VST3 low-level pointer thingies follow, proceed with care. */ +// -------------------------------------------------------------------------------------------------------------------- +// v3_funknown for static and single instances of classes + +template +static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid iid, void** iface) +{ + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_interface)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; +} + +template +static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid iid, void** iface) +{ + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_interface1) || v3_tuid_match(iid, v3_interface2) || v3_tuid_match(iid, v3_interface3)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; +} + +static V3_API uint32_t dpf_static__ref(void*) { return 1; } +static V3_API uint32_t dpf_static__unref(void*) { return 0; } + +// -------------------------------------------------------------------------------------------------------------------- +// v3_funknown with refcounter + +template +static V3_API uint32_t dpf_refcounter__ref(void* self) +{ + return ++(*(T**)self)->refcounter; +} + +template +static V3_API uint32_t dpf_refcounter__unref(void* self) +{ + return --(*(T**)self)->refcounter; +} + #if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- // dpf_dsp_connection_point @@ -2165,11 +2255,8 @@ enum ConnectionPointType { kConnectionPointBridge }; -struct v3_connection_point_cpp : v3_funknown { - v3_connection_point point; -}; - struct dpf_dsp_connection_point : v3_connection_point_cpp { + std::atomic_int refcounter; ScopedPointer& vst3; const ConnectionPointType type; v3_connection_point** other; @@ -2177,154 +2264,137 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { bool shortcircuit; // plugin as controller, should pass directly to view dpf_dsp_connection_point(const ConnectionPointType t, ScopedPointer& v) - : vst3(v), + : refcounter(1), + vst3(v), type(t), other(nullptr), bridge(nullptr), shortcircuit(false) { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_connection_point_iid - }; + static constexpr const v3_tuid interface = V3_ID_COPY(v3_connection_point_iid); - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_dsp_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } + // v3_funknown, single instance + query_interface = dpf_static__query_interface; + ref = dpf_refcounter__ref; + unref = dpf_refcounter__unref; - return V3_NO_INTERFACE; - }; + // v3_connection_point + point.connect = connect; + point.disconnect = disconnect; + point.notify = notify; + } - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; + // ---------------------------------------------------------------------------------------------------------------- + // v3_connection_point - // ------------------------------------------------------------------------------------------------------------ - // v3_connection_point + static V3_API v3_result connect(void* self, v3_connection_point** other) + { + d_stdout("dpf_dsp_connection_point::connect => %p %p", self, other); + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT(point->bridge == nullptr); - point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result - { - d_stdout("dpf_dsp_connection_point::connect => %p %p", self, other); - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); - DISTRHO_SAFE_ASSERT(point->bridge == nullptr); + point->other = other; - point->other = other; + if (point->type == kConnectionPointComponent) + if (PluginVst3* const vst3 = point->vst3) + vst3->connect((v3_connection_point**)self); - if (point->type == kConnectionPointComponent) - if (PluginVst3* const vst3 = point->vst3) - vst3->connect((v3_connection_point**)self); + return V3_OK; + } - return V3_OK; - }; + static V3_API v3_result disconnect(void* self, v3_connection_point** other) + { + d_stdout("dpf_dsp_connection_point::disconnect => %p %p", self, other); + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); - point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result - { - d_stdout("dpf_dsp_connection_point::disconnect => %p %p", self, other); - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + point->other = nullptr; + point->bridge = nullptr; - point->other = nullptr; - point->bridge = nullptr; + if (point->type == kConnectionPointComponent) + if (PluginVst3* const vst3 = point->vst3) + vst3->disconnect(); - if (point->type == kConnectionPointComponent) - if (PluginVst3* const vst3 = point->vst3) - vst3->disconnect(); + return V3_OK; + } - return V3_OK; - }; + static V3_API v3_result notify(void* self, v3_message** message) + { + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); - point.notify = []V3_API(void* self, v3_message** message) -> v3_result - { - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = point->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = point->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + v3_connection_point** const other = point->other; + DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALIZED); - v3_connection_point** const other = point->other; - DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALISED); + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG); - v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); - DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG); + int64_t target = 0; + const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target); + DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR); - int64_t target = 0; - const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target); - DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res); - DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR); + switch (point->type) + { + // message belongs to component (aka plugin) + case kConnectionPointComponent: + if (target == 1) + { + // view -> edit controller -> component + return vst3->notify(message); + } + else + { + // message is from component to controller to view + return v3_cpp_obj(other)->notify(other, message); + } - switch (point->type) + // message belongs to edit controller + case kConnectionPointController: + if (target == 1) { - // message belongs to component (aka plugin) - case kConnectionPointComponent: - if (target == 1) - { - // view -> edit controller -> component + // we are in view<->dsp short-circuit, all happens in the controller without bridge + if (point->shortcircuit) return vst3->notify(message); - } - else - { - // message is from component to controller to view - return v3_cpp_obj(other)->notify(other, message); - } - // message belongs to edit controller - case kConnectionPointController: - if (target == 1) - { - // we are in view<->dsp short-circuit, all happens in the controller without bridge - if (point->shortcircuit) - return vst3->notify(message); - - // view -> edit controller -> component + // view -> edit controller -> component + return v3_cpp_obj(other)->notify(other, message); + } + else + { + // we are in view<->dsp short-circuit, all happens in the controller without bridge + if (point->shortcircuit) return v3_cpp_obj(other)->notify(other, message); - } - else - { - // we are in view<->dsp short-circuit, all happens in the controller without bridge - if (point->shortcircuit) - return v3_cpp_obj(other)->notify(other, message); - - // message is from component to controller to view - v3_connection_point** const bridge = point->bridge; - DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED); - return v3_cpp_obj(bridge)->notify(bridge, message); - } - // message belongs to bridge (aka ui) - case kConnectionPointBridge: - if (target == 1) - { - // view -> edit controller -> component - v3_connection_point** const bridge = point->bridge; - DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED); - return v3_cpp_obj(bridge)->notify(bridge, message); - } - else - { - // message is from component to controller to view - return v3_cpp_obj(other)->notify(other, message); - } + // message is from component to controller to view + v3_connection_point** const bridge = point->bridge; + DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALIZED); + return v3_cpp_obj(bridge)->notify(bridge, message); + } + + // message belongs to bridge (aka ui) + case kConnectionPointBridge: + if (target == 1) + { + // view -> edit controller -> component + v3_connection_point** const bridge = point->bridge; + DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALIZED); + return v3_cpp_obj(bridge)->notify(bridge, message); + } + else + { + // message is from component to controller to view + return v3_cpp_obj(other)->notify(other, message); } + } - return V3_INTERNAL_ERR; - }; + return V3_INTERNAL_ERR; } }; #endif @@ -2334,60 +2404,39 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { // dpf_midi_mapping struct dpf_midi_mapping : v3_midi_mapping_cpp { - ScopedPointer& vst3; - - dpf_midi_mapping(ScopedPointer& v) - : vst3(v) + dpf_midi_mapping() { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_midi_mapping_iid - }; + static constexpr const v3_tuid interface = V3_ID_COPY(v3_midi_mapping_iid); - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown + // v3_funknown, used statically + query_interface = dpf_static__query_interface; + ref = dpf_static__ref; + unref = dpf_static__unref; - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_midi_mapping::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ // v3_midi_mapping + map.get_midi_controller_assignment = get_midi_controller_assignment; + } - map.get_midi_controller_assignment = []V3_API(void*, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id) -> v3_result - { - DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE); - DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE); - DISTRHO_SAFE_ASSERT_INT_RETURN(cc >= 0 && cc < 130, cc, V3_FALSE); + // ---------------------------------------------------------------------------------------------------------------- + // v3_midi_mapping -#if DISTRHO_PLUGIN_WANT_PROGRAMS - static constexpr const v3_param_id offset = 1; -#else - static const constexpr v3_param_id offset = 0; -#endif + static V3_API v3_result get_midi_controller_assignment(void*, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id) + { + DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE); + DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE); + DISTRHO_SAFE_ASSERT_INT_RETURN(cc >= 0 && cc < 130, cc, V3_FALSE); - *id = offset + channel * 130 + cc; - return V3_OK; - }; +# if DISTRHO_PLUGIN_WANT_PROGRAMS + static constexpr const v3_param_id offset = 1; +# else + static const constexpr v3_param_id offset = 0; +# endif + + *id = offset + channel * 130 + cc; + return V3_OK; } + + DISTRHO_PREVENT_HEAP_ALLOCATION }; #endif @@ -2395,12 +2444,10 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { // dpf_edit_controller struct dpf_edit_controller : v3_edit_controller_cpp { + std::atomic_int refcounter; #if DISTRHO_PLUGIN_HAS_UI ScopedPointer connectionComp; // kConnectionPointController ScopedPointer connectionBridge; // kConnectionPointBridge -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - ScopedPointer midiMapping; #endif ScopedPointer& vst3; bool initialized; @@ -2409,389 +2456,401 @@ struct dpf_edit_controller : v3_edit_controller_cpp { v3_plugin_base::v3_funknown** hostContext; dpf_edit_controller(ScopedPointer& v) - : vst3(v), + : refcounter(1), + vst3(v), initialized(false), handler(nullptr), hostContext(nullptr) { - static const uint8_t* kSupportedInterfacesBase[] = { - v3_funknown_iid, - v3_edit_controller_iid - }; + // v3_funknown, single instance with custom query + query_interface = query_interface_edit_controller; + ref = dpf_refcounter__ref; + unref = dpf_refcounter__unref; + + // v3_plugin_base + base.initialize = initialize; + base.terminate = terminate; + + // v3_edit_controller + ctrl.set_component_state = set_component_state; + ctrl.set_state = set_state; + ctrl.get_state = get_state; + ctrl.get_parameter_count = get_parameter_count; + ctrl.get_parameter_info = get_parameter_info; + ctrl.get_parameter_string_for_value = get_parameter_string_for_value; + ctrl.get_parameter_value_for_string = get_parameter_value_for_string; + ctrl.normalised_parameter_to_plain = normalised_parameter_to_plain; + ctrl.plain_parameter_to_normalised = plain_parameter_to_normalised; + ctrl.get_parameter_normalised = get_parameter_normalised; + ctrl.set_parameter_normalised = set_parameter_normalised; + ctrl.set_component_handler = set_component_handler; + ctrl.create_view = create_view; + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown + static V3_API v3_result query_interface_edit_controller(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + if (v3_tuid_match(iid, v3_plugin_base_iid)) { - d_stdout("dpf_edit_controller::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + *iface = self; + return V3_OK; + } - for (const uint8_t* interface_iid : kSupportedInterfacesBase) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } + if (v3_tuid_match(iid, v3_edit_controller_iid)) + { + *iface = self; + return V3_OK; + } - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NO_INTERFACE); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NO_INTERFACE); #if DISTRHO_PLUGIN_HAS_UI - if (v3_tuid_match(v3_connection_point_iid, iid)) - { - if (controller->connectionComp == nullptr) - controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, - controller->vst3); - *iface = &controller->connectionComp; - return V3_OK; - } + if (v3_tuid_match(iid, v3_connection_point_iid)) + { + if (controller->connectionComp == nullptr) + controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, + controller->vst3); + else + ++controller->connectionComp->refcounter; + *iface = &controller->connectionComp; + return V3_OK; + } #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (v3_tuid_match(v3_midi_mapping_iid, iid)) - { - if (controller->midiMapping == nullptr) - controller->midiMapping = new dpf_midi_mapping(controller->vst3); - *iface = &controller->midiMapping; - return V3_OK; - } + if (v3_tuid_match(iid, v3_midi_mapping_iid)) + { + static dpf_midi_mapping midi_mapping; + static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; + *iface = &midi_mapping_ptr; + return V3_OK; + } #endif - return V3_NO_INTERFACE; - }; + *iface = NULL; + return V3_NO_INTERFACE; + } - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_base - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_base + static V3_API v3_result initialize(void* self, v3_plugin_base::v3_funknown** context) + { + d_stdout("dpf_edit_controller::initialize => %p %p", self, context); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - base.initialize = []V3_API(void* self, v3_plugin_base::v3_funknown** context) -> v3_result - { - d_stdout("dpf_edit_controller::initialize => %p %p", self, context); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + const bool initialized = controller->initialized; + DISTRHO_SAFE_ASSERT_RETURN(! initialized, V3_INVALID_ARG); - const bool initialized = controller->initialized; - DISTRHO_SAFE_ASSERT_RETURN(! initialized, V3_INVALID_ARG); + controller->initialized = true; + controller->hostContext = context; + return V3_OK; + } - controller->initialized = true; - controller->hostContext = context; - return V3_OK; - }; + static V3_API v3_result terminate(void* self) + { + d_stdout("dpf_edit_controller::terminate => %p", self); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - base.terminate = []V3_API(void* self) -> v3_result - { - d_stdout("dpf_edit_controller::terminate => %p", self); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + const bool initialized = controller->initialized; + DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); - const bool initialized = controller->initialized; - DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); + controller->initialized = false; + controller->hostContext = nullptr; - controller->initialized = false; - controller->hostContext = nullptr; - return V3_OK; - }; +#if DISTRHO_PLUGIN_HAS_UI + // take the chance to do some cleanup if possible (we created the bridge, we need to destroy it) + if (controller->connectionBridge != nullptr) + if (controller->connectionBridge->refcounter == 1) + controller->connectionBridge = nullptr; - // ------------------------------------------------------------------------------------------------------------ - // v3_edit_controller + if (controller->connectionComp != nullptr && controller->connectionComp->shortcircuit) + if (controller->connectionComp->refcounter == 1) + controller->connectionComp = nullptr; +#endif - ctrl.set_component_state = []V3_API(void* self, v3_bstream* stream) -> v3_result - { - d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + return V3_OK; + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_edit_controller + + static V3_API v3_result set_component_state(void* self, v3_bstream* stream) + { + d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); #if 0 - return vst3->setComponentState(stream); + return vst3->setComponentState(stream); #endif - // TODO, returning ok to make renoise happy - return V3_OK; - }; + // TODO, returning ok to make renoise happy + return V3_OK; + } - ctrl.set_state = []V3_API(void* self, v3_bstream* stream) -> v3_result - { - d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_state(void* self, v3_bstream* stream) + { + d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); #if 0 - return vst3->setState(stream); + return vst3->setState(stream); #endif - return V3_NOT_IMPLEMENTED; - }; + return V3_NOT_IMPLEMENTED; + } - ctrl.get_state = []V3_API(void* self, v3_bstream* stream) -> v3_result - { - d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_state(void* self, v3_bstream* stream) + { + d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); #if 0 - return vst3->getState(stream); + return vst3->getState(stream); #endif - return V3_NOT_IMPLEMENTED; - }; + return V3_NOT_IMPLEMENTED; + } - ctrl.get_parameter_count = []V3_API(void* self) -> int32_t - { - // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API int32_t get_parameter_count(void* self) + { + // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getParameterCount(); - }; + return vst3->getParameterCount(); + } - ctrl.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result - { - // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_parameter_info(void* self, int32_t param_idx, v3_param_info* param_info) + { + // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getParameterInfo(param_idx, param_info); - }; + return vst3->getParameterInfo(param_idx, param_info); + } - ctrl.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result - { - // NOTE very noisy, called many times - // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_parameter_string_for_value(void* self, v3_param_id index, double normalised, v3_str_128 output) + { + // NOTE very noisy, called many times + // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getParameterStringForValue(index, normalised, output); - }; + return vst3->getParameterStringForValue(index, normalised, output); + } - ctrl.get_parameter_value_for_string = []V3_API(void* self, v3_param_id index, int16_t* input, double* output) -> v3_result - { - d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output) + { + d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getParameterValueForString(index, input, output); - }; + return vst3->getParameterValueForString(index, input, output); + } - ctrl.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id index, double normalised) -> double - { - d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API double normalised_parameter_to_plain(void* self, v3_param_id index, double normalised) + { + d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->normalisedParameterToPlain(index, normalised); - }; + return vst3->normalisedParameterToPlain(index, normalised); + } - ctrl.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id index, double plain) -> double - { - d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API double plain_parameter_to_normalised(void* self, v3_param_id index, double plain) + { + d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->plainParameterToNormalised(index, plain); - }; + return vst3->plainParameterToNormalised(index, plain); + } - ctrl.get_parameter_normalised = []V3_API(void* self, v3_param_id index) -> double - { - // NOTE very noisy, called many times - // d_stdout("dpf_edit_controller::get_parameter_normalised => %p %u", self, index); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); + static V3_API double get_parameter_normalised(void* self, v3_param_id index) + { + // NOTE very noisy, called many times + // d_stdout("dpf_edit_controller::get_parameter_normalised => %p %u", self, index); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0); - return vst3->getParameterNormalized(index); - }; + return vst3->getParameterNormalized(index); + } - ctrl.set_parameter_normalised = []V3_API(void* self, v3_param_id index, double normalised) -> v3_result - { - d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_parameter_normalised(void* self, v3_param_id index, double normalised) + { + d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->setParameterNormalized(index, normalised); - }; + return vst3->setParameterNormalized(index, normalised); + } - ctrl.set_component_handler = []V3_API(void* self, v3_component_handler** handler) -> v3_result - { - d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_component_handler(void* self, v3_component_handler** handler) + { + d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - controller->handler = handler; + controller->handler = handler; - if (PluginVst3* const vst3 = controller->vst3) - return vst3->setComponentHandler(handler); + if (PluginVst3* const vst3 = controller->vst3) + return vst3->setComponentHandler(handler); - return V3_NOT_INITIALISED; - }; + return V3_NOT_INITIALIZED; + } - ctrl.create_view = []V3_API(void* self, const char* name) -> v3_plugin_view** - { - d_stdout("dpf_edit_controller::create_view => %p %s", self, name); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); + static V3_API v3_plugin_view** create_view(void* self, const char* name) + { + d_stdout("dpf_edit_controller::create_view => %p %s", self, name); + dpf_edit_controller* const controller = *(dpf_edit_controller**)self; + DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); #if DISTRHO_PLUGIN_HAS_UI - // plugin must be initialized - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); + // plugin must be initialized + PluginVst3* const vst3 = controller->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); - // we require host context for message creation - DISTRHO_SAFE_ASSERT_RETURN(controller->hostContext != nullptr, nullptr); + // we require host context for message creation + DISTRHO_SAFE_ASSERT_RETURN(controller->hostContext != nullptr, nullptr); - v3_host_application** host = nullptr; - v3_cpp_obj_query_interface(controller->hostContext, v3_host_application_iid, &host); - DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); + v3_host_application** host = nullptr; + v3_cpp_obj_query_interface(controller->hostContext, v3_host_application_iid, &host); + DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); - // if there is a component connection, we require it to be active - if (controller->connectionComp != nullptr) - { - DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp->other != nullptr, nullptr); - } - // otherwise short-circuit and deal with this ourselves (assume local usage) - else - { - controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, - controller->vst3); - controller->connectionComp->shortcircuit = true; - } + // if there is a component connection, we require it to be active + if (controller->connectionComp != nullptr) + { + DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp->other != nullptr, nullptr); + } + // otherwise short-circuit and deal with this ourselves (assume local usage) + else + { + controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, + controller->vst3); + controller->connectionComp->shortcircuit = true; + } - v3_plugin_view** const view = dpf_plugin_view_create(host, - vst3->getInstancePointer(), - vst3->getSampleRate()); - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr); + v3_plugin_view** const view = dpf_plugin_view_create(host, + vst3->getInstancePointer(), + vst3->getSampleRate()); + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr); - v3_connection_point** uiconn = nullptr; - if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &uiconn) == V3_OK) - { - d_stdout("view connection query ok %p | shortcircuit %d", - uiconn, controller->connectionComp->shortcircuit); + v3_connection_point** uiconn = nullptr; + if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &uiconn) == V3_OK) + { + d_stdout("view connection query ok %p | shortcircuit %d", + uiconn, controller->connectionComp->shortcircuit); - v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionComp; + v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionComp; - if (controller->connectionComp->shortcircuit) - { - vst3->disconnect(); + if (controller->connectionComp->shortcircuit) + { + vst3->disconnect(); - v3_cpp_obj(uiconn)->connect(uiconn, ctrlconn); - v3_cpp_obj(ctrlconn)->connect(ctrlconn, uiconn); + v3_cpp_obj(uiconn)->connect(uiconn, ctrlconn); + v3_cpp_obj(ctrlconn)->connect(ctrlconn, uiconn); - vst3->connect(ctrlconn); - } - else - { - controller->connectionBridge = new dpf_dsp_connection_point(kConnectionPointBridge, - controller->vst3); + vst3->connect(ctrlconn); + } + else + { + controller->connectionBridge = new dpf_dsp_connection_point(kConnectionPointBridge, + controller->vst3); - v3_connection_point** const bridgeconn = (v3_connection_point**)&controller->connectionBridge; - v3_cpp_obj(uiconn)->connect(uiconn, bridgeconn); - v3_cpp_obj(bridgeconn)->connect(bridgeconn, uiconn); + v3_connection_point** const bridgeconn = (v3_connection_point**)&controller->connectionBridge; + v3_cpp_obj(uiconn)->connect(uiconn, bridgeconn); + v3_cpp_obj(bridgeconn)->connect(bridgeconn, uiconn); - controller->connectionComp->bridge = bridgeconn; - controller->connectionBridge->bridge = ctrlconn; - } + controller->connectionComp->bridge = bridgeconn; + controller->connectionBridge->bridge = ctrlconn; } + } - return view; + return view; #else - return nullptr; + return nullptr; #endif - }; } }; // -------------------------------------------------------------------------------------------------------------------- // dpf_process_context_requirements -struct v3_process_context_requirements_cpp : v3_funknown { - v3_process_context_requirements req; -}; - struct dpf_process_context_requirements : v3_process_context_requirements_cpp { dpf_process_context_requirements() { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_process_context_requirements_iid - }; - - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_process_context_requirements::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } + static constexpr const v3_tuid interface = V3_ID_COPY(v3_process_context_requirements_iid); - return V3_NO_INTERFACE; - }; + // v3_funknown, used statically + query_interface = dpf_static__query_interface; + ref = dpf_static__ref; + unref = dpf_static__unref; - // this is used statically, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ // v3_process_context_requirements + req.get_process_context_requirements = get_process_context_requirements; + } - req.get_process_context_requirements = []V3_API(void*) -> uint32_t - { + // ---------------------------------------------------------------------------------------------------------------- + // v3_process_context_requirements + + static uint32_t get_process_context_requirements(void*) + { #if DISTRHO_PLUGIN_WANT_TIMEPOS - return 0x0 - |V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID - |V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID - |V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID - |V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID - |V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING + return 0x0 + |V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID + |V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID + |V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID + |V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID + |V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING #else - return 0x0; + return 0x0; #endif - }; } DISTRHO_PREVENT_HEAP_ALLOCATION @@ -2800,161 +2859,171 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_audio_processor -struct v3_audio_processor_cpp : v3_funknown { - v3_audio_processor processor; -}; - struct dpf_audio_processor : v3_audio_processor_cpp { + std::atomic_int refcounter; ScopedPointer& vst3; dpf_audio_processor(ScopedPointer& v) - : vst3(v) + : refcounter(1), + vst3(v) { - static const uint8_t* kSupportedInterfacesBase[] = { - v3_funknown_iid, - v3_audio_processor_iid - }; + // v3_funknown, single instance with custom query + query_interface = query_interface_with_proc_ctx_reqs; + ref = dpf_refcounter__ref; + unref = dpf_refcounter__unref; - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown + // v3_audio_processor + proc.set_bus_arrangements = set_bus_arrangements; + proc.get_bus_arrangement = get_bus_arrangement; + proc.can_process_sample_size = can_process_sample_size; + proc.get_latency_samples = get_latency_samples; + proc.setup_processing = setup_processing; + proc.set_processing = set_processing; + proc.process = process; + proc.get_tail_samples = get_tail_samples; + } - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_audio_processor::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown - for (const uint8_t* interface_iid : kSupportedInterfacesBase) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } + static V3_API v3_result query_interface_with_proc_ctx_reqs(void* self, const v3_tuid iid, void** iface) + { + static constexpr const v3_tuid interface = V3_ID_COPY(v3_audio_processor_iid); - if (v3_tuid_match(v3_process_context_requirements_iid, iid)) - { - static dpf_process_context_requirements context_req; - static dpf_process_context_requirements* context_req_ptr = &context_req;; - *iface = &context_req_ptr; - return V3_OK; - } + if (dpf_static__query_interface(self, iid, iface) == V3_OK) + return V3_OK; - return V3_NO_INTERFACE; - }; + if (v3_tuid_match(iid, v3_process_context_requirements_iid)) + { + static dpf_process_context_requirements context_req; + static dpf_process_context_requirements* context_req_ptr = &context_req; + *iface = &context_req_ptr; + return V3_OK; + } - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; + *iface = NULL; + return V3_NO_INTERFACE; + } - // ------------------------------------------------------------------------------------------------------------ - // v3_audio_processor + // ---------------------------------------------------------------------------------------------------------------- + // v3_audio_processor - processor.set_bus_arrangements = []V3_API(void* self, - v3_speaker_arrangement* inputs, int32_t num_inputs, - v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result - { - // NOTE this is called a bunch of times - // d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", self, inputs, num_inputs, outputs, num_outputs); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_bus_arrangements(void* self, + v3_speaker_arrangement* inputs, int32_t num_inputs, + v3_speaker_arrangement* outputs, int32_t num_outputs) + { + // NOTE this is called a bunch of times + // d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", self, inputs, num_inputs, outputs, num_outputs); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = processor->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return processor->vst3->setBusArrangements(inputs, num_inputs, outputs, num_outputs); - }; + return processor->vst3->setBusArrangements(inputs, num_inputs, outputs, num_outputs); + } - processor.get_bus_arrangement = []V3_API(void* self, int32_t bus_direction, - int32_t idx, v3_speaker_arrangement* arr) -> v3_result - { - d_stdout("dpf_audio_processor::get_bus_arrangement => %p %i %i %p", self, bus_direction, idx, arr); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_bus_arrangement(void* self, int32_t bus_direction, + int32_t idx, v3_speaker_arrangement* arr) + { + d_stdout("dpf_audio_processor::get_bus_arrangement => %p %i %i %p", self, bus_direction, idx, arr); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = processor->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return processor->vst3->getBusArrangement(bus_direction, idx, arr); - }; + return processor->vst3->getBusArrangement(bus_direction, idx, arr); + } - processor.can_process_sample_size = []V3_API(void* self, int32_t symbolic_sample_size) -> v3_result - { - d_stdout("dpf_audio_processor::can_process_sample_size => %p %i", self, symbolic_sample_size); - return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED; - }; + static V3_API v3_result can_process_sample_size(void* self, int32_t symbolic_sample_size) + { + d_stdout("dpf_audio_processor::can_process_sample_size => %p %i", self, symbolic_sample_size); + return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED; + } - processor.get_latency_samples = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_audio_processor::get_latency_samples => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + static V3_API uint32_t get_latency_samples(void* self) + { + d_stdout("dpf_audio_processor::get_latency_samples => %p", self); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); - PluginVst3* const vst3 = processor->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); - return processor->vst3->getLatencySamples(); - }; + return processor->vst3->getLatencySamples(); + } - processor.setup_processing = []V3_API(void* self, v3_process_setup* setup) -> v3_result - { - d_stdout("dpf_audio_processor::setup_processing => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result setup_processing(void* self, v3_process_setup* setup) + { + d_stdout("dpf_audio_processor::setup_processing => %p", self); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = processor->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - d_lastBufferSize = setup->max_block_size; - d_lastSampleRate = setup->sample_rate; - return processor->vst3->setupProcessing(setup); - }; + d_lastBufferSize = setup->max_block_size; + d_lastSampleRate = setup->sample_rate; + return processor->vst3->setupProcessing(setup); + } - processor.set_processing = []V3_API(void* self, v3_bool state) -> v3_result - { - d_stdout("dpf_audio_processor::set_processing => %p %u", self, state); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_processing(void* self, v3_bool state) + { + d_stdout("dpf_audio_processor::set_processing => %p %u", self, state); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = processor->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return processor->vst3->setProcessing(state); - }; + return processor->vst3->setProcessing(state); + } - processor.process = []V3_API(void* self, v3_process_data* data) -> v3_result - { - // NOTE runs during RT - // d_stdout("dpf_audio_processor::process => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result process(void* self, v3_process_data* data) + { + // NOTE runs during RT + // d_stdout("dpf_audio_processor::process => %p", self); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = processor->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return processor->vst3->process(data); - }; + return processor->vst3->process(data); + } - processor.get_tail_samples = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_audio_processor::get_tail_samples => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + static V3_API uint32_t get_tail_samples(void* self) + { + d_stdout("dpf_audio_processor::get_tail_samples => %p", self); + dpf_audio_processor* const processor = *(dpf_audio_processor**)self; + DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); - PluginVst3* const vst3 = processor->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); + PluginVst3* const vst3 = processor->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); - return processor->vst3->getTailSamples(); - }; + return processor->vst3->getTailSamples(); } }; +// -------------------------------------------------------------------------------------------------------------------- +// Store components that we can't delete properly, to be cleaned up on module unload + +struct dpf_component; + +std::vector*> gComponentGarbage; + +static v3_result handleUncleanComponent(ScopedPointer* const componentptr) +{ + gComponentGarbage.push_back(componentptr); + return V3_INVALID_ARG; +} + // -------------------------------------------------------------------------------------------------------------------- // dpf_component struct dpf_component : v3_component_cpp { - std::atomic refcounter; + std::atomic_int refcounter; ScopedPointer* self; ScopedPointer processor; #if DISTRHO_PLUGIN_HAS_UI @@ -2967,418 +3036,498 @@ struct dpf_component : v3_component_cpp { : refcounter(1), self(s) { - static const uint8_t* kSupportedInterfacesBase[] = { - v3_funknown_iid, - v3_plugin_base_iid, - v3_component_iid - }; + // v3_funknown, everything custom + query_interface = query_interface_component; + ref = ref_component; + unref = unref_component; + + // v3_plugin_base + base.initialize = initialize; + base.terminate = terminate; + + // v3_component + comp.get_controller_class_id = get_controller_class_id; + comp.set_io_mode = set_io_mode; + comp.get_bus_count = get_bus_count; + comp.get_bus_info = get_bus_info; + comp.get_routing_info = get_routing_info; + comp.activate_bus = activate_bus; + comp.set_active = set_active; + comp.set_state = set_state; + comp.get_state = get_state; + } - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + static V3_API v3_result query_interface_component(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) { - d_stdout("dpf_component::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; + *iface = self; + return V3_OK; + } - for (const uint8_t* interface_iid : kSupportedInterfacesBase) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } + if (v3_tuid_match(iid, v3_plugin_base_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_component_iid)) + { + *iface = self; + return V3_OK; + } - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NO_INTERFACE); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NO_INTERFACE); - if (v3_tuid_match(v3_audio_processor_iid, iid)) - { - if (component->processor == nullptr) - component->processor = new dpf_audio_processor(component->vst3); - *iface = &component->processor; - return V3_OK; - } + if (v3_tuid_match(iid, v3_audio_processor_iid)) + { + if (component->processor == nullptr) + component->processor = new dpf_audio_processor(component->vst3); + else + ++component->processor->refcounter; + *iface = &component->processor; + return V3_OK; + } #if DISTRHO_PLUGIN_HAS_UI - if (v3_tuid_match(v3_connection_point_iid, iid)) - { - if (component->connection == nullptr) - component->connection = new dpf_dsp_connection_point(kConnectionPointComponent, - component->vst3); - *iface = &component->connection; - return V3_OK; - } + if (v3_tuid_match(iid, v3_connection_point_iid)) + { + if (component->connection == nullptr) + component->connection = new dpf_dsp_connection_point(kConnectionPointComponent, + component->vst3); + else + ++component->connection->refcounter; + *iface = &component->connection; + return V3_OK; + } #endif - if (v3_tuid_match(v3_edit_controller_iid, iid)) - { - if (component->controller == nullptr) - component->controller = new dpf_edit_controller(component->vst3); - *iface = &component->controller; - return V3_OK; - } + if (v3_tuid_match(iid, v3_edit_controller_iid)) + { + if (component->controller == nullptr) + component->controller = new dpf_edit_controller(component->vst3); + else + ++component->controller->refcounter; + *iface = &component->controller; + return V3_OK; + } - return V3_NO_INTERFACE; - }; + *iface = NULL; + return V3_NO_INTERFACE; + } -#if 1 - // TODO fix this up later - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; -#else - ref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_component::ref => %p", self); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, 0); + static V3_API uint32_t ref_component(void* self) + { + return ++(*(dpf_component**)self)->refcounter; + } - return ++component->refcounter; - }; + static V3_API uint32_t unref_component(void* self) + { + ScopedPointer* const componentptr = (ScopedPointer*)self; + dpf_component* const component = *componentptr; - unref = []V3_API(void* self) -> uint32_t + if (const int refcount = --component->refcounter) { - d_stdout("dpf_component::unref => %p", self); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, 0); + d_stdout("dpf_component::unref => %p | refcount %i", self, refcount); + return refcount; + } - if (const int refcount = --component->refcounter) + /** + * Some hosts will have unclean instances of a few of the component child classes at this point. + * We check for those here, going through the whole possible chain to see if it is safe to delete. + * If not, we add this component to the `gComponentGarbage` global which will take care of it during unload. + */ + + bool unclean = false; + if (dpf_audio_processor* const proc = component->processor) + { + if (const int refcount = proc->refcounter) { - d_stdout("dpf_component::unref => %p | refcount %i", self, refcount); - return refcount; + unclean = true; + d_stderr("DPF warning: asked to delete component while audio processor still active (refcount %d)", refcount); } + } - d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); - *component->self = nullptr; - delete (dpf_component**)self; - return 0; - }; +#if DISTRHO_PLUGIN_HAS_UI + if (dpf_dsp_connection_point* const conn = component->connection) + { + if (const int refcount = conn->refcounter) + { + unclean = true; + d_stderr("DPF warning: asked to delete component while connection point still active (refcount %d)", refcount); + } + } #endif - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_base - - base.initialize = []V3_API(void* self, v3_plugin_base::v3_funknown** context) -> v3_result + if (dpf_edit_controller* const ctrl = component->controller) { - d_stdout("dpf_component::initialize => %p %p", self, context); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + if (const int refcount = ctrl->refcounter) + { + unclean = true; + d_stderr("DPF warning: asked to delete component while edit controller still active (refcount %d)", refcount); + } - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 == nullptr, V3_INVALID_ARG); +#if DISTRHO_PLUGIN_HAS_UI + if (dpf_dsp_connection_point* const comp = ctrl->connectionComp) + { + if (const int refcount = comp->refcounter) + { + unclean = true; + d_stderr("DPF warning: asked to delete component while edit controller connection point still active (refcount %d)", refcount); + } + } - d_lastCanRequestParameterValueChanges = true; + if (dpf_dsp_connection_point* const bridge = ctrl->connectionBridge) + { + if (const int refcount = bridge->refcounter) + { + unclean = true; + d_stderr("DPF warning: asked to delete component while view bridge connection still active (refcount %d)", refcount); + } + } +#endif + } - // default early values - if (d_lastBufferSize == 0) - d_lastBufferSize = 2048; - if (d_lastSampleRate <= 0.0) - d_lastSampleRate = 44100.0; + if (unclean) + return handleUncleanComponent(componentptr); - // query for host context - v3_host_application** host = nullptr; - v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); + *(component->self) = nullptr; + delete componentptr; + return 0; + } - component->vst3 = new PluginVst3(host); - return V3_OK; - }; + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_base - base.terminate = []V3_API(void* self) -> v3_result - { - d_stdout("dpf_component::terminate => %p", self); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result initialize(void* self, v3_plugin_base::v3_funknown** context) + { + d_stdout("dpf_component::initialize => %p %p", self, context); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_INVALID_ARG); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 == nullptr, V3_INVALID_ARG); - component->vst3 = nullptr; - return V3_OK; - }; + d_lastCanRequestParameterValueChanges = true; - // ------------------------------------------------------------------------------------------------------------ - // v3_component + // default early values + if (d_lastBufferSize == 0) + d_lastBufferSize = 2048; + if (d_lastSampleRate <= 0.0) + d_lastSampleRate = 44100.0; - comp.get_controller_class_id = []V3_API(void* self, v3_tuid class_id) -> v3_result - { - d_stdout("dpf_component::get_controller_class_id => %p %s", self, tuid2str(class_id)); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + // query for host context + v3_host_application** host = nullptr; + v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + component->vst3 = new PluginVst3(host); + return V3_OK; + } - // TODO - return V3_NOT_IMPLEMENTED; - }; + static V3_API v3_result terminate(void* self) + { + d_stdout("dpf_component::terminate => %p", self); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - comp.set_io_mode = []V3_API(void* self, int32_t io_mode) -> v3_result - { - d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_INVALID_ARG); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + component->vst3 = nullptr; + return V3_OK; + } - // TODO - return V3_NOT_IMPLEMENTED; - }; + // ---------------------------------------------------------------------------------------------------------------- + // v3_component - comp.get_bus_count = []V3_API(void* self, int32_t media_type, int32_t bus_direction) -> int32_t - { - // NOTE runs during RT - // d_stdout("dpf_component::get_bus_count => %p %i %i", self, media_type, bus_direction); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_controller_class_id(void* self, v3_tuid class_id) + { + d_stdout("dpf_component::get_controller_class_id => %p %s", self, tuid2str(class_id)); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getBusCount(media_type, bus_direction); - }; + // TODO + return V3_NOT_IMPLEMENTED; + } - comp.get_bus_info = []V3_API(void* self, int32_t media_type, int32_t bus_direction, - int32_t bus_idx, v3_bus_info* info) -> v3_result - { - d_stdout("dpf_component::get_bus_info => %p %i %i %i %p", self, media_type, bus_direction, bus_idx, info); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_io_mode(void* self, int32_t io_mode) + { + d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getBusInfo(media_type, bus_direction, bus_idx, info); - }; + // TODO + return V3_NOT_IMPLEMENTED; + } - comp.get_routing_info = []V3_API(void* self, v3_routing_info* input, v3_routing_info* output) -> v3_result - { - d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API int32_t get_bus_count(void* self, int32_t media_type, int32_t bus_direction) + { + // NOTE runs during RT + // d_stdout("dpf_component::get_bus_count => %p %i %i", self, media_type, bus_direction); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getRoutingInfo(input, output); - }; + return vst3->getBusCount(media_type, bus_direction); + } - comp.activate_bus = []V3_API(void* self, int32_t media_type, int32_t bus_direction, - int32_t bus_idx, v3_bool state) -> v3_result - { - // NOTE this is called a bunch of times - // d_stdout("dpf_component::activate_bus => %p %i %i %i %u", self, media_type, bus_direction, bus_idx, state); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_bus_info(void* self, int32_t media_type, int32_t bus_direction, + int32_t bus_idx, v3_bus_info* info) + { + d_stdout("dpf_component::get_bus_info => %p %i %i %i %p", self, media_type, bus_direction, bus_idx, info); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->activateBus(media_type, bus_direction, bus_idx, state); - }; + return vst3->getBusInfo(media_type, bus_direction, bus_idx, info); + } - comp.set_active = []V3_API(void* self, v3_bool state) -> v3_result - { - d_stdout("dpf_component::set_active => %p %u", self, state); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result get_routing_info(void* self, v3_routing_info* input, v3_routing_info* output) + { + d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return component->vst3->setActive(state); - }; + return vst3->getRoutingInfo(input, output); + } - comp.set_state = []V3_API(void* self, v3_bstream** stream) -> v3_result - { - d_stdout("dpf_component::set_state => %p", self); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result activate_bus(void* self, int32_t media_type, int32_t bus_direction, + int32_t bus_idx, v3_bool state) + { + // NOTE this is called a bunch of times + // d_stdout("dpf_component::activate_bus => %p %i %i %i %u", self, media_type, bus_direction, bus_idx, state); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->setState(stream); - }; + return vst3->activateBus(media_type, bus_direction, bus_idx, state); + } - comp.get_state = []V3_API(void* self, v3_bstream** stream) -> v3_result - { - d_stdout("dpf_component::get_state => %p %p", self, stream); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result set_active(void* self, v3_bool state) + { + d_stdout("dpf_component::set_active => %p %u", self, state); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getState(stream); - }; + return component->vst3->setActive(state); } -}; -// -------------------------------------------------------------------------------------------------------------------- -// dpf_factory + static V3_API v3_result set_state(void* self, v3_bstream** stream) + { + d_stdout("dpf_component::set_state => %p", self); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); -struct v3_plugin_factory_cpp : v3_funknown { - v3_plugin_factory v1; - v3_plugin_factory_2 v2; - v3_plugin_factory_3 v3; -}; + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); -struct dpf_factory : v3_plugin_factory_cpp { - std::vector*> components; + return vst3->setState(stream); + } - dpf_factory() + static V3_API v3_result get_state(void* self, v3_bstream** stream) { - static const uint8_t* kSupportedFactories[] = { - v3_funknown_iid, - v3_plugin_factory_iid, - v3_plugin_factory_2_iid, - v3_plugin_factory_3_iid - }; + d_stdout("dpf_component::get_state => %p %p", self, stream); + dpf_component* const component = *(dpf_component**)self; + DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - // ------------------------------------------------------------------------------------------------------------ - // Dummy plugin to get data from + PluginVst3* const vst3 = component->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - d_lastBufferSize = 512; - d_lastSampleRate = 44100.0; - d_lastCanRequestParameterValueChanges = true; - static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr); - d_lastBufferSize = 0; - d_lastSampleRate = 0.0; - d_lastCanRequestParameterValueChanges = false; + return vst3->getState(stream); + } +}; - dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] - = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo.getUniqueId(); +// -------------------------------------------------------------------------------------------------------------------- +// Dummy plugin to get data from - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown +static const PluginExporter& _getPluginInfo() +{ + d_lastBufferSize = 512; + d_lastSampleRate = 44100.0; + d_lastCanRequestParameterValueChanges = true; + static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr); + d_lastBufferSize = 0; + d_lastSampleRate = 0.0; + d_lastCanRequestParameterValueChanges = false; + + dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] + = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo.getUniqueId(); + + return gPluginInfo; +} - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_factory::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); +static const PluginExporter& getPluginInfo() +{ + static const PluginExporter& info(_getPluginInfo()); + return info; +} - for (const uint8_t* factory_iid : kSupportedFactories) - { - if (v3_tuid_match(factory_iid, iid)) - { - *iface = self; - return V3_OK; - } - } +// -------------------------------------------------------------------------------------------------------------------- +// dpf_factory - return V3_NO_INTERFACE; - }; +struct dpf_factory : v3_plugin_factory_cpp { + dpf_factory() + { + static constexpr const v3_tuid interface_v1 = V3_ID_COPY(v3_plugin_factory_iid); + static constexpr const v3_tuid interface_v2 = V3_ID_COPY(v3_plugin_factory_2_iid); + static constexpr const v3_tuid interface_v3 = V3_ID_COPY(v3_plugin_factory_3_iid); - // we only support 1 plugin per binary, so don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; + // v3_funknown, used statically + query_interface = dpf_static__query_interface; + ref = dpf_static__ref; + unref = dpf_static__unref; - // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory + v1.get_factory_info = get_factory_info; + v1.num_classes = num_classes; + v1.get_class_info = get_class_info; + v1.create_instance = create_instance; - v1.get_factory_info = []V3_API(void*, v3_factory_info* const info) -> v3_result - { - std::memset(info, 0, sizeof(*info)); - DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); - DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo.getHomePage(), sizeof(info->url)); - // DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO - return V3_OK; - }; + // v3_plugin_factory_2 + v2.get_class_info_2 = get_class_info_2; - v1.num_classes = []V3_API(void*) -> int32_t - { - return 1; - }; + // v3_plugin_factory_3 + v3.get_class_info_utf16 = get_class_info_utf16; + v3.set_host_context = set_host_context; + } - v1.get_class_info = []V3_API(void*, int32_t idx, v3_class_info* const info) -> v3_result - { - std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + ~dpf_factory() + { + if (gComponentGarbage.size() == 0) + return; - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); - info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); - DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo.getName(), sizeof(info->name)); - return V3_OK; - }; + d_stdout("DPF notice: cleaning up previously undeleted components now"); - v1.create_instance = []V3_API(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) -> v3_result + for (std::vector*>::iterator it = gComponentGarbage.begin(); + it != gComponentGarbage.end(); ++it) { - d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); - DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && - v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); + ScopedPointer* const componentptr = *it; + dpf_component* const component = *componentptr; +#if DISTRHO_PLUGIN_HAS_UI + component->connection = nullptr; +#endif + component->processor = nullptr; + component->controller = nullptr; + *(component->self) = nullptr; + delete componentptr; + } - dpf_factory* const factory = *(dpf_factory**)self; - DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALISED); + gComponentGarbage.clear(); + } - ScopedPointer* const componentptr = new ScopedPointer; - *componentptr = new dpf_component(componentptr); - *instance = componentptr; - factory->components.push_back(componentptr); - return V3_OK; - }; + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_factory - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_factory_2 + static V3_API v3_result get_factory_info(void*, v3_factory_info* const info) + { + std::memset(info, 0, sizeof(*info)); + DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), sizeof(info->vendor)); + DISTRHO_NAMESPACE::strncpy(info->url, getPluginInfo().getHomePage(), sizeof(info->url)); + // DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO + return V3_OK; + } - v2.get_class_info_2 = []V3_API(void*, int32_t idx, v3_class_info_2* info) -> v3_result - { - std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + static V3_API int32_t num_classes(void*) + { + return 1; + } - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); - info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); - DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo.getName(), ARRAY_SIZE(info->name)); + static V3_API v3_result get_class_info(void*, int32_t idx, v3_class_info* const info) + { + std::memset(info, 0, sizeof(*info)); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); - info->class_flags = 0; - // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO - DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo.getMaker(), ARRAY_SIZE(info->vendor)); - DISTRHO_NAMESPACE::snprintf_u32(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); // FIXME - DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? - return V3_OK; - }; + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + info->cardinality = 0x7FFFFFFF; + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); + DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), sizeof(info->name)); + return V3_OK; + } - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_factory_3 + static V3_API v3_result create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) + { + d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); + DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && + v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); - v3.get_class_info_utf16 = []V3_API(void*, int32_t idx, v3_class_info_3* info) -> v3_result - { - std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + dpf_factory* const factory = *(dpf_factory**)self; + DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALIZED); - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); - info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); - DISTRHO_NAMESPACE::strncpy_utf16(info->name, gPluginInfo.getName(), ARRAY_SIZE(info->name)); + ScopedPointer* const componentptr = new ScopedPointer; + *componentptr = new dpf_component(componentptr); + *instance = componentptr; + return V3_OK; + } - info->class_flags = 0; - // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", ARRAY_SIZE(info->sub_categories)); // TODO - DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, gPluginInfo.getMaker(), sizeof(info->vendor)); - DISTRHO_NAMESPACE::snprintf_u32_utf16(info->version, gPluginInfo.getVersion(), ARRAY_SIZE(info->version)); // FIXME - DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? - return V3_OK; - }; + // ---------------------------------------------------------------------------------------------------------------- + // v3_plugin_factory_2 - v3.set_host_context = []V3_API (void* self, v3_funknown* host) -> v3_result - { - d_stdout("dpf_factory::set_host_context => %p %p", self, host); - return V3_NOT_IMPLEMENTED; - }; + static V3_API v3_result get_class_info_2(void*, int32_t idx, v3_class_info_2* info) + { + std::memset(info, 0, sizeof(*info)); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + info->cardinality = 0x7FFFFFFF; + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); + + info->class_flags = 0; + // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO + DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); + DISTRHO_NAMESPACE::snprintf_u32(info->version, getPluginInfo().getVersion(), ARRAY_SIZE(info->version)); // FIXME + DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? + return V3_OK; } - ~dpf_factory() + // ------------------------------------------------------------------------------------------------------------ + // v3_plugin_factory_3 + + static V3_API v3_result get_class_info_utf16(void*, int32_t idx, v3_class_info_3* info) { - d_stdout("dpf_factory deleting"); + std::memset(info, 0, sizeof(*info)); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + info->cardinality = 0x7FFFFFFF; + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); + + info->class_flags = 0; + // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", ARRAY_SIZE(info->sub_categories)); // TODO + DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), sizeof(info->vendor)); + DISTRHO_NAMESPACE::snprintf_u32_utf16(info->version, getPluginInfo().getVersion(), ARRAY_SIZE(info->version)); // FIXME + DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? + return V3_OK; + } - for (ScopedPointer* componentptr : components) - { - *componentptr = nullptr; - delete componentptr; - } + static V3_API v3_result set_host_context(void* self, v3_funknown* host) + { + d_stdout("dpf_factory::set_host_context => %p %p", self, host); + return V3_NOT_IMPLEMENTED; } DISTRHO_PREVENT_HEAP_ALLOCATION diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 42a708de..f0eee106 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -16,12 +16,24 @@ #include "DistrhoUIInternal.hpp" -#include - #include "travesty/edit_controller.h" #include "travesty/host.h" #include "travesty/view.h" +#ifdef DISTRHO_PROPER_CPP11_SUPPORT +# include +#else +// quick and dirty std::atomic replacement for the things we need +namespace std { + struct atomic_int { + volatile int value; + explicit atomic_int(volatile int v) noexcept : value(v) {} + int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } + int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } + }; +}; +#endif + /* TODO items: * - mousewheel event * - key down/up events @@ -555,6 +567,31 @@ private: * VST3 low-level pointer thingies follow, proceed with care. */ +// -------------------------------------------------------------------------------------------------------------------- +// v3_funknown for static and single instances of classes + +template +static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid iid, void** iface) +{ + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_interface)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; +} + +static V3_API uint32_t dpf_static__ref(void*) { return 1; } +static V3_API uint32_t dpf_static__unref(void*) { return 0; } + // -------------------------------------------------------------------------------------------------------------------- // dpf_ui_connection_point @@ -566,79 +603,61 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { : uivst3(v), other(nullptr) { - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_connection_point_iid - }; + static constexpr const v3_tuid interface = V3_ID_COPY(v3_connection_point_iid); - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown - - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_ui_connection_point::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + // v3_funknown, single instance + query_interface = dpf_static__query_interface; + ref = dpf_static__ref; + unref = dpf_static__unref; - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; - }; - - // there is only a single instance of this, so we don't have to care here - ref = []V3_API(void*) -> uint32_t { return 1; }; - unref = []V3_API(void*) -> uint32_t { return 0; }; - - // ------------------------------------------------------------------------------------------------------------ // v3_connection_point + point.connect = connect; + point.disconnect = disconnect; + point.notify = notify; + } - point.connect = []V3_API(void* self, v3_connection_point** other) -> v3_result - { - d_stdout("dpf_ui_connection_point::connect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); + // ---------------------------------------------------------------------------------------------------------------- + // v3_connection_point - point->other = other; + static V3_API v3_result connect(void* self, v3_connection_point** other) + { + d_stdout("dpf_ui_connection_point::connect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); - if (UIVst3* const uivst3 = point->uivst3) - uivst3->connect(other); + point->other = other; - return V3_OK; - }; + if (UIVst3* const uivst3 = point->uivst3) + uivst3->connect(other); - point.disconnect = []V3_API(void* self, v3_connection_point** other) -> v3_result - { - d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + return V3_OK; + }; - point->other = nullptr; + static V3_API v3_result disconnect(void* self, v3_connection_point** other) + { + d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); - if (UIVst3* const uivst3 = point->uivst3) - uivst3->disconnect(); + point->other = nullptr; - return V3_OK; - }; + if (UIVst3* const uivst3 = point->uivst3) + uivst3->disconnect(); - point.notify = []V3_API(void* self, v3_message** message) -> v3_result - { - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED); + return V3_OK; + }; + + static V3_API v3_result notify(void* self, v3_message** message) + { + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); - UIVst3* const uivst3 = point->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + UIVst3* const uivst3 = point->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); - return uivst3->notify(message); - }; + return uivst3->notify(message); } }; @@ -654,57 +673,32 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { : uivst3(v), scaleFactor(0.0f) { - query_interface = query_interface_fn; - ref = ref_fn; - unref = unref_fn; - scale.set_content_scale_factor = set_content_scale_factor_fn; - } - - // ---------------------------------------------------------------------------------------------------------------- - // v3_funknown - - static v3_result V3_API query_interface_fn(void* self, const v3_tuid iid, void** iface) - { - d_stdout("dpf_plugin_view_content_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + static constexpr const v3_tuid interface = V3_ID_COPY(v3_plugin_view_content_scale_iid); - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_plugin_view_content_scale_iid - }; + // v3_funknown, single instance + query_interface = dpf_static__query_interface; + ref = dpf_static__ref; + unref = dpf_static__unref; - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; + // v3_plugin_view_content_scale + scale.set_content_scale_factor = set_content_scale_factor; } - // there is only a single instance of this, so we don't have to care here - static uint32_t V3_API ref_fn(void*) { return 1; }; - static uint32_t V3_API unref_fn(void*) { return 0; }; - // ---------------------------------------------------------------------------------------------------------------- - // v3_plugin_view_content_scale_steinberg + // v3_plugin_view_content_scale - static v3_result V3_API set_content_scale_factor_fn(void* self, float factor) + static V3_API v3_result set_content_scale_factor(void* self, float factor) { d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); dpf_plugin_view_content_scale* const scale = *(dpf_plugin_view_content_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALISED); + DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALIZED); scale->scaleFactor = factor; if (UIVst3* const uivst3 = scale->uivst3) return uivst3->setContentScaleFactor(factor); - return V3_NOT_INITIALISED; + return V3_NOT_INITIALIZED; } }; @@ -718,46 +712,21 @@ struct dpf_timer_handler : v3_timer_handler_cpp { dpf_timer_handler(ScopedPointer& v) : uivst3(v) { - query_interface = query_interface_fn; - ref = ref_fn; - unref = unref_fn; - handler.on_timer = on_timer; - } + static constexpr const v3_tuid interface = V3_ID_COPY(v3_timer_handler_iid); - // ---------------------------------------------------------------------------------------------------------------- - // v3_funknown + // v3_funknown, single instance + query_interface = dpf_static__query_interface; + ref = dpf_static__ref; + unref = dpf_static__unref; - static v3_result V3_API query_interface_fn(void* self, const v3_tuid iid, void** iface) - { - d_stdout("dpf_plugin_view_content_scale::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_plugin_view_content_scale_iid - }; - - for (const uint8_t* interface_iid : kSupportedInterfaces) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } - - return V3_NO_INTERFACE; + // v3_timer_handler + handler.on_timer = on_timer; } - // there is only a single instance of this, so we don't have to care here - static uint32_t V3_API ref_fn(void*) { return 1; }; - static uint32_t V3_API unref_fn(void*) { return 0; }; - // ---------------------------------------------------------------------------------------------------------------- // v3_timer_handler - static void V3_API on_timer(void* self) + static V3_API void on_timer(void* self) { dpf_timer_handler* const handler = *(dpf_timer_handler**)self; DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); @@ -770,8 +739,18 @@ struct dpf_timer_handler : v3_timer_handler_cpp { // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view +static const char* const kSupportedPlatforms[] = { +#ifdef _WIN32 + V3_VIEW_PLATFORM_TYPE_HWND, +#elif defined(__APPLE__) + V3_VIEW_PLATFORM_TYPE_NSVIEW, +#else + V3_VIEW_PLATFORM_TYPE_X11, +#endif +}; + struct dpf_plugin_view : v3_plugin_view_cpp { - std::atomic refcounter; + std::atomic_int refcounter; ScopedPointer* self; ScopedPointer connection; ScopedPointer scale; @@ -794,294 +773,301 @@ struct dpf_plugin_view : v3_plugin_view_cpp { sampleRate(sr), frame(nullptr) { - static const uint8_t* kSupportedInterfacesBase[] = { - v3_funknown_iid, - v3_plugin_view_iid - }; - - static const char* const kSupportedPlatforms[] = { -#ifdef _WIN32 - V3_VIEW_PLATFORM_TYPE_HWND, -#elif defined(__APPLE__) - V3_VIEW_PLATFORM_TYPE_NSVIEW, -#else - V3_VIEW_PLATFORM_TYPE_X11, -#endif - }; + // v3_funknown, everything custom + query_interface = query_interface_view; + ref = ref_view; + unref = unref_view; - // ------------------------------------------------------------------------------------------------------------ - // v3_funknown + // v3_plugin_view + view.is_platform_type_supported = is_platform_type_supported; + view.attached = attached; + view.removed = removed; + view.on_wheel = on_wheel; + view.on_key_down = on_key_down; + view.on_key_up = on_key_up; + view.get_size = get_size; + view.on_size = on_size; + view.on_focus = on_focus; + view.set_frame = set_frame; + view.can_resize = can_resize; + view.check_size_constraint = check_size_constraint; + } - query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result - { - d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown - for (const uint8_t* interface_iid : kSupportedInterfacesBase) - { - if (v3_tuid_match(interface_iid, iid)) - { - *iface = self; - return V3_OK; - } - } + static V3_API v3_result query_interface_view(void* self, const v3_tuid iid, void** iface) + { + d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } - if (v3_tuid_match(v3_connection_point_iid, iid)) - { - if (view->connection == nullptr) - view->connection = new dpf_ui_connection_point(view->uivst3); - *iface = &view->connection; - return V3_OK; - } + if (v3_tuid_match(iid, v3_plugin_view_iid)) + { + *iface = self; + return V3_OK; + } - if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) - { - if (view->scale == nullptr) - view->scale = new dpf_plugin_view_content_scale(view->uivst3); - *iface = &view->scale; - return V3_OK; - } + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); - return V3_NO_INTERFACE; - }; + if (v3_tuid_match(v3_connection_point_iid, iid)) + { + if (view->connection == nullptr) + view->connection = new dpf_ui_connection_point(view->uivst3); + *iface = &view->connection; + return V3_OK; + } - ref = []V3_API(void* self) -> uint32_t + if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) { - d_stdout("dpf_plugin_view::ref => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); + if (view->scale == nullptr) + view->scale = new dpf_plugin_view_content_scale(view->uivst3); + *iface = &view->scale; + return V3_OK; + } - return ++view->refcounter; - }; + return V3_NO_INTERFACE; + } - unref = []V3_API(void* self) -> uint32_t - { - d_stdout("dpf_plugin_view::unref => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, 0); + static V3_API uint32_t ref_view(void* self) + { + return ++(*(dpf_plugin_view**)self)->refcounter; + } - if (const int refcounter = --view->refcounter) - return refcounter; + static V3_API uint32_t unref_view(void* self) + { + ScopedPointer* const viewptr = (ScopedPointer*)self; + dpf_plugin_view* const view = *viewptr; - if (view->connection != nullptr && view->connection->other) - v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, - (v3_connection_point**)&view->connection); + if (const int refcount = --view->refcounter) + { + d_stdout("dpf_plugin_view::unref => %p | refcount %i", self, refcount); + return refcount; + } - *view->self = nullptr; - delete (dpf_plugin_view**)self; - return 0; - }; + d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); - // ------------------------------------------------------------------------------------------------------------ - // v3_plugin_view + if (view->connection != nullptr && view->connection->other) + v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, + (v3_connection_point**)&view->connection); - view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result - { - d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + *(view->self) = nullptr; + delete viewptr; + return 0; + } - for (size_t i=0; i %p %s", self, platform_type); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - view.attached = []V3_API(void* self, void* parent, const char* platform_type) -> v3_result + for (size_t i=0; i %p %p %s", self, parent, platform_type); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); + if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) + return V3_OK; + } - for (size_t i=0; iframe != nullptr, V3_INVALID_ARG); - - v3_run_loop** runloop = nullptr; - v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); - DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); - #endif - - const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - view->uivst3 = new UIVst3((v3_plugin_view**)view->self, - view->host, - (uintptr_t)parent, - scaleFactor, - view->sampleRate, - view->instancePointer); - - if (dpf_ui_connection_point* const point = view->connection) - if (point->other != nullptr) - view->uivst3->connect(point->other); - - view->uivst3->setFrame(view->frame); - - #ifdef DPF_VST3_USING_HOST_RUN_LOOP - // register a timer host run loop stuff - view->timer = new dpf_timer_handler(view->uivst3); - v3_cpp_obj(runloop)->register_timer(runloop, - (v3_timer_handler**)&view->timer, - DPF_VST3_TIMER_INTERVAL); - #endif - - return V3_OK; - } - } + return V3_NOT_IMPLEMENTED; + }; - return V3_NOT_IMPLEMENTED; - }; + static V3_API v3_result attached(void* self, void* parent, const char* platform_type) + { + d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); - view.removed = []V3_API(void* self) -> v3_result + for (size_t i=0; i %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); - DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); - - #ifdef DPF_VST3_USING_HOST_RUN_LOOP - // unregister our timer as needed - if (view->timer != nullptr) + if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { + #ifdef DPF_VST3_USING_HOST_RUN_LOOP + // find host run loop to plug ourselves into (required on some systems) + DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG); + v3_run_loop** runloop = nullptr; - if (v3_cpp_obj_query_interface(view->host, v3_run_loop_iid, &runloop) == V3_OK && runloop != nullptr) - v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); + v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); + DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); + #endif + + const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; + view->uivst3 = new UIVst3((v3_plugin_view**)view->self, + view->host, + (uintptr_t)parent, + scaleFactor, + view->sampleRate, + view->instancePointer); + + if (dpf_ui_connection_point* const point = view->connection) + if (point->other != nullptr) + view->uivst3->connect(point->other); + + view->uivst3->setFrame(view->frame); + + #ifdef DPF_VST3_USING_HOST_RUN_LOOP + // register a timer host run loop stuff + view->timer = new dpf_timer_handler(view->uivst3); + v3_cpp_obj(runloop)->register_timer(runloop, + (v3_timer_handler**)&view->timer, + DPF_VST3_TIMER_INTERVAL); + #endif - view->timer = nullptr; + return V3_OK; } - #endif + } - view->uivst3 = nullptr; - return V3_OK; - }; + return V3_NOT_IMPLEMENTED; + }; - view.on_wheel = []V3_API(void* self, float distance) -> v3_result + static V3_API v3_result removed(void* self) + { + d_stdout("dpf_plugin_view::removed => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); + + #ifdef DPF_VST3_USING_HOST_RUN_LOOP + // unregister our timer as needed + if (view->timer != nullptr) { - d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + v3_run_loop** runloop = nullptr; + if (v3_cpp_obj_query_interface(view->host, v3_run_loop_iid, &runloop) == V3_OK && runloop != nullptr) + v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + view->timer = nullptr; + } + #endif - return uivst3->onWheel(distance); - }; + view->uivst3 = nullptr; + return V3_OK; + }; - view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result - { - d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result on_wheel(void* self, float distance) + { + d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); - return uivst3->onKeyDown(key_char, key_code, modifiers); - }; + return uivst3->onWheel(distance); + }; - view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result - { - d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result on_key_down(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) + { + d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); - return uivst3->onKeyUp(key_char, key_code, modifiers); - }; + return uivst3->onKeyDown(key_char, key_code, modifiers); + }; - view.get_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result - { - d_stdout("dpf_plugin_view::get_size => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result on_key_up(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) + { + d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - if (UIVst3* const uivst3 = view->uivst3) - return uivst3->getSize(rect); + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); - // special case: allow UI to not be attached yet, as a way to get size before window creation + return uivst3->onKeyUp(key_char, key_code, modifiers); + }; - const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - UIExporter tmpUI(nullptr, 0, view->sampleRate, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->instancePointer, scaleFactor); - rect->right = tmpUI.getWidth(); - rect->bottom = tmpUI.getHeight(); - return V3_OK; - }; + static V3_API v3_result get_size(void* self, v3_view_rect* rect) + { + d_stdout("dpf_plugin_view::get_size => %p", self); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - view.on_size = []V3_API(void* self, v3_view_rect* rect) -> v3_result - { - d_stdout("dpf_plugin_view::on_size => %p %p", self, rect); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + if (UIVst3* const uivst3 = view->uivst3) + return uivst3->getSize(rect); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + // special case: allow UI to not be attached yet, as a way to get size before window creation - return uivst3->onSize(rect); - }; + const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; + UIExporter tmpUI(nullptr, 0, view->sampleRate, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + view->instancePointer, scaleFactor); + rect->right = tmpUI.getWidth(); + rect->bottom = tmpUI.getHeight(); + return V3_OK; + }; - view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result - { - d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result on_size(void* self, v3_view_rect* rect) + { + d_stdout("dpf_plugin_view::on_size => %p %p", self, rect); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); - return uivst3->onFocus(state); - }; + return uivst3->onSize(rect); + }; - view.set_frame = []V3_API(void* self, v3_plugin_frame** frame) -> v3_result - { - d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result on_focus(void* self, v3_bool state) + { + d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - view->frame = frame; + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); - if (UIVst3* const uivst3 = view->uivst3) - return uivst3->setFrame(frame); + return uivst3->onFocus(state); + }; - return V3_NOT_INITIALISED; - }; + static V3_API v3_result set_frame(void* self, v3_plugin_frame** frame) + { + d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - view.can_resize = []V3_API(void* self) -> v3_result - { - d_stdout("dpf_plugin_view::can_resize => %p", self); + view->frame = frame; + + if (UIVst3* const uivst3 = view->uivst3) + return uivst3->setFrame(frame); + + return V3_NOT_INITIALIZED; + }; + + static V3_API v3_result can_resize(void* self) + { + d_stdout("dpf_plugin_view::can_resize => %p", self); // #if DISTRHO_UI_USER_RESIZABLE // return V3_OK; // #else - return V3_NOT_IMPLEMENTED; + return V3_NOT_IMPLEMENTED; // #endif - }; + }; - view.check_size_constraint = []V3_API(void* self, v3_view_rect* rect) -> v3_result - { - d_stdout("dpf_plugin_view::check_size_constraint => %p %p", self, rect); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALISED); + static V3_API v3_result check_size_constraint(void* self, v3_view_rect* rect) + { + d_stdout("dpf_plugin_view::check_size_constraint => %p %p", self, rect); + dpf_plugin_view* const view = *(dpf_plugin_view**)self; + DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALISED); + UIVst3* const uivst3 = view->uivst3; + DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); - return uivst3->checkSizeConstraint(rect); - }; - } + return uivst3->checkSizeConstraint(rect); + }; }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h index 6f00d89b..85b2297d 100644 --- a/distrho/src/travesty/audio_processor.h +++ b/distrho/src/travesty/audio_processor.h @@ -238,4 +238,28 @@ struct v3_audio_processor { static constexpr const v3_tuid v3_audio_processor_iid = V3_ID(0x42043F99, 0xB7DA453C, 0xA569E79D, 0x9AAEC33D); +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_param_value_queue_cpp : v3_funknown { + v3_param_value_queue queue; +}; + +struct v3_param_changes_cpp : v3_funknown { + v3_param_changes changes; +}; + +struct v3_process_context_requirements_cpp : v3_funknown { + v3_process_context_requirements req; +}; + +struct v3_audio_processor_cpp : v3_funknown { + v3_audio_processor proc; +}; + +#endif + #include "align_pop.h" diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index bc7b5fd1..86a2378c 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -128,7 +128,7 @@ enum { V3_INVALID_ARG, V3_NOT_IMPLEMENTED, V3_INTERNAL_ERR, - V3_NOT_INITIALISED, + V3_NOT_INITIALIZED, V3_NOMEM }; @@ -155,6 +155,10 @@ enum { } #endif // V3_COM_COMPAT +#define V3_ID_COPY(iid) \ + { iid[0], iid[1], iid[ 2], iid[ 3], iid[ 4], iid[ 5], iid[ 6], iid[ 7], \ + iid[8], iid[9], iid[10], iid[11], iid[12], iid[13], iid[14], iid[15] } + /** * funknown */ diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index db3f46be..4db5eb91 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -101,3 +101,17 @@ struct v3_plugin_factory_3 { static constexpr const v3_tuid v3_plugin_factory_3_iid = V3_ID(0x4555A2AB, 0xC1234E57, 0x9B122910, 0x36878931); + +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_plugin_factory_cpp : v3_funknown { + v3_plugin_factory v1; + v3_plugin_factory_2 v2; + v3_plugin_factory_3 v3; +}; + +#endif diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h index 733a0e2b..3cd77a68 100644 --- a/distrho/src/travesty/message.h +++ b/distrho/src/travesty/message.h @@ -77,11 +77,11 @@ static constexpr const v3_tuid v3_connection_point_iid = */ struct v3_attribute_list_cpp : v3_funknown { - v3_attribute_list attrlist; + v3_attribute_list attrlist; }; struct v3_message_cpp : v3_funknown { - v3_message msg; + v3_message msg; }; struct v3_connection_point_cpp : v3_funknown { From d9044f27fe462d1827a8fcd2a2fa0bf988c0067f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 03:34:29 +0100 Subject: [PATCH 135/504] Prevent crashes when UI initializes too early Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 6 +++++- distrho/src/DistrhoUIInternal.hpp | 2 ++ distrho/src/DistrhoUIPrivateData.hpp | 12 ++++++++++++ distrho/src/DistrhoUIVST3.cpp | 22 +++++++++++----------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 27705e63..15a1d280 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -33,7 +33,11 @@ #define DGL_DEBUG_EVENTS #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) -# include +# ifdef DISTRHO_PROPER_CPP11_SUPPORT +# include +# else +# include +# endif #endif START_NAMESPACE_DGL diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 35606a98..65c9d93d 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -99,6 +99,8 @@ public: DISTRHO_SAFE_ASSERT_RETURN(uiPtr != nullptr,); ui = uiPtr; + uiData->initializing = false; + #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI // unused (void)bundlePath; diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 93a8a89a..80894ce0 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -285,6 +285,7 @@ struct UI::PrivateData { void* dspPtr; // UI + bool initializing; uint bgColor; uint fgColor; double scaleFactor; @@ -308,6 +309,7 @@ struct UI::PrivateData { sampleRate(0), parameterOffset(0), dspPtr(nullptr), + initializing(true), bgColor(0), fgColor(0xffffffff), scaleFactor(1.0), @@ -358,30 +360,40 @@ struct UI::PrivateData { void editParamCallback(const uint32_t rindex, const bool started) { + DISTRHO_SAFE_ASSERT_RETURN(!initializing,); + if (editParamCallbackFunc != nullptr) editParamCallbackFunc(callbacksPtr, rindex, started); } void setParamCallback(const uint32_t rindex, const float value) { + DISTRHO_SAFE_ASSERT_RETURN(!initializing,); + if (setParamCallbackFunc != nullptr) setParamCallbackFunc(callbacksPtr, rindex, value); } void setStateCallback(const char* const key, const char* const value) { + DISTRHO_SAFE_ASSERT_RETURN(!initializing,); + if (setStateCallbackFunc != nullptr) setStateCallbackFunc(callbacksPtr, key, value); } void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) { + DISTRHO_SAFE_ASSERT_RETURN(!initializing,); + if (sendNoteCallbackFunc != nullptr) sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); } void setSizeCallback(const uint width, const uint height) { + DISTRHO_SAFE_ASSERT_RETURN(!initializing,); + if (setSizeCallbackFunc != nullptr) setSizeCallbackFunc(callbacksPtr, width, height); } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index f0eee106..20217169 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -486,17 +486,17 @@ private: DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); d_stdout("from UI setSize %u %u | %p %p", width, height, fView, fFrame); -// #ifdef DISTRHO_OS_MAC -// const double scaleFactor = fUI.getScaleFactor(); -// width /= scaleFactor; -// height /= scaleFactor; -// #endif -// -// v3_view_rect rect; -// std::memset(&rect, 0, sizeof(rect)); -// rect.right = width; -// rect.bottom = height; -// v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); +#ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + width /= scaleFactor; + height /= scaleFactor; +#endif + + v3_view_rect rect; + std::memset(&rect, 0, sizeof(rect)); + rect.right = width; + rect.bottom = height; + v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); } static void setSizeCallback(void* ptr, uint width, uint height) From 4893656da0f83c16ed1e1f11a8dd87f1cb483a79 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 04:37:05 +0100 Subject: [PATCH 136/504] A few more VST3 tests on refcounter and host context Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 60 ++++++++++++----- distrho/src/DistrhoUIVST3.cpp | 107 +++++++++++++++++++++--------- distrho/src/travesty/factory.h | 2 +- 3 files changed, 118 insertions(+), 51 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index d92dd48c..4569e746 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2311,13 +2311,16 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); - point->other = nullptr; - point->bridge = nullptr; - if (point->type == kConnectionPointComponent) if (PluginVst3* const vst3 = point->vst3) vst3->disconnect(); + if (point->type == kConnectionPointBridge) + v3_cpp_obj_unref(point->other); + + point->other = nullptr; + point->bridge = nullptr; + return V3_OK; } @@ -2453,13 +2456,15 @@ struct dpf_edit_controller : v3_edit_controller_cpp { bool initialized; // cached values v3_component_handler** handler; + v3_host_application** const originalHostContext; v3_plugin_base::v3_funknown** hostContext; - dpf_edit_controller(ScopedPointer& v) + dpf_edit_controller(ScopedPointer& v, v3_host_application** const h) : refcounter(1), vst3(v), initialized(false), handler(nullptr), + originalHostContext(h), hostContext(nullptr) { // v3_funknown, single instance with custom query @@ -2757,10 +2762,14 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); // we require host context for message creation - DISTRHO_SAFE_ASSERT_RETURN(controller->hostContext != nullptr, nullptr); - v3_host_application** host = nullptr; - v3_cpp_obj_query_interface(controller->hostContext, v3_host_application_iid, &host); + + if (controller->hostContext != nullptr) + v3_cpp_obj_query_interface(controller->hostContext, v3_host_application_iid, &host); + + if (host == nullptr) + host = controller->originalHostContext; + DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); // if there is a component connection, we require it to be active @@ -3031,10 +3040,12 @@ struct dpf_component : v3_component_cpp { #endif ScopedPointer controller; ScopedPointer vst3; + v3_host_application** const hostContext; - dpf_component(ScopedPointer* const s) + dpf_component(ScopedPointer* const s, v3_host_application** const h) : refcounter(1), - self(s) + self(s), + hostContext(h) { // v3_funknown, everything custom query_interface = query_interface_component; @@ -3109,7 +3120,7 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_edit_controller_iid)) { if (component->controller == nullptr) - component->controller = new dpf_edit_controller(component->vst3); + component->controller = new dpf_edit_controller(component->vst3, component->hostContext); else ++component->controller->refcounter; *iface = &component->controller; @@ -3225,6 +3236,9 @@ struct dpf_component : v3_component_cpp { v3_host_application** host = nullptr; v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + if (host == nullptr) + host = component->hostContext; + component->vst3 = new PluginVst3(host); return V3_OK; } @@ -3373,9 +3387,6 @@ static const PluginExporter& _getPluginInfo() d_lastSampleRate = 0.0; d_lastCanRequestParameterValueChanges = false; - dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] - = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo.getUniqueId(); - return gPluginInfo; } @@ -3389,12 +3400,19 @@ static const PluginExporter& getPluginInfo() // dpf_factory struct dpf_factory : v3_plugin_factory_cpp { + // cached values + v3_funknown** hostContext; + dpf_factory() + : hostContext(nullptr) { static constexpr const v3_tuid interface_v1 = V3_ID_COPY(v3_plugin_factory_iid); static constexpr const v3_tuid interface_v2 = V3_ID_COPY(v3_plugin_factory_2_iid); static constexpr const v3_tuid interface_v3 = V3_ID_COPY(v3_plugin_factory_3_iid); + dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] + = dpf_tuid_processor[2] = dpf_tuid_view[2] = getPluginInfo().getUniqueId(); + // v3_funknown, used statically query_interface = dpf_static__query_interface; ref = dpf_static__ref; @@ -3476,8 +3494,12 @@ struct dpf_factory : v3_plugin_factory_cpp { dpf_factory* const factory = *(dpf_factory**)self; DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALIZED); + // query for host context + v3_host_application** host = nullptr; + v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &host); + ScopedPointer* const componentptr = new ScopedPointer; - *componentptr = new dpf_component(componentptr); + *componentptr = new dpf_component(componentptr, host); *instance = componentptr; return V3_OK; } @@ -3524,10 +3546,14 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; } - static V3_API v3_result set_host_context(void* self, v3_funknown* host) + static V3_API v3_result set_host_context(void* self, v3_funknown** context) { - d_stdout("dpf_factory::set_host_context => %p %p", self, host); - return V3_NOT_IMPLEMENTED; + d_stdout("dpf_factory::set_host_context => %p %p", self, context); + dpf_factory* const factory = *(dpf_factory**)self; + DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALIZED); + + factory->hostContext = context; + return V3_OK; } DISTRHO_PREVENT_HEAP_ALLOCATION diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 20217169..c7d281f5 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -30,6 +30,7 @@ namespace std { explicit atomic_int(volatile int v) noexcept : value(v) {} int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } + operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); } }; }; #endif @@ -592,23 +593,40 @@ static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid ii static V3_API uint32_t dpf_static__ref(void*) { return 1; } static V3_API uint32_t dpf_static__unref(void*) { return 0; } +// -------------------------------------------------------------------------------------------------------------------- +// v3_funknown with refcounter + +template +static V3_API uint32_t dpf_refcounter__ref(void* self) +{ + return ++(*(T**)self)->refcounter; +} + +template +static V3_API uint32_t dpf_refcounter__unref(void* self) +{ + return --(*(T**)self)->refcounter; +} + // -------------------------------------------------------------------------------------------------------------------- // dpf_ui_connection_point struct dpf_ui_connection_point : v3_connection_point_cpp { + std::atomic_int refcounter; ScopedPointer& uivst3; v3_connection_point** other; dpf_ui_connection_point(ScopedPointer& v) - : uivst3(v), + : refcounter(1), + uivst3(v), other(nullptr) { static constexpr const v3_tuid interface = V3_ID_COPY(v3_connection_point_iid); // v3_funknown, single instance query_interface = dpf_static__query_interface; - ref = dpf_static__ref; - unref = dpf_static__unref; + ref = dpf_refcounter__ref; + unref = dpf_refcounter__unref; // v3_connection_point point.connect = connect; @@ -665,20 +683,22 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { // dpf_plugin_view_content_scale struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { + std::atomic_int refcounter; ScopedPointer& uivst3; // cached values float scaleFactor; dpf_plugin_view_content_scale(ScopedPointer& v) - : uivst3(v), + : refcounter(1), + uivst3(v), scaleFactor(0.0f) { static constexpr const v3_tuid interface = V3_ID_COPY(v3_plugin_view_content_scale_iid); // v3_funknown, single instance query_interface = dpf_static__query_interface; - ref = dpf_static__ref; - unref = dpf_static__unref; + ref = dpf_refcounter__ref; + unref = dpf_refcounter__unref; // v3_plugin_view_content_scale scale.set_content_scale_factor = set_content_scale_factor; @@ -751,7 +771,7 @@ static const char* const kSupportedPlatforms[] = { struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic_int refcounter; - ScopedPointer* self; + dpf_plugin_view** const self; ScopedPointer connection; ScopedPointer scale; #ifdef DPF_VST3_USING_HOST_RUN_LOOP @@ -762,12 +782,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { v3_host_application** const host; void* const instancePointer; double sampleRate; - v3_plugin_frame** frame = nullptr; + v3_plugin_frame** frame; - dpf_plugin_view(ScopedPointer* selfptr, - v3_host_application** const h, void* const instance, const double sr) + dpf_plugin_view(dpf_plugin_view** const s, v3_host_application** const h, void* const instance, const double sr) : refcounter(1), - self(selfptr), + self(s), host(h), instancePointer(instance), sampleRate(sr), @@ -843,7 +862,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static V3_API uint32_t unref_view(void* self) { - ScopedPointer* const viewptr = (ScopedPointer*)self; + dpf_plugin_view** const viewptr = (dpf_plugin_view**)self; dpf_plugin_view* const view = *viewptr; if (const int refcount = --view->refcounter) @@ -854,11 +873,31 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); + DISTRHO_SAFE_ASSERT_RETURN(viewptr == view->self, V3_INTERNAL_ERR); + if (view->connection != nullptr && view->connection->other) v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, (v3_connection_point**)&view->connection); - *(view->self) = nullptr; + if (dpf_ui_connection_point* const conn = view->connection) + { + if (const int refcount = conn->refcounter) + { + d_stderr("DPF warning: asked to delete view while connection point still active (refcount %d)", refcount); + return V3_INVALID_ARG; + } + } + + if (dpf_plugin_view_content_scale* const scale = view->scale) + { + if (const int refcount = scale->refcounter) + { + d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount); + return V3_INVALID_ARG; + } + } + + delete view; delete viewptr; return 0; } @@ -879,7 +918,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { } return V3_NOT_IMPLEMENTED; - }; + } static V3_API v3_result attached(void* self, void* parent, const char* platform_type) { @@ -892,14 +931,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { - #ifdef DPF_VST3_USING_HOST_RUN_LOOP + #ifdef DPF_VST3_USING_HOST_RUN_LOOP // find host run loop to plug ourselves into (required on some systems) DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG); v3_run_loop** runloop = nullptr; v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); - #endif + #endif const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)view->self, @@ -915,20 +954,20 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->uivst3->setFrame(view->frame); - #ifdef DPF_VST3_USING_HOST_RUN_LOOP + #ifdef DPF_VST3_USING_HOST_RUN_LOOP // register a timer host run loop stuff view->timer = new dpf_timer_handler(view->uivst3); v3_cpp_obj(runloop)->register_timer(runloop, (v3_timer_handler**)&view->timer, DPF_VST3_TIMER_INTERVAL); - #endif + #endif return V3_OK; } } return V3_NOT_IMPLEMENTED; - }; + } static V3_API v3_result removed(void* self) { @@ -937,21 +976,23 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); - #ifdef DPF_VST3_USING_HOST_RUN_LOOP + #ifdef DPF_VST3_USING_HOST_RUN_LOOP // unregister our timer as needed if (view->timer != nullptr) { v3_run_loop** runloop = nullptr; - if (v3_cpp_obj_query_interface(view->host, v3_run_loop_iid, &runloop) == V3_OK && runloop != nullptr) + v3_cpp_obj_query_interface(view->host, v3_run_loop_iid, &runloop); + + if (runloop != nullptr) v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); view->timer = nullptr; } - #endif + #endif view->uivst3 = nullptr; return V3_OK; - }; + } static V3_API v3_result on_wheel(void* self, float distance) { @@ -963,7 +1004,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onWheel(distance); - }; + } static V3_API v3_result on_key_down(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) { @@ -975,7 +1016,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onKeyDown(key_char, key_code, modifiers); - }; + } static V3_API v3_result on_key_up(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) { @@ -987,7 +1028,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onKeyUp(key_char, key_code, modifiers); - }; + } static V3_API v3_result get_size(void* self, v3_view_rect* rect) { @@ -1007,7 +1048,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { rect->right = tmpUI.getWidth(); rect->bottom = tmpUI.getHeight(); return V3_OK; - }; + } static V3_API v3_result on_size(void* self, v3_view_rect* rect) { @@ -1019,7 +1060,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onSize(rect); - }; + } static V3_API v3_result on_focus(void* self, v3_bool state) { @@ -1031,7 +1072,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onFocus(state); - }; + } static V3_API v3_result set_frame(void* self, v3_plugin_frame** frame) { @@ -1045,7 +1086,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->setFrame(frame); return V3_NOT_INITIALIZED; - }; + } static V3_API v3_result can_resize(void* self) { @@ -1055,7 +1096,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // #else return V3_NOT_IMPLEMENTED; // #endif - }; + } static V3_API v3_result check_size_constraint(void* self, v3_view_rect* rect) { @@ -1067,7 +1108,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->checkSizeConstraint(rect); - }; + } }; // -------------------------------------------------------------------------------------------------------------------- @@ -1079,7 +1120,7 @@ v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host, void* const instancePointer, const double sampleRate) { - ScopedPointer* const viewptr = new ScopedPointer; + dpf_plugin_view** const viewptr = new dpf_plugin_view*; *viewptr = new dpf_plugin_view(viewptr, host, instancePointer, sampleRate); return (v3_plugin_view**)static_cast(viewptr); } diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index 4db5eb91..39629750 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -96,7 +96,7 @@ struct v3_plugin_factory_3 { struct v3_plugin_factory_2; v3_result (V3_API *get_class_info_utf16)(void* self, int32_t idx, struct v3_class_info_3*); - v3_result (V3_API *set_host_context)(void* self, struct v3_funknown* host); + v3_result (V3_API *set_host_context)(void* self, struct v3_funknown** host); }; static constexpr const v3_tuid v3_plugin_factory_3_iid = From 9fa65320dfbb9c5ef701a121e7392d3e9156b37d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 12:12:05 +0100 Subject: [PATCH 137/504] Fix VST3 UI cleanup issues Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 3 ++- distrho/src/DistrhoUIVST3.cpp | 40 +++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 4569e746..4b3d4247 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -3496,7 +3496,8 @@ struct dpf_factory : v3_plugin_factory_cpp { // query for host context v3_host_application** host = nullptr; - v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &host); + if (factory->hostContext != nullptr) + v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &host); ScopedPointer* const componentptr = new ScopedPointer; *componentptr = new dpf_component(componentptr, host); diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index c7d281f5..3df2f049 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -590,8 +590,8 @@ static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid ii return V3_NO_INTERFACE; } -static V3_API uint32_t dpf_static__ref(void*) { return 1; } -static V3_API uint32_t dpf_static__unref(void*) { return 0; } +// static V3_API uint32_t dpf_static__ref(void*) { return 1; } +// static V3_API uint32_t dpf_static__unref(void*) { return 0; } // -------------------------------------------------------------------------------------------------------------------- // v3_funknown with refcounter @@ -727,17 +727,21 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { // dpf_timer_handler struct dpf_timer_handler : v3_timer_handler_cpp { + std::atomic_int refcounter; ScopedPointer& uivst3; + bool valid; dpf_timer_handler(ScopedPointer& v) - : uivst3(v) + : refcounter(1), + uivst3(v), + valid(true) { static constexpr const v3_tuid interface = V3_ID_COPY(v3_timer_handler_iid); // v3_funknown, single instance query_interface = dpf_static__query_interface; - ref = dpf_static__ref; - unref = dpf_static__unref; + ref = dpf_refcounter__ref; + unref = dpf_refcounter__unref; // v3_timer_handler handler.on_timer = on_timer; @@ -750,6 +754,7 @@ struct dpf_timer_handler : v3_timer_handler_cpp { { dpf_timer_handler* const handler = *(dpf_timer_handler**)self; DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(handler->valid,); handler->uivst3->onTimer(); } @@ -840,6 +845,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { if (view->connection == nullptr) view->connection = new dpf_ui_connection_point(view->uivst3); + else + ++view->connection->refcounter; *iface = &view->connection; return V3_OK; } @@ -848,6 +855,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { if (view->scale == nullptr) view->scale = new dpf_plugin_view_content_scale(view->uivst3); + else + ++view->scale->refcounter; *iface = &view->scale; return V3_OK; } @@ -981,12 +990,29 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (view->timer != nullptr) { v3_run_loop** runloop = nullptr; - v3_cpp_obj_query_interface(view->host, v3_run_loop_iid, &runloop); + + if (view->frame != nullptr) + v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); if (runloop != nullptr) + { v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); - view->timer = nullptr; + if (const int refcount = --view->timer->refcounter) + { + view->timer->valid = false; + d_stderr("VST3 warning: Host run loop did not give away timer (refcount %d)", refcount); + } + else + { + view->timer = nullptr; + } + } + else + { + view->timer->valid = false; + d_stderr("VST3 warning: Host run loop not available during dpf_plugin_view::removed"); + } } #endif From b59c740c068d266921a4092eb69c7a94a50862c6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 15:00:33 +0100 Subject: [PATCH 138/504] Fix build with clang Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 260 +++++++++++++++--------- distrho/src/DistrhoUIVST3.cpp | 142 ++++++++----- examples/CVPort/ExamplePluginCVPort.cpp | 4 +- 3 files changed, 249 insertions(+), 157 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 4b3d4247..c2a29718 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -379,8 +379,8 @@ public: fComponentHandler(nullptr), #if DISTRHO_PLUGIN_HAS_UI fConnection(nullptr), -#endif fHostContext(context), +#endif fParameterOffset(fPlugin.getParameterOffset()), fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), fParameterValues(nullptr), @@ -486,6 +486,11 @@ public: fStateMap[dkey] = fPlugin.getStateDefaultValue(i); } #endif + +#if !DISTRHO_PLUGIN_HAS_UI + // unused + return; (void)context; +#endif } ~PluginVst3() @@ -1896,8 +1901,8 @@ private: v3_component_handler** fComponentHandler; #if DISTRHO_PLUGIN_HAS_UI v3_connection_point** fConnection; -#endif v3_host_application** const fHostContext; +#endif // Temporary data const uint32_t fParameterOffset; @@ -2187,67 +2192,29 @@ private: */ // -------------------------------------------------------------------------------------------------------------------- -// v3_funknown for static and single instances of classes - -template -static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid iid, void** iface) -{ - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } - - if (v3_tuid_match(iid, v3_interface)) - { - *iface = self; - return V3_OK; - } - - *iface = NULL; - return V3_NO_INTERFACE; -} +// v3_funknown for static instances -template -static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid iid, void** iface) -{ - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } - - if (v3_tuid_match(iid, v3_interface1) || v3_tuid_match(iid, v3_interface2) || v3_tuid_match(iid, v3_interface3)) - { - *iface = self; - return V3_OK; - } - - *iface = NULL; - return V3_NO_INTERFACE; -} - -static V3_API uint32_t dpf_static__ref(void*) { return 1; } -static V3_API uint32_t dpf_static__unref(void*) { return 0; } +static V3_API uint32_t dpf_static_ref(void*) { return 1; } +static V3_API uint32_t dpf_static_unref(void*) { return 0; } // -------------------------------------------------------------------------------------------------------------------- -// v3_funknown with refcounter +// v3_funknown for classes with a single instance template -static V3_API uint32_t dpf_refcounter__ref(void* self) +static V3_API uint32_t dpf_single_instance_ref(void* self) { return ++(*(T**)self)->refcounter; } template -static V3_API uint32_t dpf_refcounter__unref(void* self) +static V3_API uint32_t dpf_single_instance_unref(void* self) { return --(*(T**)self)->refcounter; } #if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- -// dpf_dsp_connection_point +// dpf_connection_point enum ConnectionPointType { kConnectionPointComponent, @@ -2255,7 +2222,7 @@ enum ConnectionPointType { kConnectionPointBridge }; -struct dpf_dsp_connection_point : v3_connection_point_cpp { +struct dpf_connection_point : v3_connection_point_cpp { std::atomic_int refcounter; ScopedPointer& vst3; const ConnectionPointType type; @@ -2263,7 +2230,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { v3_connection_point** bridge; // when type is controller this points to ctrl<->view point bool shortcircuit; // plugin as controller, should pass directly to view - dpf_dsp_connection_point(const ConnectionPointType t, ScopedPointer& v) + dpf_connection_point(const ConnectionPointType t, ScopedPointer& v) : refcounter(1), vst3(v), type(t), @@ -2271,12 +2238,10 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { bridge(nullptr), shortcircuit(false) { - static constexpr const v3_tuid interface = V3_ID_COPY(v3_connection_point_iid); - // v3_funknown, single instance - query_interface = dpf_static__query_interface; - ref = dpf_refcounter__ref; - unref = dpf_refcounter__unref; + query_interface = query_interface_connection_point; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_connection_point point.connect = connect; @@ -2284,13 +2249,34 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { point.notify = notify; } + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_connection_point(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_connection_point_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point static V3_API v3_result connect(void* self, v3_connection_point** other) { - d_stdout("dpf_dsp_connection_point::connect => %p %p", self, other); - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; + d_stdout("dpf_connection_point::connect => %p %p", self, other); + dpf_connection_point* const point = *(dpf_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT(point->bridge == nullptr); @@ -2306,8 +2292,8 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { static V3_API v3_result disconnect(void* self, v3_connection_point** other) { - d_stdout("dpf_dsp_connection_point::disconnect => %p %p", self, other); - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; + d_stdout("dpf_connection_point::disconnect => %p %p", self, other); + dpf_connection_point* const point = *(dpf_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); @@ -2326,7 +2312,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { static V3_API v3_result notify(void* self, v3_message** message) { - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; + dpf_connection_point* const point = *(dpf_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); PluginVst3* const vst3 = point->vst3; @@ -2409,17 +2395,36 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { struct dpf_midi_mapping : v3_midi_mapping_cpp { dpf_midi_mapping() { - static constexpr const v3_tuid interface = V3_ID_COPY(v3_midi_mapping_iid); - - // v3_funknown, used statically - query_interface = dpf_static__query_interface; - ref = dpf_static__ref; - unref = dpf_static__unref; + // v3_funknown, static + query_interface = query_interface_midi_mapping; + ref = dpf_static_ref; + unref = dpf_static_unref; // v3_midi_mapping map.get_midi_controller_assignment = get_midi_controller_assignment; } + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_midi_mapping(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_midi_mapping_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_midi_mapping @@ -2436,7 +2441,7 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { # endif *id = offset + channel * 130 + cc; - return V3_OK; + return V3_TRUE; } DISTRHO_PREVENT_HEAP_ALLOCATION @@ -2449,8 +2454,8 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { struct dpf_edit_controller : v3_edit_controller_cpp { std::atomic_int refcounter; #if DISTRHO_PLUGIN_HAS_UI - ScopedPointer connectionComp; // kConnectionPointController - ScopedPointer connectionBridge; // kConnectionPointBridge + ScopedPointer connectionComp; // kConnectionPointController + ScopedPointer connectionBridge; // kConnectionPointBridge #endif ScopedPointer& vst3; bool initialized; @@ -2467,10 +2472,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { originalHostContext(h), hostContext(nullptr) { - // v3_funknown, single instance with custom query + // v3_funknown, single instance query_interface = query_interface_edit_controller; - ref = dpf_refcounter__ref; - unref = dpf_refcounter__unref; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_plugin_base base.initialize = initialize; @@ -2522,7 +2527,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (v3_tuid_match(iid, v3_connection_point_iid)) { if (controller->connectionComp == nullptr) - controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, + controller->connectionComp = new dpf_connection_point(kConnectionPointController, controller->vst3); else ++controller->connectionComp->refcounter; @@ -2780,7 +2785,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // otherwise short-circuit and deal with this ourselves (assume local usage) else { - controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, + controller->connectionComp = new dpf_connection_point(kConnectionPointController, controller->vst3); controller->connectionComp->shortcircuit = true; } @@ -2809,7 +2814,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } else { - controller->connectionBridge = new dpf_dsp_connection_point(kConnectionPointBridge, + controller->connectionBridge = new dpf_connection_point(kConnectionPointBridge, controller->vst3); v3_connection_point** const bridgeconn = (v3_connection_point**)&controller->connectionBridge; @@ -2834,17 +2839,36 @@ struct dpf_edit_controller : v3_edit_controller_cpp { struct dpf_process_context_requirements : v3_process_context_requirements_cpp { dpf_process_context_requirements() { - static constexpr const v3_tuid interface = V3_ID_COPY(v3_process_context_requirements_iid); - - // v3_funknown, used statically - query_interface = dpf_static__query_interface; - ref = dpf_static__ref; - unref = dpf_static__unref; + // v3_funknown, static + query_interface = query_interface_process_context_requirements; + ref = dpf_static_ref; + unref = dpf_static_unref; // v3_process_context_requirements req.get_process_context_requirements = get_process_context_requirements; } + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_process_context_requirements(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_process_context_requirements_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_process_context_requirements @@ -2876,10 +2900,10 @@ struct dpf_audio_processor : v3_audio_processor_cpp { : refcounter(1), vst3(v) { - // v3_funknown, single instance with custom query - query_interface = query_interface_with_proc_ctx_reqs; - ref = dpf_refcounter__ref; - unref = dpf_refcounter__unref; + // v3_funknown, single instance + query_interface = query_interface_audio_processor; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_audio_processor proc.set_bus_arrangements = set_bus_arrangements; @@ -2895,12 +2919,19 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_with_proc_ctx_reqs(void* self, const v3_tuid iid, void** iface) + static V3_API v3_result query_interface_audio_processor(void* self, const v3_tuid iid, void** iface) { - static constexpr const v3_tuid interface = V3_ID_COPY(v3_audio_processor_iid); + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } - if (dpf_static__query_interface(self, iid, iface) == V3_OK) + if (v3_tuid_match(iid, v3_audio_processor_iid)) + { + *iface = self; return V3_OK; + } if (v3_tuid_match(iid, v3_process_context_requirements_iid)) { @@ -3036,7 +3067,7 @@ struct dpf_component : v3_component_cpp { ScopedPointer* self; ScopedPointer processor; #if DISTRHO_PLUGIN_HAS_UI - ScopedPointer connection; // kConnectionPointComponent + ScopedPointer connection; // kConnectionPointComponent #endif ScopedPointer controller; ScopedPointer vst3; @@ -3108,7 +3139,7 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_connection_point_iid)) { if (component->connection == nullptr) - component->connection = new dpf_dsp_connection_point(kConnectionPointComponent, + component->connection = new dpf_connection_point(kConnectionPointComponent, component->vst3); else ++component->connection->refcounter; @@ -3164,7 +3195,7 @@ struct dpf_component : v3_component_cpp { } #if DISTRHO_PLUGIN_HAS_UI - if (dpf_dsp_connection_point* const conn = component->connection) + if (dpf_connection_point* const conn = component->connection) { if (const int refcount = conn->refcounter) { @@ -3183,7 +3214,7 @@ struct dpf_component : v3_component_cpp { } #if DISTRHO_PLUGIN_HAS_UI - if (dpf_dsp_connection_point* const comp = ctrl->connectionComp) + if (dpf_connection_point* const comp = ctrl->connectionComp) { if (const int refcount = comp->refcounter) { @@ -3192,7 +3223,7 @@ struct dpf_component : v3_component_cpp { } } - if (dpf_dsp_connection_point* const bridge = ctrl->connectionBridge) + if (dpf_connection_point* const bridge = ctrl->connectionBridge) { if (const int refcount = bridge->refcounter) { @@ -3406,17 +3437,13 @@ struct dpf_factory : v3_plugin_factory_cpp { dpf_factory() : hostContext(nullptr) { - static constexpr const v3_tuid interface_v1 = V3_ID_COPY(v3_plugin_factory_iid); - static constexpr const v3_tuid interface_v2 = V3_ID_COPY(v3_plugin_factory_2_iid); - static constexpr const v3_tuid interface_v3 = V3_ID_COPY(v3_plugin_factory_3_iid); - dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] = dpf_tuid_processor[2] = dpf_tuid_view[2] = getPluginInfo().getUniqueId(); - // v3_funknown, used statically - query_interface = dpf_static__query_interface; - ref = dpf_static__ref; - unref = dpf_static__unref; + // v3_funknown, static + query_interface = query_interface_factory; + ref = dpf_static_ref; + unref = dpf_static_unref; // v3_plugin_factory v1.get_factory_info = get_factory_info; @@ -3456,6 +3483,39 @@ struct dpf_factory : v3_plugin_factory_cpp { gComponentGarbage.clear(); } + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_factory(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_plugin_factory_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_plugin_factory_2_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_plugin_factory_3_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_factory diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 3df2f049..cb735cb7 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -569,64 +569,37 @@ private: */ // -------------------------------------------------------------------------------------------------------------------- -// v3_funknown for static and single instances of classes - -template -static V3_API v3_result dpf_static__query_interface(void* self, const v3_tuid iid, void** iface) -{ - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } - - if (v3_tuid_match(iid, v3_interface)) - { - *iface = self; - return V3_OK; - } - - *iface = NULL; - return V3_NO_INTERFACE; -} - -// static V3_API uint32_t dpf_static__ref(void*) { return 1; } -// static V3_API uint32_t dpf_static__unref(void*) { return 0; } - -// -------------------------------------------------------------------------------------------------------------------- -// v3_funknown with refcounter +// v3_funknown for classes with a single instance template -static V3_API uint32_t dpf_refcounter__ref(void* self) +static V3_API uint32_t dpf_single_instance_ref(void* self) { return ++(*(T**)self)->refcounter; } template -static V3_API uint32_t dpf_refcounter__unref(void* self) +static V3_API uint32_t dpf_single_instance_unref(void* self) { return --(*(T**)self)->refcounter; } // -------------------------------------------------------------------------------------------------------------------- -// dpf_ui_connection_point +// dpf_connection_point -struct dpf_ui_connection_point : v3_connection_point_cpp { +struct dpf_connection_point : v3_connection_point_cpp { std::atomic_int refcounter; ScopedPointer& uivst3; v3_connection_point** other; - dpf_ui_connection_point(ScopedPointer& v) + dpf_connection_point(ScopedPointer& v) : refcounter(1), uivst3(v), other(nullptr) { - static constexpr const v3_tuid interface = V3_ID_COPY(v3_connection_point_iid); - // v3_funknown, single instance - query_interface = dpf_static__query_interface; - ref = dpf_refcounter__ref; - unref = dpf_refcounter__unref; + query_interface = query_interface_connection_point; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_connection_point point.connect = connect; @@ -634,13 +607,34 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { point.notify = notify; } + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_connection_point(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_connection_point_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point static V3_API v3_result connect(void* self, v3_connection_point** other) { - d_stdout("dpf_ui_connection_point::connect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + d_stdout("dpf_connection_point::connect => %p %p", self, other); + dpf_connection_point* const point = *(dpf_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); @@ -654,8 +648,8 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { static V3_API v3_result disconnect(void* self, v3_connection_point** other) { - d_stdout("dpf_ui_connection_point::disconnect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + d_stdout("dpf_connection_point::disconnect => %p %p", self, other); + dpf_connection_point* const point = *(dpf_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); @@ -669,7 +663,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { static V3_API v3_result notify(void* self, v3_message** message) { - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; + dpf_connection_point* const point = *(dpf_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); UIVst3* const uivst3 = point->uivst3; @@ -693,17 +687,36 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { uivst3(v), scaleFactor(0.0f) { - static constexpr const v3_tuid interface = V3_ID_COPY(v3_plugin_view_content_scale_iid); - // v3_funknown, single instance - query_interface = dpf_static__query_interface; - ref = dpf_refcounter__ref; - unref = dpf_refcounter__unref; + query_interface = query_interface_view_content_scale; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_plugin_view_content_scale scale.set_content_scale_factor = set_content_scale_factor; } + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_view_content_scale(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view_content_scale @@ -736,17 +749,36 @@ struct dpf_timer_handler : v3_timer_handler_cpp { uivst3(v), valid(true) { - static constexpr const v3_tuid interface = V3_ID_COPY(v3_timer_handler_iid); - // v3_funknown, single instance - query_interface = dpf_static__query_interface; - ref = dpf_refcounter__ref; - unref = dpf_refcounter__unref; + query_interface = query_interface_timer_handler; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_timer_handler handler.on_timer = on_timer; } + // ---------------------------------------------------------------------------------------------------------------- + // v3_funknown + + static V3_API v3_result query_interface_timer_handler(void* self, const v3_tuid iid, void** iface) + { + if (v3_tuid_match(iid, v3_funknown_iid)) + { + *iface = self; + return V3_OK; + } + + if (v3_tuid_match(iid, v3_timer_handler_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = NULL; + return V3_NO_INTERFACE; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_timer_handler @@ -777,7 +809,7 @@ static const char* const kSupportedPlatforms[] = { struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic_int refcounter; dpf_plugin_view** const self; - ScopedPointer connection; + ScopedPointer connection; ScopedPointer scale; #ifdef DPF_VST3_USING_HOST_RUN_LOOP ScopedPointer timer; @@ -844,7 +876,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (v3_tuid_match(v3_connection_point_iid, iid)) { if (view->connection == nullptr) - view->connection = new dpf_ui_connection_point(view->uivst3); + view->connection = new dpf_connection_point(view->uivst3); else ++view->connection->refcounter; *iface = &view->connection; @@ -888,7 +920,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, (v3_connection_point**)&view->connection); - if (dpf_ui_connection_point* const conn = view->connection) + if (dpf_connection_point* const conn = view->connection) { if (const int refcount = conn->refcounter) { @@ -957,7 +989,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->sampleRate, view->instancePointer); - if (dpf_ui_connection_point* const point = view->connection) + if (dpf_connection_point* const point = view->connection) if (point->other != nullptr) view->uivst3->connect(point->other); diff --git a/examples/CVPort/ExamplePluginCVPort.cpp b/examples/CVPort/ExamplePluginCVPort.cpp index b39115b3..4fb78c88 100644 --- a/examples/CVPort/ExamplePluginCVPort.cpp +++ b/examples/CVPort/ExamplePluginCVPort.cpp @@ -21,6 +21,8 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- +static constexpr const float kMaxHoldTime = 1.0f; + /** Simple plugin to demonstrate how to modify input/output port type in DPF. The plugin outputs sample & hold (S&H) value of input signal. @@ -28,8 +30,6 @@ START_NAMESPACE_DISTRHO */ class ExamplePluginCVPort : public Plugin { - static constexpr const float kMaxHoldTime = 1.0f; - public: ExamplePluginCVPort() : Plugin(1, 0, 0), // 1 parameters, 0 programs, 0 states From 8c9be800be5265a07c526c06713a4e2e4084e61d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 15:42:35 +0100 Subject: [PATCH 139/504] Class names must be different after all; Fix parameter state restore Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 61 ++++++++++++++++++++----------- distrho/src/DistrhoUIVST3.cpp | 30 ++++++++------- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index c2a29718..3e75ed78 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -893,7 +893,7 @@ public: if (fPlugin.getParameterSymbol(j) != key) continue; - const float fvalue = fParameterValues[i] = std::atof(value.buffer()); + const float fvalue = fParameterValues[j] = std::atof(value.buffer()); fPlugin.setParameterValue(j, fvalue); #if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) @@ -2214,7 +2214,7 @@ static V3_API uint32_t dpf_single_instance_unref(void* self) #if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- -// dpf_connection_point +// dpf_dsp_connection_point enum ConnectionPointType { kConnectionPointComponent, @@ -2222,7 +2222,22 @@ enum ConnectionPointType { kConnectionPointBridge }; -struct dpf_connection_point : v3_connection_point_cpp { +static const char* ConnectionPointType2str(const ConnectionPointType type) +{ + switch (type) + { + case kConnectionPointComponent: + return "kConnectionPointComponent"; + case kConnectionPointController: + return "kConnectionPointController"; + case kConnectionPointBridge: + return "kConnectionPointBridge"; + } + + return "[unknown]"; +} + +struct dpf_dsp_connection_point : v3_connection_point_cpp { std::atomic_int refcounter; ScopedPointer& vst3; const ConnectionPointType type; @@ -2230,7 +2245,7 @@ struct dpf_connection_point : v3_connection_point_cpp { v3_connection_point** bridge; // when type is controller this points to ctrl<->view point bool shortcircuit; // plugin as controller, should pass directly to view - dpf_connection_point(const ConnectionPointType t, ScopedPointer& v) + dpf_dsp_connection_point(const ConnectionPointType t, ScopedPointer& v) : refcounter(1), vst3(v), type(t), @@ -2240,8 +2255,8 @@ struct dpf_connection_point : v3_connection_point_cpp { { // v3_funknown, single instance query_interface = query_interface_connection_point; - ref = dpf_single_instance_ref; - unref = dpf_single_instance_unref; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_connection_point point.connect = connect; @@ -2275,9 +2290,10 @@ struct dpf_connection_point : v3_connection_point_cpp { static V3_API v3_result connect(void* self, v3_connection_point** other) { - d_stdout("dpf_connection_point::connect => %p %p", self, other); - dpf_connection_point* const point = *(dpf_connection_point**)self; + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + d_stdout("DSP|dpf_dsp_connection_point::connect => %p %p | %d:%s %d", + self, other, point->type, ConnectionPointType2str(point->type), point->shortcircuit); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT(point->bridge == nullptr); @@ -2292,9 +2308,10 @@ struct dpf_connection_point : v3_connection_point_cpp { static V3_API v3_result disconnect(void* self, v3_connection_point** other) { - d_stdout("dpf_connection_point::disconnect => %p %p", self, other); - dpf_connection_point* const point = *(dpf_connection_point**)self; + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + d_stdout("DSP|dpf_dsp_connection_point::disconnect => %p %p | %d:%s %d", + self, other, point->type, ConnectionPointType2str(point->type), point->shortcircuit); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); if (point->type == kConnectionPointComponent) @@ -2312,7 +2329,7 @@ struct dpf_connection_point : v3_connection_point_cpp { static V3_API v3_result notify(void* self, v3_message** message) { - dpf_connection_point* const point = *(dpf_connection_point**)self; + dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); PluginVst3* const vst3 = point->vst3; @@ -2454,8 +2471,8 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { struct dpf_edit_controller : v3_edit_controller_cpp { std::atomic_int refcounter; #if DISTRHO_PLUGIN_HAS_UI - ScopedPointer connectionComp; // kConnectionPointController - ScopedPointer connectionBridge; // kConnectionPointBridge + ScopedPointer connectionComp; // kConnectionPointController + ScopedPointer connectionBridge; // kConnectionPointBridge #endif ScopedPointer& vst3; bool initialized; @@ -2527,7 +2544,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (v3_tuid_match(iid, v3_connection_point_iid)) { if (controller->connectionComp == nullptr) - controller->connectionComp = new dpf_connection_point(kConnectionPointController, + controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, controller->vst3); else ++controller->connectionComp->refcounter; @@ -2785,7 +2802,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // otherwise short-circuit and deal with this ourselves (assume local usage) else { - controller->connectionComp = new dpf_connection_point(kConnectionPointController, + controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, controller->vst3); controller->connectionComp->shortcircuit = true; } @@ -2814,8 +2831,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } else { - controller->connectionBridge = new dpf_connection_point(kConnectionPointBridge, - controller->vst3); + controller->connectionBridge = new dpf_dsp_connection_point(kConnectionPointBridge, + controller->vst3); v3_connection_point** const bridgeconn = (v3_connection_point**)&controller->connectionBridge; v3_cpp_obj(uiconn)->connect(uiconn, bridgeconn); @@ -3067,7 +3084,7 @@ struct dpf_component : v3_component_cpp { ScopedPointer* self; ScopedPointer processor; #if DISTRHO_PLUGIN_HAS_UI - ScopedPointer connection; // kConnectionPointComponent + ScopedPointer connection; // kConnectionPointComponent #endif ScopedPointer controller; ScopedPointer vst3; @@ -3139,7 +3156,7 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_connection_point_iid)) { if (component->connection == nullptr) - component->connection = new dpf_connection_point(kConnectionPointComponent, + component->connection = new dpf_dsp_connection_point(kConnectionPointComponent, component->vst3); else ++component->connection->refcounter; @@ -3195,7 +3212,7 @@ struct dpf_component : v3_component_cpp { } #if DISTRHO_PLUGIN_HAS_UI - if (dpf_connection_point* const conn = component->connection) + if (dpf_dsp_connection_point* const conn = component->connection) { if (const int refcount = conn->refcounter) { @@ -3214,7 +3231,7 @@ struct dpf_component : v3_component_cpp { } #if DISTRHO_PLUGIN_HAS_UI - if (dpf_connection_point* const comp = ctrl->connectionComp) + if (dpf_dsp_connection_point* const comp = ctrl->connectionComp) { if (const int refcount = comp->refcounter) { @@ -3223,7 +3240,7 @@ struct dpf_component : v3_component_cpp { } } - if (dpf_connection_point* const bridge = ctrl->connectionBridge) + if (dpf_dsp_connection_point* const bridge = ctrl->connectionBridge) { if (const int refcount = bridge->refcounter) { diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index cb735cb7..a9b9b156 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -584,22 +584,22 @@ static V3_API uint32_t dpf_single_instance_unref(void* self) } // -------------------------------------------------------------------------------------------------------------------- -// dpf_connection_point +// dpf_ui_connection_point -struct dpf_connection_point : v3_connection_point_cpp { +struct dpf_ui_connection_point : v3_connection_point_cpp { std::atomic_int refcounter; ScopedPointer& uivst3; v3_connection_point** other; - dpf_connection_point(ScopedPointer& v) + dpf_ui_connection_point(ScopedPointer& v) : refcounter(1), uivst3(v), other(nullptr) { // v3_funknown, single instance query_interface = query_interface_connection_point; - ref = dpf_single_instance_ref; - unref = dpf_single_instance_unref; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_connection_point point.connect = connect; @@ -612,6 +612,8 @@ struct dpf_connection_point : v3_connection_point_cpp { static V3_API v3_result query_interface_connection_point(void* self, const v3_tuid iid, void** iface) { + d_stdout("UI|query_interface_connection_point => %p", self); + if (v3_tuid_match(iid, v3_funknown_iid)) { *iface = self; @@ -633,8 +635,8 @@ struct dpf_connection_point : v3_connection_point_cpp { static V3_API v3_result connect(void* self, v3_connection_point** other) { - d_stdout("dpf_connection_point::connect => %p %p", self, other); - dpf_connection_point* const point = *(dpf_connection_point**)self; + d_stdout("UI|dpf_ui_connection_point::connect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); @@ -648,8 +650,8 @@ struct dpf_connection_point : v3_connection_point_cpp { static V3_API v3_result disconnect(void* self, v3_connection_point** other) { - d_stdout("dpf_connection_point::disconnect => %p %p", self, other); - dpf_connection_point* const point = *(dpf_connection_point**)self; + d_stdout("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); @@ -663,7 +665,7 @@ struct dpf_connection_point : v3_connection_point_cpp { static V3_API v3_result notify(void* self, v3_message** message) { - dpf_connection_point* const point = *(dpf_connection_point**)self; + dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); UIVst3* const uivst3 = point->uivst3; @@ -809,7 +811,7 @@ static const char* const kSupportedPlatforms[] = { struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic_int refcounter; dpf_plugin_view** const self; - ScopedPointer connection; + ScopedPointer connection; ScopedPointer scale; #ifdef DPF_VST3_USING_HOST_RUN_LOOP ScopedPointer timer; @@ -876,7 +878,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (v3_tuid_match(v3_connection_point_iid, iid)) { if (view->connection == nullptr) - view->connection = new dpf_connection_point(view->uivst3); + view->connection = new dpf_ui_connection_point(view->uivst3); else ++view->connection->refcounter; *iface = &view->connection; @@ -920,7 +922,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, (v3_connection_point**)&view->connection); - if (dpf_connection_point* const conn = view->connection) + if (dpf_ui_connection_point* const conn = view->connection) { if (const int refcount = conn->refcounter) { @@ -989,7 +991,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->sampleRate, view->instancePointer); - if (dpf_connection_point* const point = view->connection) + if (dpf_ui_connection_point* const point = view->connection) if (point->other != nullptr) view->uivst3->connect(point->other); From 09f0747211b63e4e42e27169927de32f231c98cf Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 15:43:20 +0100 Subject: [PATCH 140/504] Fix windows build Signed-off-by: falkTX --- distrho/src/travesty/base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 86a2378c..417d3061 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -93,7 +93,7 @@ enum { V3_INVALID_ARG = 0x80070057L, V3_NOT_IMPLEMENTED = 0x80004001L, V3_INTERNAL_ERR = 0x80004005L, - V3_NOT_INITIALISED = 0x8000FFFFL, + V3_NOT_INITIALIZED = 0x8000FFFFL, V3_NOMEM = 0x8007000EL }; From 77dea31f20ab6f0b39b63dcae397fc596c05533d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 16:01:26 +0100 Subject: [PATCH 141/504] Fix win32 build Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 3e75ed78..bb479632 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2889,7 +2889,7 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_process_context_requirements - static uint32_t get_process_context_requirements(void*) + static V3_API uint32_t get_process_context_requirements(void*) { #if DISTRHO_PLUGIN_WANT_TIMEPOS return 0x0 From 542ebcf161c16e8fc6fd922bc7eeb1ece39cd504 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 16:51:55 +0100 Subject: [PATCH 142/504] VST3: unref queried host contexts, fixing memory leaks Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 104 +++++++++++++++++++----------- distrho/src/DistrhoUIVST3.cpp | 4 ++ 2 files changed, 71 insertions(+), 37 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index bb479632..5e1dd439 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -345,7 +345,7 @@ v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instan * VST3 DSP class. * * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things. - * The DSP is created during the "initialise" component event, and destroyed during "terminate". + * The DSP is created during the "initialize" component event, and destroyed during "terminate". * * The low-level VST3 stuff comes after. */ @@ -2478,16 +2478,16 @@ struct dpf_edit_controller : v3_edit_controller_cpp { bool initialized; // cached values v3_component_handler** handler; - v3_host_application** const originalHostContext; - v3_plugin_base::v3_funknown** hostContext; + v3_host_application** const hostContextFromFactory; + v3_host_application** hostContextFromInitialize; dpf_edit_controller(ScopedPointer& v, v3_host_application** const h) : refcounter(1), vst3(v), initialized(false), handler(nullptr), - originalHostContext(h), - hostContext(nullptr) + hostContextFromFactory(h), + hostContextFromInitialize(nullptr) { // v3_funknown, single instance query_interface = query_interface_edit_controller; @@ -2579,8 +2579,14 @@ struct dpf_edit_controller : v3_edit_controller_cpp { const bool initialized = controller->initialized; DISTRHO_SAFE_ASSERT_RETURN(! initialized, V3_INVALID_ARG); + // query for host context + v3_host_application** host = nullptr; + v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + + // save it for later so we can unref it + controller->hostContextFromInitialize = host; + controller->initialized = true; - controller->hostContext = context; return V3_OK; } @@ -2594,7 +2600,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); controller->initialized = false; - controller->hostContext = nullptr; #if DISTRHO_PLUGIN_HAS_UI // take the chance to do some cleanup if possible (we created the bridge, we need to destroy it) @@ -2607,6 +2612,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { controller->connectionComp = nullptr; #endif + if (controller->hostContextFromInitialize != nullptr) + { + v3_cpp_obj_unref(controller->hostContextFromInitialize); + controller->hostContextFromInitialize = nullptr; + } + return V3_OK; } @@ -2783,15 +2794,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); - // we require host context for message creation - v3_host_application** host = nullptr; - - if (controller->hostContext != nullptr) - v3_cpp_obj_query_interface(controller->hostContext, v3_host_application_iid, &host); - - if (host == nullptr) - host = controller->originalHostContext; - + // we require a host context for message creation + v3_host_application** host = controller->hostContextFromInitialize != nullptr + ? controller->hostContextFromInitialize + : controller->hostContextFromFactory; DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); // if there is a component connection, we require it to be active @@ -3088,12 +3094,14 @@ struct dpf_component : v3_component_cpp { #endif ScopedPointer controller; ScopedPointer vst3; - v3_host_application** const hostContext; + v3_host_application** const hostContextFromFactory; + v3_host_application** hostContextFromInitialize; dpf_component(ScopedPointer* const s, v3_host_application** const h) : refcounter(1), self(s), - hostContext(h) + hostContextFromFactory(h), + hostContextFromInitialize(nullptr) { // v3_funknown, everything custom query_interface = query_interface_component; @@ -3116,6 +3124,19 @@ struct dpf_component : v3_component_cpp { comp.get_state = get_state; } + void cleanup() + { + vst3 = nullptr; + processor = nullptr; +#if DISTRHO_PLUGIN_HAS_UI + connection = nullptr; +#endif + controller = nullptr; + + if (hostContextFromFactory != nullptr) + v3_cpp_obj_unref(hostContextFromFactory); + } + // ---------------------------------------------------------------------------------------------------------------- // v3_funknown @@ -3157,7 +3178,7 @@ struct dpf_component : v3_component_cpp { { if (component->connection == nullptr) component->connection = new dpf_dsp_connection_point(kConnectionPointComponent, - component->vst3); + component->vst3); else ++component->connection->refcounter; *iface = &component->connection; @@ -3168,7 +3189,8 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_edit_controller_iid)) { if (component->controller == nullptr) - component->controller = new dpf_edit_controller(component->vst3, component->hostContext); + component->controller = new dpf_edit_controller(component->vst3, + component->hostContextFromFactory); else ++component->controller->refcounter; *iface = &component->controller; @@ -3255,6 +3277,10 @@ struct dpf_component : v3_component_cpp { return handleUncleanComponent(componentptr); d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); + + if (component->hostContextFromFactory != nullptr) + v3_cpp_obj_unref(component->hostContextFromFactory); + *(component->self) = nullptr; delete componentptr; return 0; @@ -3268,11 +3294,19 @@ struct dpf_component : v3_component_cpp { d_stdout("dpf_component::initialize => %p %p", self, context); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + DISTRHO_SAFE_ASSERT_RETURN(component->vst3 == nullptr, V3_INVALID_ARG); - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 == nullptr, V3_INVALID_ARG); + // query for host context + v3_host_application** host = nullptr; + if (context != nullptr) + v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); - d_lastCanRequestParameterValueChanges = true; + // save it for later so we can unref it + component->hostContextFromInitialize = host; + + // provide the factory context to the plugin if this new one is invalid + if (host == nullptr) + host = component->hostContextFromFactory; // default early values if (d_lastBufferSize == 0) @@ -3280,12 +3314,7 @@ struct dpf_component : v3_component_cpp { if (d_lastSampleRate <= 0.0) d_lastSampleRate = 44100.0; - // query for host context - v3_host_application** host = nullptr; - v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); - - if (host == nullptr) - host = component->hostContext; + d_lastCanRequestParameterValueChanges = true; component->vst3 = new PluginVst3(host); return V3_OK; @@ -3296,11 +3325,16 @@ struct dpf_component : v3_component_cpp { d_stdout("dpf_component::terminate => %p", self); dpf_component* const component = *(dpf_component**)self; DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(component->vst3 != nullptr, V3_INVALID_ARG); component->vst3 = nullptr; + + if (component->hostContextFromInitialize != nullptr) + { + v3_cpp_obj_unref(component->hostContextFromInitialize); + component->hostContextFromInitialize = nullptr; + } + return V3_OK; } @@ -3488,11 +3522,7 @@ struct dpf_factory : v3_plugin_factory_cpp { { ScopedPointer* const componentptr = *it; dpf_component* const component = *componentptr; -#if DISTRHO_PLUGIN_HAS_UI - component->connection = nullptr; -#endif - component->processor = nullptr; - component->controller = nullptr; + component->cleanup(); *(component->self) = nullptr; delete componentptr; } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index a9b9b156..c0207976 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -1032,6 +1032,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); + // we query it 2 times in total, so lets unref 2 times as well + v3_cpp_obj_unref(runloop); + v3_cpp_obj_unref(runloop); + if (const int refcount = --view->timer->refcounter) { view->timer->valid = false; From 4e4d49ff70d9b317c23f0997a10519aee9666ec0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 18:02:16 +0100 Subject: [PATCH 143/504] Cleanup Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 3 +++ distrho/src/DistrhoPluginVST3.cpp | 29 +++++++++++++---------------- distrho/src/DistrhoUIVST3.cpp | 14 +++++--------- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 34bfc857..edbdd659 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -634,6 +634,9 @@ struct MidiEvent { /** MIDI data.@n If size > kDataSize, dataExt is used (otherwise null). + + When dataExt is used, the event holder is responsible for + keeping the pointer valid during the entirity of the run function. */ uint8_t data[kDataSize]; const uint8_t* dataExt; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 5e1dd439..5ee5fd26 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -57,7 +57,6 @@ namespace std { #include /* TODO items: - * - base component refcount handling * - parameter enumeration as lists * - hide parameter outputs? * - hide program parameter? @@ -3074,9 +3073,9 @@ struct dpf_audio_processor : v3_audio_processor_cpp { struct dpf_component; -std::vector*> gComponentGarbage; +std::vector gComponentGarbage; -static v3_result handleUncleanComponent(ScopedPointer* const componentptr) +static v3_result handleUncleanComponent(dpf_component** const componentptr) { gComponentGarbage.push_back(componentptr); return V3_INVALID_ARG; @@ -3087,7 +3086,6 @@ static v3_result handleUncleanComponent(ScopedPointer* const comp struct dpf_component : v3_component_cpp { std::atomic_int refcounter; - ScopedPointer* self; ScopedPointer processor; #if DISTRHO_PLUGIN_HAS_UI ScopedPointer connection; // kConnectionPointComponent @@ -3097,9 +3095,8 @@ struct dpf_component : v3_component_cpp { v3_host_application** const hostContextFromFactory; v3_host_application** hostContextFromInitialize; - dpf_component(ScopedPointer* const s, v3_host_application** const h) + dpf_component(v3_host_application** const h) : refcounter(1), - self(s), hostContextFromFactory(h), hostContextFromInitialize(nullptr) { @@ -3208,7 +3205,7 @@ struct dpf_component : v3_component_cpp { static V3_API uint32_t unref_component(void* self) { - ScopedPointer* const componentptr = (ScopedPointer*)self; + dpf_component** const componentptr = (dpf_component**)self; dpf_component* const component = *componentptr; if (const int refcount = --component->refcounter) @@ -3281,7 +3278,7 @@ struct dpf_component : v3_component_cpp { if (component->hostContextFromFactory != nullptr) v3_cpp_obj_unref(component->hostContextFromFactory); - *(component->self) = nullptr; + delete component; delete componentptr; return 0; } @@ -3517,13 +3514,13 @@ struct dpf_factory : v3_plugin_factory_cpp { d_stdout("DPF notice: cleaning up previously undeleted components now"); - for (std::vector*>::iterator it = gComponentGarbage.begin(); + for (std::vector::iterator it = gComponentGarbage.begin(); it != gComponentGarbage.end(); ++it) { - ScopedPointer* const componentptr = *it; + dpf_component** const componentptr = *it; dpf_component* const component = *componentptr; component->cleanup(); - *(component->self) = nullptr; + delete component; delete componentptr; } @@ -3595,8 +3592,8 @@ struct dpf_factory : v3_plugin_factory_cpp { static V3_API v3_result create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) { d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); - DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && - v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); + DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && + v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); dpf_factory* const factory = *(dpf_factory**)self; DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALIZED); @@ -3606,9 +3603,9 @@ struct dpf_factory : v3_plugin_factory_cpp { if (factory->hostContext != nullptr) v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &host); - ScopedPointer* const componentptr = new ScopedPointer; - *componentptr = new dpf_component(componentptr, host); - *instance = componentptr; + dpf_component** const componentptr = new dpf_component*; + *componentptr = new dpf_component(host); + *instance = static_cast(componentptr); return V3_OK; } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index c0207976..6862208e 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -810,7 +810,6 @@ static const char* const kSupportedPlatforms[] = { struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic_int refcounter; - dpf_plugin_view** const self; ScopedPointer connection; ScopedPointer scale; #ifdef DPF_VST3_USING_HOST_RUN_LOOP @@ -823,9 +822,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { double sampleRate; v3_plugin_frame** frame; - dpf_plugin_view(dpf_plugin_view** const s, v3_host_application** const h, void* const instance, const double sr) + dpf_plugin_view(v3_host_application** const h, void* const instance, const double sr) : refcounter(1), - self(s), host(h), instancePointer(instance), sampleRate(sr), @@ -916,8 +914,6 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); - DISTRHO_SAFE_ASSERT_RETURN(viewptr == view->self, V3_INTERNAL_ERR); - if (view->connection != nullptr && view->connection->other) v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, (v3_connection_point**)&view->connection); @@ -984,7 +980,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - view->uivst3 = new UIVst3((v3_plugin_view**)view->self, + view->uivst3 = new UIVst3((v3_plugin_view**)self, view->host, (uintptr_t)parent, scaleFactor, @@ -1107,8 +1103,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->instancePointer, scaleFactor); + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + view->instancePointer, scaleFactor); rect->right = tmpUI.getWidth(); rect->bottom = tmpUI.getHeight(); return V3_OK; @@ -1185,7 +1181,7 @@ v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host, const double sampleRate) { dpf_plugin_view** const viewptr = new dpf_plugin_view*; - *viewptr = new dpf_plugin_view(viewptr, host, instancePointer, sampleRate); + *viewptr = new dpf_plugin_view(host, instancePointer, sampleRate); return (v3_plugin_view**)static_cast(viewptr); } From a7c2278668a9fe825c1a9011137c1fe1648531d3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 20:51:57 +0100 Subject: [PATCH 144/504] VST3: Fix UI related issues Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 11 ++----- distrho/src/DistrhoUIInternal.hpp | 4 +-- distrho/src/DistrhoUIPrivateData.hpp | 12 -------- distrho/src/DistrhoUIVST3.cpp | 45 ++++++++++++++-------------- utils/valgrind-dpf.supp | 7 +++++ 6 files changed, 33 insertions(+), 48 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index edbdd659..e0b7ccb1 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -636,7 +636,7 @@ struct MidiEvent { If size > kDataSize, dataExt is used (otherwise null). When dataExt is used, the event holder is responsible for - keeping the pointer valid during the entirity of the run function. + keeping the pointer valid during the entirety of the run function. */ uint8_t data[kDataSize]; const uint8_t* dataExt; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 5ee5fd26..03d29288 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2022,7 +2022,6 @@ private: v3_cpp_obj(attrlist)->set_float(attrlist, "value", sampleRate); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -2039,7 +2038,6 @@ private: v3_cpp_obj(attrlist)->set_float(attrlist, "value", value); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -2056,7 +2054,6 @@ private: v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value)); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -2071,7 +2068,6 @@ private: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } #endif @@ -2745,8 +2741,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static V3_API double get_parameter_normalised(void* self, v3_param_id index) { - // NOTE very noisy, called many times - // d_stdout("dpf_edit_controller::get_parameter_normalised => %p %u", self, index); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); @@ -2758,7 +2752,6 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static V3_API v3_result set_parameter_normalised(void* self, v3_param_id index, double normalised) { - d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); @@ -2813,8 +2806,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } v3_plugin_view** const view = dpf_plugin_view_create(host, - vst3->getInstancePointer(), - vst3->getSampleRate()); + vst3->getInstancePointer(), + vst3->getSampleRate()); DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr); v3_connection_point** uiconn = nullptr; diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 65c9d93d..64148f83 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -91,7 +91,7 @@ public: g_nextScaleFactor = 0.0; g_nextBundlePath = nullptr; #else - // Leave context called in the PluginWindow constructor, see DistrhoUIPrivateData.hpp + // enter context called in the PluginWindow constructor, see DistrhoUIPrivateData.hpp uiData->window->leaveContext(); #endif UI::PrivateData::s_nextPrivateData = nullptr; @@ -99,8 +99,6 @@ public: DISTRHO_SAFE_ASSERT_RETURN(uiPtr != nullptr,); ui = uiPtr; - uiData->initializing = false; - #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI // unused (void)bundlePath; diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 80894ce0..93a8a89a 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -285,7 +285,6 @@ struct UI::PrivateData { void* dspPtr; // UI - bool initializing; uint bgColor; uint fgColor; double scaleFactor; @@ -309,7 +308,6 @@ struct UI::PrivateData { sampleRate(0), parameterOffset(0), dspPtr(nullptr), - initializing(true), bgColor(0), fgColor(0xffffffff), scaleFactor(1.0), @@ -360,40 +358,30 @@ struct UI::PrivateData { void editParamCallback(const uint32_t rindex, const bool started) { - DISTRHO_SAFE_ASSERT_RETURN(!initializing,); - if (editParamCallbackFunc != nullptr) editParamCallbackFunc(callbacksPtr, rindex, started); } void setParamCallback(const uint32_t rindex, const float value) { - DISTRHO_SAFE_ASSERT_RETURN(!initializing,); - if (setParamCallbackFunc != nullptr) setParamCallbackFunc(callbacksPtr, rindex, value); } void setStateCallback(const char* const key, const char* const value) { - DISTRHO_SAFE_ASSERT_RETURN(!initializing,); - if (setStateCallbackFunc != nullptr) setStateCallbackFunc(callbacksPtr, key, value); } void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) { - DISTRHO_SAFE_ASSERT_RETURN(!initializing,); - if (sendNoteCallbackFunc != nullptr) sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); } void setSizeCallback(const uint width, const uint height) { - DISTRHO_SAFE_ASSERT_RETURN(!initializing,); - if (setSizeCallbackFunc != nullptr) setSizeCallbackFunc(callbacksPtr, width, height); } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 6862208e..999c4e15 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -97,11 +97,19 @@ class UIVst3 public: UIVst3(v3_plugin_view** const view, v3_host_application** const host, + v3_connection_point** const connection, + v3_plugin_frame** const frame, const intptr_t winId, const float scaleFactor, const double sampleRate, void* const instancePointer) - : fUI(this, winId, sampleRate, + : fView(view), + fHostContext(host), + fConnection(connection), + fFrame(frame), + fReadyForPluginData(false), + fScaleFactor(scaleFactor), + fUI(this, winId, sampleRate, editParameterCallback, setParameterCallback, setStateCallback, @@ -110,13 +118,7 @@ public: nullptr, // TODO file request nullptr, // bundlePath instancePointer, - scaleFactor), - fView(view), - fHostContext(host), - fConnection(nullptr), - fFrame(nullptr), - fReadyForPluginData(false), - fScaleFactor(scaleFactor) + scaleFactor) { #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); @@ -132,6 +134,12 @@ public: disconnect(); } + void reconnectIfNeeded() + { + if (fConnection != nullptr) + connect(fConnection); + } + // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view interface calls @@ -218,7 +226,6 @@ public: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -238,7 +245,6 @@ public: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); fConnection = nullptr; @@ -382,9 +388,6 @@ public: // ---------------------------------------------------------------------------------------------------------------- private: - // Plugin UI - UIExporter fUI; - // VST3 stuff v3_plugin_view** const fView; v3_host_application** const fHostContext; @@ -395,6 +398,9 @@ private: bool fReadyForPluginData; float fScaleFactor; + // Plugin UI (after VST3 stuff so the UI can call into us during its constructor) + UIExporter fUI; + // ---------------------------------------------------------------------------------------------------------------- // helper functions called during message passing @@ -426,7 +432,6 @@ private: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -448,7 +453,6 @@ private: v3_cpp_obj(attrlist)->set_int(attrlist, "started", started ? 1 : 0); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -472,7 +476,6 @@ private: v3_cpp_obj(attrlist)->set_float(attrlist, "value", realValue); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -525,7 +528,6 @@ private: v3_cpp_obj(attrlist)->set_binary(attrlist, "data", midiData, sizeof(midiData)); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -551,7 +553,6 @@ private: v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value)); v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj_unref(attrlist); v3_cpp_obj_unref(message); } @@ -982,16 +983,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)self, view->host, + view->connection != nullptr ? view->connection->other : nullptr, + view->frame, (uintptr_t)parent, scaleFactor, view->sampleRate, view->instancePointer); - if (dpf_ui_connection_point* const point = view->connection) - if (point->other != nullptr) - view->uivst3->connect(point->other); - - view->uivst3->setFrame(view->frame); + view->uivst3->reconnectIfNeeded(); #ifdef DPF_VST3_USING_HOST_RUN_LOOP // register a timer host run loop stuff diff --git a/utils/valgrind-dpf.supp b/utils/valgrind-dpf.supp index a7c866f1..a7b8f615 100644 --- a/utils/valgrind-dpf.supp +++ b/utils/valgrind-dpf.supp @@ -39,3 +39,10 @@ fun:XInitThreads ... } +{ + ignore XrmGetStringDatabase + Memcheck:Leak + ... + fun:XrmGetStringDatabase + ... +} From 17f09f33bc046202fd2546a452d9923bf2921596 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 23:19:09 +0100 Subject: [PATCH 145/504] VST3: Set factory categories and flags, add documentation Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 89 +++++++++++++++++++++++++++++++ distrho/src/DistrhoPluginVST3.cpp | 66 ++++++++++++++++------- distrho/src/travesty/factory.h | 9 +++- 3 files changed, 143 insertions(+), 21 deletions(-) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 71eb3cd1..6805f4a1 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -620,6 +620,95 @@ START_NAMESPACE_DISTRHO */ #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" +/** + Custom LV2 category for the plugin.@n + This can be one of the following values: + + - lv2:Plugin + - lv2:AllpassPlugin + - lv2:AmplifierPlugin + - lv2:AnalyserPlugin + - lv2:BandpassPlugin + - lv2:ChorusPlugin + - lv2:CombPlugin + - lv2:CompressorPlugin + - lv2:ConstantPlugin + - lv2:ConverterPlugin + - lv2:DelayPlugin + - lv2:DistortionPlugin + - lv2:DynamicsPlugin + - lv2:EQPlugin + - lv2:EnvelopePlugin + - lv2:ExpanderPlugin + - lv2:FilterPlugin + - lv2:FlangerPlugin + - lv2:FunctionPlugin + - lv2:GatePlugin + - lv2:GeneratorPlugin + - lv2:HighpassPlugin + - lv2:InstrumentPlugin + - lv2:LimiterPlugin + - lv2:LowpassPlugin + - lv2:MIDIPlugin + - lv2:MixerPlugin + - lv2:ModulatorPlugin + - lv2:MultiEQPlugin + - lv2:OscillatorPlugin + - lv2:ParaEQPlugin + - lv2:PhaserPlugin + - lv2:PitchPlugin + - lv2:ReverbPlugin + - lv2:SimulatorPlugin + - lv2:SpatialPlugin + - lv2:SpectralPlugin + - lv2:UtilityPlugin + - lv2:WaveshaperPlugin + + See http://lv2plug.in/ns/lv2core for more information. + */ +#define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:Plugin" + +/** + Custom VST3 categories for the plugin.@n + This is a list of categories, separated by a @c |. + + Each effect category can be one of the following values: + + - Fx + - Fx|Ambisonics + - Fx|Analyzer + - Fx|Delay + - Fx|Distortion + - Fx|Dynamics + - Fx|EQ + - Fx|Filter + - Fx|Instrument + - Fx|Instrument|External + - Fx|Spatial + - Fx|Generator + - Fx|Mastering + - Fx|Modulation + - Fx|Network + - Fx|Pitch Shift + - Fx|Restoration + - Fx|Reverb + - Fx|Surround + - Fx|Tools + + Each instrument category can be one of the following values: + + - Instrument + - Instrument|Drum + - Instrument|External + - Instrument|Piano + - Instrument|Sampler + - Instrument|Synth + - Instrument|Synth|Sampler + + @note DPF will automatically set Mono and Stereo categories when appropriate. + */ +#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx" + /** @} */ /* ------------------------------------------------------------------------------------------------------------ diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 03d29288..cbd7e735 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -70,8 +70,6 @@ namespace std { * - routing info, do we care? * - set sidechain bus name from port group * - implement getParameterValueForString (use names from enumeration if available, fallback to std::atof) - * - set factory class_flags - * - set factory sub_categories * - set factory email (needs new DPF API, useful for LV2 as well) * - do something with get_controller_class_id and set_io_mode? */ @@ -3468,6 +3466,33 @@ static const PluginExporter& getPluginInfo() return info; } +static const char* getPluginCategories() +{ + static String categories; + static bool firstInit = true; + + if (firstInit) + { +#ifdef DISTRHO_PLUGIN_VST3_CATEGORIES + categories = DISTRHO_PLUGIN_VST3_CATEGORIES; +#elif DISTRHO_PLUGIN_IS_SYNTH + categories = "Instrument"; +#endif +#if (DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_INPUTS == 1) && DISTRHO_PLUGIN_NUM_OUTPUTS == 1 + if (categories.isNotEmpty()) + categories += "|"; + categories += "Mono"; +#elif (DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_INPUTS == 2) && DISTRHO_PLUGIN_NUM_OUTPUTS == 2 + if (categories.isNotEmpty()) + categories += "|"; + categories += "Stereo"; +#endif + firstInit = false; + } + + return categories.buffer(); +} + // -------------------------------------------------------------------------------------------------------------------- // dpf_factory @@ -3559,9 +3584,10 @@ struct dpf_factory : v3_plugin_factory_cpp { static V3_API v3_result get_factory_info(void*, v3_factory_info* const info) { std::memset(info, 0, sizeof(*info)); - DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), sizeof(info->vendor)); - DISTRHO_NAMESPACE::strncpy(info->url, getPluginInfo().getHomePage(), sizeof(info->url)); - // DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO + DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); + DISTRHO_NAMESPACE::strncpy(info->url, getPluginInfo().getHomePage(), ARRAY_SIZE(info->url)); + // DISTRHO_NAMESPACE::strncpy(info->email, "", ARRAY_SIZE(info->email)); // TODO + info->flags = 0x10; return V3_OK; } @@ -3573,18 +3599,18 @@ struct dpf_factory : v3_plugin_factory_cpp { static V3_API v3_result get_class_info(void*, int32_t idx, v3_class_info* const info) { std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); - DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), sizeof(info->name)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); return V3_OK; } static V3_API v3_result create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) { - d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); + d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); @@ -3608,7 +3634,7 @@ struct dpf_factory : v3_plugin_factory_cpp { static V3_API v3_result get_class_info_2(void*, int32_t idx, v3_class_info_2* info) { std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; @@ -3616,10 +3642,10 @@ struct dpf_factory : v3_plugin_factory_cpp { DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); info->class_flags = 0; - // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO + DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::snprintf_u32(info->version, getPluginInfo().getVersion(), ARRAY_SIZE(info->version)); // FIXME - DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? + DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); return V3_OK; } @@ -3629,27 +3655,29 @@ struct dpf_factory : v3_plugin_factory_cpp { static V3_API v3_result get_class_info_utf16(void*, int32_t idx, v3_class_info_3* info) { std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); + DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS info->class_flags = 0; - // DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", ARRAY_SIZE(info->sub_categories)); // TODO +#else + info->class_flags = V3_DISTRIBUTABLE; +#endif + DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), sizeof(info->vendor)); DISTRHO_NAMESPACE::snprintf_u32_utf16(info->version, getPluginInfo().getVersion(), ARRAY_SIZE(info->version)); // FIXME - DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); // TESTING use "VST 3.7" ? + DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); return V3_OK; } static V3_API v3_result set_host_context(void* self, v3_funknown** context) { - d_stdout("dpf_factory::set_host_context => %p %p", self, context); - dpf_factory* const factory = *(dpf_factory**)self; - DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALIZED); - + d_stdout("dpf_factory::set_host_context => %p %p", self, context); + dpf_factory* const factory = *static_cast(self); factory->hostContext = context; return V3_OK; } diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index 39629750..adad83cd 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -26,12 +26,12 @@ struct v3_factory_info { char vendor[64]; char url[256]; char email[128]; - int32_t flags; + int32_t flags; // set to 0x10 (unicode) }; struct v3_class_info { v3_tuid class_id; - int32_t cardinality; // set to 0x7FFFFFFF + int32_t cardinality; // set to 0x7FFFFFFF (many instances) char category[32]; char name[64]; }; @@ -52,6 +52,11 @@ static constexpr const v3_tuid v3_plugin_factory_iid = * plugin factory v2 */ +enum { + V3_DISTRIBUTABLE = 1 << 0, + V3_SIMPLE_MODE = 1 << 1 +}; + struct v3_class_info_2 { v3_tuid class_id; int32_t cardinality; // set to 0x7FFFFFFF From c12d9def50ce2bfd4a2bab6c0eb4c8661f82ffe4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 23:31:31 +0100 Subject: [PATCH 146/504] VST3: Correctly set plugin version information Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 38 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index cbd7e735..614c7584 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -61,10 +61,10 @@ namespace std { * - hide parameter outputs? * - hide program parameter? * - deal with parameter triggers + * - MIDI CC changes (need to store value to give to the host?) * - MIDI program changes * - MIDI sysex * - append MIDI input events in a sorted way - * - decode version number (0x010203 -> 1.2.3) * - bus arrangements * - optional audio buses, create dummy buffer of max_block_size length for them * - routing info, do we care? @@ -294,12 +294,6 @@ void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size return snprintf_utf16_t(dst, value, "%f", size); } -static inline -void snprintf_u32_utf16(int16_t* const dst, const uint32_t value, const size_t size) -{ - return snprintf_utf16_t(dst, value, "%u", size); -} - // -------------------------------------------------------------------------------------------------------------------- // handy way to create a utf16 string on the current function scope, used for message strings @@ -3493,6 +3487,26 @@ static const char* getPluginCategories() return categories.buffer(); } +static const char* getPluginVersion() +{ + static String version; + + if (version.isEmpty()) + { + const uint32_t versionNum = getPluginInfo().getVersion(); + + char versionBuf[64]; + snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d", + (versionNum >> 16) & 0xff, + (versionNum >> 8) & 0xff, + (versionNum >> 0) & 0xff); + versionBuf[sizeof(versionBuf)-1] = '\0'; + version = versionBuf; + } + + return version.buffer(); +} + // -------------------------------------------------------------------------------------------------------------------- // dpf_factory @@ -3641,10 +3655,14 @@ struct dpf_factory : v3_plugin_factory_cpp { DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS info->class_flags = 0; +#else + info->class_flags = V3_DISTRIBUTABLE; +#endif DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); - DISTRHO_NAMESPACE::snprintf_u32(info->version, getPluginInfo().getVersion(), ARRAY_SIZE(info->version)); // FIXME + DISTRHO_NAMESPACE::strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); return V3_OK; } @@ -3668,8 +3686,8 @@ struct dpf_factory : v3_plugin_factory_cpp { info->class_flags = V3_DISTRIBUTABLE; #endif DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); - DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), sizeof(info->vendor)); - DISTRHO_NAMESPACE::snprintf_u32_utf16(info->version, getPluginInfo().getVersion(), ARRAY_SIZE(info->version)); // FIXME + DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); + DISTRHO_NAMESPACE::strncpy_utf16(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); return V3_OK; } From 253edfc88a3a2c8ca4e68606af9eceb052faaef0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 23:39:05 +0100 Subject: [PATCH 147/504] Enable VST3 build on MSVC, remove C++98 workaround Signed-off-by: falkTX --- .github/workflows/makefile.yml | 2 +- cmake/DPF-plugin.cmake | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index 9bacad75..f453d10d 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -40,7 +40,7 @@ jobs: CXXFLAGS: -Werror -std=gnu++98 run: | make clean >/dev/null - make -j $(nproc) VST3_FILENAME= + make -j $(nproc) - name: No namespace env: CFLAGS: -Werror diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 8f458fa3..a0b316b0 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -154,11 +154,7 @@ function(dpf_add_plugin NAME) elseif(_target STREQUAL "vst2") dpf__build_vst2("${NAME}" "${_dgl_library}") elseif(_target STREQUAL "vst3") - if(MSVC) - message(WARNING "TODO: VST3 is not supported on MSVC yet") - else() - dpf__build_vst3("${NAME}" "${_dgl_library}") - endif() + dpf__build_vst3("${NAME}" "${_dgl_library}") else() message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}") endif() From cd9434e3d03349f8db45d77938d5095ff61a88a1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Oct 2021 23:49:02 +0100 Subject: [PATCH 148/504] VST3: Move V3_API to the other side Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 118 +++++++++++++++--------------- distrho/src/DistrhoUIVST3.cpp | 50 ++++++------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 614c7584..f8b3de2c 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2181,20 +2181,20 @@ private: // -------------------------------------------------------------------------------------------------------------------- // v3_funknown for static instances -static V3_API uint32_t dpf_static_ref(void*) { return 1; } -static V3_API uint32_t dpf_static_unref(void*) { return 0; } +static uint32_t V3_API dpf_static_ref(void*) { return 1; } +static uint32_t V3_API dpf_static_unref(void*) { return 0; } // -------------------------------------------------------------------------------------------------------------------- // v3_funknown for classes with a single instance template -static V3_API uint32_t dpf_single_instance_ref(void* self) +static uint32_t V3_API dpf_single_instance_ref(void* self) { return ++(*(T**)self)->refcounter; } template -static V3_API uint32_t dpf_single_instance_unref(void* self) +static uint32_t V3_API dpf_single_instance_unref(void* self) { return --(*(T**)self)->refcounter; } @@ -2254,7 +2254,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_connection_point(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_connection_point(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -2275,7 +2275,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point - static V3_API v3_result connect(void* self, v3_connection_point** other) + static v3_result V3_API connect(void* self, v3_connection_point** other) { dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); @@ -2293,7 +2293,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { return V3_OK; } - static V3_API v3_result disconnect(void* self, v3_connection_point** other) + static v3_result V3_API disconnect(void* self, v3_connection_point** other) { dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); @@ -2314,7 +2314,7 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { return V3_OK; } - static V3_API v3_result notify(void* self, v3_message** message) + static v3_result V3_API notify(void* self, v3_message** message) { dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); @@ -2411,7 +2411,7 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_midi_mapping(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_midi_mapping(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -2432,7 +2432,7 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_midi_mapping - static V3_API v3_result get_midi_controller_assignment(void*, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id) + static v3_result V3_API get_midi_controller_assignment(void*, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id) { DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE); DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE); @@ -2504,7 +2504,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_edit_controller(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_edit_controller(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -2557,7 +2557,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_base - static V3_API v3_result initialize(void* self, v3_plugin_base::v3_funknown** context) + static v3_result V3_API initialize(void* self, v3_plugin_base::v3_funknown** context) { d_stdout("dpf_edit_controller::initialize => %p %p", self, context); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2577,7 +2577,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_OK; } - static V3_API v3_result terminate(void* self) + static v3_result V3_API terminate(void* self) { d_stdout("dpf_edit_controller::terminate => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2611,7 +2611,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_edit_controller - static V3_API v3_result set_component_state(void* self, v3_bstream* stream) + static v3_result V3_API set_component_state(void* self, v3_bstream* stream) { d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2628,7 +2628,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_OK; } - static V3_API v3_result set_state(void* self, v3_bstream* stream) + static v3_result V3_API set_state(void* self, v3_bstream* stream) { d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2643,7 +2643,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NOT_IMPLEMENTED; } - static V3_API v3_result get_state(void* self, v3_bstream* stream) + static v3_result V3_API get_state(void* self, v3_bstream* stream) { d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2658,7 +2658,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NOT_IMPLEMENTED; } - static V3_API int32_t get_parameter_count(void* self) + static int32_t V3_API get_parameter_count(void* self) { // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2670,7 +2670,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterCount(); } - static V3_API v3_result get_parameter_info(void* self, int32_t param_idx, v3_param_info* param_info) + static v3_result V3_API get_parameter_info(void* self, int32_t param_idx, v3_param_info* param_info) { // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2682,7 +2682,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterInfo(param_idx, param_info); } - static V3_API v3_result get_parameter_string_for_value(void* self, v3_param_id index, double normalised, v3_str_128 output) + static v3_result V3_API get_parameter_string_for_value(void* self, v3_param_id index, double normalised, v3_str_128 output) { // NOTE very noisy, called many times // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); @@ -2695,7 +2695,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterStringForValue(index, normalised, output); } - static V3_API v3_result get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output) + static v3_result V3_API get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output) { d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2707,7 +2707,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterValueForString(index, input, output); } - static V3_API double normalised_parameter_to_plain(void* self, v3_param_id index, double normalised) + static double V3_API normalised_parameter_to_plain(void* self, v3_param_id index, double normalised) { d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2719,7 +2719,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->normalisedParameterToPlain(index, normalised); } - static V3_API double plain_parameter_to_normalised(void* self, v3_param_id index, double plain) + static double V3_API plain_parameter_to_normalised(void* self, v3_param_id index, double plain) { d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2731,7 +2731,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->plainParameterToNormalised(index, plain); } - static V3_API double get_parameter_normalised(void* self, v3_param_id index) + static double V3_API get_parameter_normalised(void* self, v3_param_id index) { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); @@ -2742,7 +2742,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterNormalized(index); } - static V3_API v3_result set_parameter_normalised(void* self, v3_param_id index, double normalised) + static v3_result V3_API set_parameter_normalised(void* self, v3_param_id index, double normalised) { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); @@ -2753,7 +2753,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->setParameterNormalized(index, normalised); } - static V3_API v3_result set_component_handler(void* self, v3_component_handler** handler) + static v3_result V3_API set_component_handler(void* self, v3_component_handler** handler) { d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2767,7 +2767,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NOT_INITIALIZED; } - static V3_API v3_plugin_view** create_view(void* self, const char* name) + static v3_plugin_view** V3_API create_view(void* self, const char* name) { d_stdout("dpf_edit_controller::create_view => %p %s", self, name); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; @@ -2858,7 +2858,7 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_process_context_requirements(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_process_context_requirements(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -2879,7 +2879,7 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_process_context_requirements - static V3_API uint32_t get_process_context_requirements(void*) + static uint32_t V3_API get_process_context_requirements(void*) { #if DISTRHO_PLUGIN_WANT_TIMEPOS return 0x0 @@ -2926,7 +2926,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_audio_processor(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_audio_processor(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -2955,7 +2955,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_audio_processor - static V3_API v3_result set_bus_arrangements(void* self, + static v3_result V3_API set_bus_arrangements(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs) { @@ -2970,7 +2970,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->setBusArrangements(inputs, num_inputs, outputs, num_outputs); } - static V3_API v3_result get_bus_arrangement(void* self, int32_t bus_direction, + static v3_result V3_API get_bus_arrangement(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement* arr) { d_stdout("dpf_audio_processor::get_bus_arrangement => %p %i %i %p", self, bus_direction, idx, arr); @@ -2983,13 +2983,13 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->getBusArrangement(bus_direction, idx, arr); } - static V3_API v3_result can_process_sample_size(void* self, int32_t symbolic_sample_size) + static v3_result V3_API can_process_sample_size(void* self, int32_t symbolic_sample_size) { d_stdout("dpf_audio_processor::can_process_sample_size => %p %i", self, symbolic_sample_size); return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED; } - static V3_API uint32_t get_latency_samples(void* self) + static uint32_t V3_API get_latency_samples(void* self) { d_stdout("dpf_audio_processor::get_latency_samples => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; @@ -3001,7 +3001,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->getLatencySamples(); } - static V3_API v3_result setup_processing(void* self, v3_process_setup* setup) + static v3_result V3_API setup_processing(void* self, v3_process_setup* setup) { d_stdout("dpf_audio_processor::setup_processing => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; @@ -3015,7 +3015,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->setupProcessing(setup); } - static V3_API v3_result set_processing(void* self, v3_bool state) + static v3_result V3_API set_processing(void* self, v3_bool state) { d_stdout("dpf_audio_processor::set_processing => %p %u", self, state); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; @@ -3027,7 +3027,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->setProcessing(state); } - static V3_API v3_result process(void* self, v3_process_data* data) + static v3_result V3_API process(void* self, v3_process_data* data) { // NOTE runs during RT // d_stdout("dpf_audio_processor::process => %p", self); @@ -3040,7 +3040,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->process(data); } - static V3_API uint32_t get_tail_samples(void* self) + static uint32_t V3_API get_tail_samples(void* self) { d_stdout("dpf_audio_processor::get_tail_samples => %p", self); dpf_audio_processor* const processor = *(dpf_audio_processor**)self; @@ -3122,7 +3122,7 @@ struct dpf_component : v3_component_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_component(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_component(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -3183,12 +3183,12 @@ struct dpf_component : v3_component_cpp { return V3_NO_INTERFACE; } - static V3_API uint32_t ref_component(void* self) + static uint32_t V3_API ref_component(void* self) { return ++(*(dpf_component**)self)->refcounter; } - static V3_API uint32_t unref_component(void* self) + static uint32_t V3_API unref_component(void* self) { dpf_component** const componentptr = (dpf_component**)self; dpf_component* const component = *componentptr; @@ -3271,7 +3271,7 @@ struct dpf_component : v3_component_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_base - static V3_API v3_result initialize(void* self, v3_plugin_base::v3_funknown** context) + static v3_result V3_API initialize(void* self, v3_plugin_base::v3_funknown** context) { d_stdout("dpf_component::initialize => %p %p", self, context); dpf_component* const component = *(dpf_component**)self; @@ -3302,7 +3302,7 @@ struct dpf_component : v3_component_cpp { return V3_OK; } - static V3_API v3_result terminate(void* self) + static v3_result V3_API terminate(void* self) { d_stdout("dpf_component::terminate => %p", self); dpf_component* const component = *(dpf_component**)self; @@ -3323,7 +3323,7 @@ struct dpf_component : v3_component_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_component - static V3_API v3_result get_controller_class_id(void* self, v3_tuid class_id) + static v3_result V3_API get_controller_class_id(void* self, v3_tuid class_id) { d_stdout("dpf_component::get_controller_class_id => %p %s", self, tuid2str(class_id)); dpf_component* const component = *(dpf_component**)self; @@ -3336,7 +3336,7 @@ struct dpf_component : v3_component_cpp { return V3_NOT_IMPLEMENTED; } - static V3_API v3_result set_io_mode(void* self, int32_t io_mode) + static v3_result V3_API set_io_mode(void* self, int32_t io_mode) { d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode); dpf_component* const component = *(dpf_component**)self; @@ -3349,7 +3349,7 @@ struct dpf_component : v3_component_cpp { return V3_NOT_IMPLEMENTED; } - static V3_API int32_t get_bus_count(void* self, int32_t media_type, int32_t bus_direction) + static int32_t V3_API get_bus_count(void* self, int32_t media_type, int32_t bus_direction) { // NOTE runs during RT // d_stdout("dpf_component::get_bus_count => %p %i %i", self, media_type, bus_direction); @@ -3362,7 +3362,7 @@ struct dpf_component : v3_component_cpp { return vst3->getBusCount(media_type, bus_direction); } - static V3_API v3_result get_bus_info(void* self, int32_t media_type, int32_t bus_direction, + static v3_result V3_API get_bus_info(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bus_info* info) { d_stdout("dpf_component::get_bus_info => %p %i %i %i %p", self, media_type, bus_direction, bus_idx, info); @@ -3375,7 +3375,7 @@ struct dpf_component : v3_component_cpp { return vst3->getBusInfo(media_type, bus_direction, bus_idx, info); } - static V3_API v3_result get_routing_info(void* self, v3_routing_info* input, v3_routing_info* output) + static v3_result V3_API get_routing_info(void* self, v3_routing_info* input, v3_routing_info* output) { d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output); dpf_component* const component = *(dpf_component**)self; @@ -3387,7 +3387,7 @@ struct dpf_component : v3_component_cpp { return vst3->getRoutingInfo(input, output); } - static V3_API v3_result activate_bus(void* self, int32_t media_type, int32_t bus_direction, + static v3_result V3_API activate_bus(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bool state) { // NOTE this is called a bunch of times @@ -3401,7 +3401,7 @@ struct dpf_component : v3_component_cpp { return vst3->activateBus(media_type, bus_direction, bus_idx, state); } - static V3_API v3_result set_active(void* self, v3_bool state) + static v3_result V3_API set_active(void* self, v3_bool state) { d_stdout("dpf_component::set_active => %p %u", self, state); dpf_component* const component = *(dpf_component**)self; @@ -3413,7 +3413,7 @@ struct dpf_component : v3_component_cpp { return component->vst3->setActive(state); } - static V3_API v3_result set_state(void* self, v3_bstream** stream) + static v3_result V3_API set_state(void* self, v3_bstream** stream) { d_stdout("dpf_component::set_state => %p", self); dpf_component* const component = *(dpf_component**)self; @@ -3425,7 +3425,7 @@ struct dpf_component : v3_component_cpp { return vst3->setState(stream); } - static V3_API v3_result get_state(void* self, v3_bstream** stream) + static v3_result V3_API get_state(void* self, v3_bstream** stream) { d_stdout("dpf_component::get_state => %p %p", self, stream); dpf_component* const component = *(dpf_component**)self; @@ -3562,7 +3562,7 @@ struct dpf_factory : v3_plugin_factory_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_factory(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_factory(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -3595,7 +3595,7 @@ struct dpf_factory : v3_plugin_factory_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_factory - static V3_API v3_result get_factory_info(void*, v3_factory_info* const info) + static v3_result V3_API get_factory_info(void*, v3_factory_info* const info) { std::memset(info, 0, sizeof(*info)); DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); @@ -3605,12 +3605,12 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; } - static V3_API int32_t num_classes(void*) + static int32_t V3_API num_classes(void*) { return 1; } - static V3_API v3_result get_class_info(void*, int32_t idx, v3_class_info* const info) + static v3_result V3_API get_class_info(void*, int32_t idx, v3_class_info* const info) { std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); @@ -3622,7 +3622,7 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; } - static V3_API v3_result create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) + static v3_result V3_API create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) { d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && @@ -3645,7 +3645,7 @@ struct dpf_factory : v3_plugin_factory_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_factory_2 - static V3_API v3_result get_class_info_2(void*, int32_t idx, v3_class_info_2* info) + static v3_result V3_API get_class_info_2(void*, int32_t idx, v3_class_info_2* info) { std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); @@ -3670,7 +3670,7 @@ struct dpf_factory : v3_plugin_factory_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_3 - static V3_API v3_result get_class_info_utf16(void*, int32_t idx, v3_class_info_3* info) + static v3_result V3_API get_class_info_utf16(void*, int32_t idx, v3_class_info_3* info) { std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); @@ -3692,7 +3692,7 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; } - static V3_API v3_result set_host_context(void* self, v3_funknown** context) + static v3_result V3_API set_host_context(void* self, v3_funknown** context) { d_stdout("dpf_factory::set_host_context => %p %p", self, context); dpf_factory* const factory = *static_cast(self); diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 999c4e15..0bd6ce0c 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -573,13 +573,13 @@ private: // v3_funknown for classes with a single instance template -static V3_API uint32_t dpf_single_instance_ref(void* self) +static uint32_t V3_API dpf_single_instance_ref(void* self) { return ++(*(T**)self)->refcounter; } template -static V3_API uint32_t dpf_single_instance_unref(void* self) +static uint32_t V3_API dpf_single_instance_unref(void* self) { return --(*(T**)self)->refcounter; } @@ -611,7 +611,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_connection_point(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_connection_point(void* self, const v3_tuid iid, void** iface) { d_stdout("UI|query_interface_connection_point => %p", self); @@ -634,7 +634,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point - static V3_API v3_result connect(void* self, v3_connection_point** other) + static v3_result V3_API connect(void* self, v3_connection_point** other) { d_stdout("UI|dpf_ui_connection_point::connect => %p %p", self, other); dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; @@ -649,7 +649,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { return V3_OK; }; - static V3_API v3_result disconnect(void* self, v3_connection_point** other) + static v3_result V3_API disconnect(void* self, v3_connection_point** other) { d_stdout("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; @@ -664,7 +664,7 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { return V3_OK; }; - static V3_API v3_result notify(void* self, v3_message** message) + static v3_result V3_API notify(void* self, v3_message** message) { dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); @@ -702,7 +702,7 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_view_content_scale(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_view_content_scale(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -723,7 +723,7 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view_content_scale - static V3_API v3_result set_content_scale_factor(void* self, float factor) + static v3_result V3_API set_content_scale_factor(void* self, float factor) { d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); dpf_plugin_view_content_scale* const scale = *(dpf_plugin_view_content_scale**)self; @@ -764,7 +764,7 @@ struct dpf_timer_handler : v3_timer_handler_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_timer_handler(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_timer_handler(void* self, const v3_tuid iid, void** iface) { if (v3_tuid_match(iid, v3_funknown_iid)) { @@ -785,7 +785,7 @@ struct dpf_timer_handler : v3_timer_handler_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_timer_handler - static V3_API void on_timer(void* self) + static void V3_API on_timer(void* self) { dpf_timer_handler* const handler = *(dpf_timer_handler**)self; DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); @@ -853,7 +853,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static V3_API v3_result query_interface_view(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_view(void* self, const v3_tuid iid, void** iface) { d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); *iface = NULL; @@ -897,12 +897,12 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_NO_INTERFACE; } - static V3_API uint32_t ref_view(void* self) + static uint32_t V3_API ref_view(void* self) { return ++(*(dpf_plugin_view**)self)->refcounter; } - static V3_API uint32_t unref_view(void* self) + static uint32_t V3_API unref_view(void* self) { dpf_plugin_view** const viewptr = (dpf_plugin_view**)self; dpf_plugin_view* const view = *viewptr; @@ -945,7 +945,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view - static V3_API v3_result is_platform_type_supported(void* self, const char* platform_type) + static v3_result V3_API is_platform_type_supported(void* self, const char* platform_type) { d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -960,7 +960,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_NOT_IMPLEMENTED; } - static V3_API v3_result attached(void* self, void* parent, const char* platform_type) + static v3_result V3_API attached(void* self, void* parent, const char* platform_type) { d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1007,7 +1007,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_NOT_IMPLEMENTED; } - static V3_API v3_result removed(void* self) + static v3_result V3_API removed(void* self) { d_stdout("dpf_plugin_view::removed => %p", self); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1053,7 +1053,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; } - static V3_API v3_result on_wheel(void* self, float distance) + static v3_result V3_API on_wheel(void* self, float distance) { d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1065,7 +1065,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->onWheel(distance); } - static V3_API v3_result on_key_down(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) + static v3_result V3_API on_key_down(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) { d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1077,7 +1077,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->onKeyDown(key_char, key_code, modifiers); } - static V3_API v3_result on_key_up(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) + static v3_result V3_API on_key_up(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) { d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1089,7 +1089,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->onKeyUp(key_char, key_code, modifiers); } - static V3_API v3_result get_size(void* self, v3_view_rect* rect) + static v3_result V3_API get_size(void* self, v3_view_rect* rect) { d_stdout("dpf_plugin_view::get_size => %p", self); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1109,7 +1109,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; } - static V3_API v3_result on_size(void* self, v3_view_rect* rect) + static v3_result V3_API on_size(void* self, v3_view_rect* rect) { d_stdout("dpf_plugin_view::on_size => %p %p", self, rect); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1121,7 +1121,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->onSize(rect); } - static V3_API v3_result on_focus(void* self, v3_bool state) + static v3_result V3_API on_focus(void* self, v3_bool state) { d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1133,7 +1133,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->onFocus(state); } - static V3_API v3_result set_frame(void* self, v3_plugin_frame** frame) + static v3_result V3_API set_frame(void* self, v3_plugin_frame** frame) { d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame); dpf_plugin_view* const view = *(dpf_plugin_view**)self; @@ -1147,7 +1147,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_NOT_INITIALIZED; } - static V3_API v3_result can_resize(void* self) + static v3_result V3_API can_resize(void* self) { d_stdout("dpf_plugin_view::can_resize => %p", self); // #if DISTRHO_UI_USER_RESIZABLE @@ -1157,7 +1157,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // #endif } - static V3_API v3_result check_size_constraint(void* self, v3_view_rect* rect) + static v3_result V3_API check_size_constraint(void* self, v3_view_rect* rect) { d_stdout("dpf_plugin_view::check_size_constraint => %p %p", self, rect); dpf_plugin_view* const view = *(dpf_plugin_view**)self; From 6fd0b5b70ef0c40478950c33ccf25daf4a7ca3e8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 21:25:27 +0100 Subject: [PATCH 149/504] Keep host context from factory alive as needed Fixes #334 Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index f8b3de2c..4cbc5d14 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -3085,6 +3085,10 @@ struct dpf_component : v3_component_cpp { hostContextFromFactory(h), hostContextFromInitialize(nullptr) { + // make sure context is valid through this component lifetime + if (hostContextFromFactory != nullptr) + v3_cpp_obj_ref(hostContextFromFactory); + // v3_funknown, everything custom query_interface = query_interface_component; ref = ref_component; @@ -3541,6 +3545,10 @@ struct dpf_factory : v3_plugin_factory_cpp { ~dpf_factory() { + // unref old context if there is one + if (hostContext != nullptr) + v3_cpp_obj_unref(hostContext); + if (gComponentGarbage.size() == 0) return; @@ -3696,7 +3704,18 @@ struct dpf_factory : v3_plugin_factory_cpp { { d_stdout("dpf_factory::set_host_context => %p %p", self, context); dpf_factory* const factory = *static_cast(self); + + // unref old context if there is one + if (factory->hostContext != nullptr) + v3_cpp_obj_unref(factory->hostContext); + + // store new context factory->hostContext = context; + + // make sure the object keeps being valid for a while + if (context != nullptr) + v3_cpp_obj_ref(context); + return V3_OK; } From 97068d3014545c48fd02d2585515b964963d9ae3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 22:00:15 +0100 Subject: [PATCH 150/504] VST3: Fix missing 1st parameter when MIDI input was enabled Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 4cbc5d14..7993163a 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1402,11 +1402,11 @@ public: v3_result getParameterInfo(int32_t rindex, v3_param_info* const info) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INVALID_ARG); + std::memset(info, 0, sizeof(v3_param_info)); #if DISTRHO_PLUGIN_WANT_PROGRAMS if (rindex == 0) { - std::memset(info, 0, sizeof(v3_param_info)); info->param_id = rindex; info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_LIST | V3_PARAM_PROGRAM_CHANGE; info->step_count = fProgramCountMinusOne; @@ -1417,10 +1417,12 @@ public: --rindex; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) { - std::memset(info, 0, sizeof(v3_param_info)); info->param_id = rindex; +# if DISTRHO_PLUGIN_WANT_PROGRAMS + ++info->param_id; +# endif info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN; info->step_count = 127; char ccstr[24]; @@ -1465,7 +1467,6 @@ public: if ((hints & kParameterIsInteger) && ranges.max - ranges.min > 1) step_count = ranges.max - ranges.min - 1; - std::memset(info, 0, sizeof(v3_param_info)); info->param_id = index + fParameterOffset; info->flags = flags; info->step_count = step_count; @@ -1493,7 +1494,7 @@ public: --rindex; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) { snprintf_f32_utf16(output, std::round(normalised * 127), 128); return V3_OK; @@ -1519,7 +1520,7 @@ public: --rindex; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) { // TODO return V3_NOT_IMPLEMENTED; @@ -1542,7 +1543,7 @@ public: --rindex; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) return std::round(normalised * 127); rindex -= 130*16; #endif @@ -1561,7 +1562,7 @@ public: --rindex; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) return std::max(0.0, std::min(1.0, plain / 127)); rindex -= 130*16; #endif @@ -1580,7 +1581,7 @@ public: --rindex; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) { // TODO return 0.0; @@ -1625,7 +1626,7 @@ public: --rindex; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) { // TODO fChangedParameterValues[rindex] = true; @@ -1775,7 +1776,7 @@ public: --rindex; # endif # if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex <= 130*16) + if (rindex < 130*16) continue; rindex -= 130*16; # endif From 87c2461d5b4ba1f77ac9b5db461b070793d4ba7d Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 22:19:56 +0100 Subject: [PATCH 151/504] Test IRC notifications Signed-off-by: falkTX --- .github/workflows/irc.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/irc.yml diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml new file mode 100644 index 00000000..8459f247 --- /dev/null +++ b/.github/workflows/irc.yml @@ -0,0 +1,14 @@ +on: [push] + +jobs: + notification: + runs-on: ubuntu-latest + name: IRC notification + steps: + - name: IRC notification + uses: Gottox/irc-message-action@v2 + with: + channel: '#kxstudio' + nickname: kxstudio-bot + message: |- + ${{ github.actor }} pushed ${{ github.event.ref }} ${{ github.event.compare }} From e0b136656d75e910d271f7adfe436b5e83146033 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 22:23:05 +0100 Subject: [PATCH 152/504] Set the name of the workflow Signed-off-by: falkTX --- .github/workflows/irc.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml index 8459f247..eace82ee 100644 --- a/.github/workflows/irc.yml +++ b/.github/workflows/irc.yml @@ -1,3 +1,5 @@ +name: irc + on: [push] jobs: From 6260f5b44fb03090e426563d5e79bbcfb8d76cfe Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 22:28:06 +0100 Subject: [PATCH 153/504] Let IRC know of the commit message Signed-off-by: falkTX --- .github/workflows/irc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml index eace82ee..b1e80177 100644 --- a/.github/workflows/irc.yml +++ b/.github/workflows/irc.yml @@ -13,4 +13,4 @@ jobs: channel: '#kxstudio' nickname: kxstudio-bot message: |- - ${{ github.actor }} pushed ${{ github.event.ref }} ${{ github.event.compare }} + ${{ github.actor }} pushed ${{ github.event.head_commit.message }} ${{ github.event.compare }} From 1c72ea096b63fe87d005fe002f3999b72fa9be4b Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 22:37:15 +0100 Subject: [PATCH 154/504] Try a better message Signed-off-by: falkTX --- .github/workflows/irc.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml index b1e80177..46f3c9b6 100644 --- a/.github/workflows/irc.yml +++ b/.github/workflows/irc.yml @@ -7,10 +7,16 @@ jobs: runs-on: ubuntu-latest name: IRC notification steps: - - name: IRC notification - uses: Gottox/irc-message-action@v2 - with: - channel: '#kxstudio' - nickname: kxstudio-bot - message: |- - ${{ github.actor }} pushed ${{ github.event.head_commit.message }} ${{ github.event.compare }} + - uses: jungwinter/split@v2 + id: split + with: + msg: ${{ github.event.head_commit.message }} + separator: '\n' + maxsplit: 1 + - name: IRC notification + uses: Gottox/irc-message-action@v2 + with: + channel: '#kxstudio' + nickname: kxstudio-bot + message: |- + ${{ github.actor }} pushed ${{ steps.split.outputs._0 }} ${{ github.event.compare }} From d4ecaab1d2473a14c981022554954ae552fcc747 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 22:42:11 +0100 Subject: [PATCH 155/504] Try again Signed-off-by: falkTX --- .github/workflows/irc.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml index 46f3c9b6..ae434281 100644 --- a/.github/workflows/irc.yml +++ b/.github/workflows/irc.yml @@ -10,13 +10,12 @@ jobs: - uses: jungwinter/split@v2 id: split with: - msg: ${{ github.event.head_commit.message }} + msg: ${{ github.event.commits[0].message }} separator: '\n' - maxsplit: 1 - name: IRC notification uses: Gottox/irc-message-action@v2 with: channel: '#kxstudio' nickname: kxstudio-bot message: |- - ${{ github.actor }} pushed ${{ steps.split.outputs._0 }} ${{ github.event.compare }} + ${{ github.actor }} pushed ${{ steps.split.outputs._0 }} ${{ github.event.commits[0].url }} From c9b1f631accd803bca1985d02e9ac1682fdff4fe Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 22:58:28 +0100 Subject: [PATCH 156/504] Try yet again Signed-off-by: falkTX --- .github/workflows/irc.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml index ae434281..6802dad7 100644 --- a/.github/workflows/irc.yml +++ b/.github/workflows/irc.yml @@ -7,15 +7,14 @@ jobs: runs-on: ubuntu-latest name: IRC notification steps: - - uses: jungwinter/split@v2 - id: split - with: - msg: ${{ github.event.commits[0].message }} - separator: '\n' + - name: Format message + id: message + run: | + message="${{ github.actor }} pushed ${{ github.event.commits[0].message }} ${{ github.event.commits[0].url }}" + echo ::set-output name=message::"${message}" - name: IRC notification uses: Gottox/irc-message-action@v2 with: channel: '#kxstudio' nickname: kxstudio-bot - message: |- - ${{ github.actor }} pushed ${{ steps.split.outputs._0 }} ${{ github.event.commits[0].url }} + message: steps.message.outputs.message From a309df8a1f808b4cdf98efc099972acfb12a875e Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 23:00:10 +0100 Subject: [PATCH 157/504] Fix a typo Signed-off-by: falkTX --- .github/workflows/irc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml index 6802dad7..e8d2cfac 100644 --- a/.github/workflows/irc.yml +++ b/.github/workflows/irc.yml @@ -17,4 +17,4 @@ jobs: with: channel: '#kxstudio' nickname: kxstudio-bot - message: steps.message.outputs.message + message: ${{ steps.message.outputs.message }} From 7069b02cddb7f82c152629d2f406ad9b2d95ce10 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Oct 2021 23:07:14 +0100 Subject: [PATCH 158/504] Yet another attempt Signed-off-by: falkTX --- .github/workflows/irc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/irc.yml b/.github/workflows/irc.yml index e8d2cfac..539fa1d2 100644 --- a/.github/workflows/irc.yml +++ b/.github/workflows/irc.yml @@ -10,7 +10,7 @@ jobs: - name: Format message id: message run: | - message="${{ github.actor }} pushed ${{ github.event.commits[0].message }} ${{ github.event.commits[0].url }}" + message="${{ github.actor }} pushed $(echo '${{ github.event.commits[0].message }}' | head -n 1) ${{ github.event.commits[0].url }}" echo ::set-output name=message::"${message}" - name: IRC notification uses: Gottox/irc-message-action@v2 From ab236b8d7aa345345376265388e127f8e8febcf0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Oct 2021 00:04:57 +0100 Subject: [PATCH 159/504] VST3: Fix state messages Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 40 ++++++++++++++++++++++++------- distrho/src/DistrhoUIVST3.cpp | 35 +++++++++++++++++++++------ 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 7993163a..eff8bb11 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -310,9 +310,9 @@ ScopedUTF16String::ScopedUTF16String(const char* const s) noexcept : str(nullptr) { const size_t len = strlen(s); - str = (int16_t*)malloc(sizeof(int16_t) * len); + str = (int16_t*)malloc(sizeof(int16_t) * (len + 1)); DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,); - strncpy_utf16(str, s, len); + strncpy_utf16(str, s, len + 1); } ScopedUTF16String::~ScopedUTF16String() noexcept @@ -1836,26 +1836,42 @@ public: # if DISTRHO_PLUGIN_WANT_STATE if (std::strcmp(msgid, "state-set") == 0) { - int16_t* key16; - int16_t* value16; - uint32_t keySize, valueSize; + int64_t keyLength = -1; + int64_t valueLength = -1; v3_result res; - res = v3_cpp_obj(attrs)->get_binary(attrs, "key", (const void**)&key16, &keySize); + res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR); - res = v3_cpp_obj(attrs)->get_binary(attrs, "value", (const void**)&value16, &valueSize); + res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR); + + int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1)); + DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM); + + int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1)); + DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); + + res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); // do cheap inline conversion char* const key = (char*)key16; char* const value = (char*)value16; - for (uint32_t i=0; isecond = value; + std::free(key16); + std::free(value16); return V3_OK; } } @@ -1875,6 +1893,8 @@ public: d_stderr("Failed to find plugin state with key \"%s\"", key); } + std::free(key16); + std::free(value16); return V3_OK; } # endif @@ -2043,6 +2063,8 @@ private: DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); + 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); diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 0bd6ce0c..d50bb203 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -299,27 +299,46 @@ public: #if DISTRHO_PLUGIN_WANT_STATE if (std::strcmp(msgid, "state-set") == 0) { - int16_t* key16; - int16_t* value16; - uint32_t keySize, valueSize; + int64_t keyLength = -1; + int64_t valueLength = -1; v3_result res; - res = v3_cpp_obj(attrs)->get_binary(attrs, "key", (const void**)&key16, &keySize); + res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR); - res = v3_cpp_obj(attrs)->get_binary(attrs, "value", (const void**)&value16, &valueSize); + res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR); + + int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1)); + DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM); + + int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1)); + DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); + + res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); // do cheap inline conversion char* const key = (char*)key16; char* const value = (char*)value16; - for (uint32_t i=0; iset_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); From fc96e9fd894df36e45aaacac209ca7078ff0629d Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Oct 2021 21:26:24 +0100 Subject: [PATCH 160/504] Fix wrong context on UI deletion, needed for macOS hosts using GL --- distrho/src/DistrhoUIInternal.hpp | 3 +++ distrho/src/DistrhoUIPrivateData.hpp | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 64148f83..ed67a6c0 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -108,6 +108,9 @@ public: ~UIExporter() { quit(); +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + uiData->window->enterContextForDeletion(); +#endif delete ui; delete uiData; } diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 93a8a89a..c3be4dc5 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -183,10 +183,18 @@ public: if (pData->view == nullptr) return; + // this is called just before creating UI, ensuring proper context to it if (pData->initPost()) puglBackendEnter(pData->view); } + ~PluginWindow() + { + if (pData->view != nullptr) + puglBackendLeave(pData->view); + } + + // called after creating UI, restoring proper context void leaveContext() { if (pData->view == nullptr) @@ -199,11 +207,19 @@ public: puglBackendLeave(pData->view); } + // used for temporary windows (VST2/3 get size without active/visible view) void setIgnoreIdleCallbacks(const bool ignore = true) { pData->ignoreIdleCallbacks = ignore; } + // called right before deleting UI, ensuring correct context + void enterContextForDeletion() + { + if (pData->view != nullptr) + puglBackendEnter(pData->view); + } + protected: void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override { From 1ad2bcc6b544466f917198ad31c2c2cf1daef96f Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Oct 2021 22:11:51 +0100 Subject: [PATCH 161/504] VST3: Use context from component too, fixes GUI on some hosts --- distrho/src/DistrhoPluginVST3.cpp | 56 +++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index eff8bb11..b4fabf98 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2489,20 +2489,26 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // cached values v3_component_handler** handler; v3_host_application** const hostContextFromFactory; + v3_host_application** hostContextFromComponent; v3_host_application** hostContextFromInitialize; - dpf_edit_controller(ScopedPointer& v, v3_host_application** const h) + dpf_edit_controller(ScopedPointer& v, v3_host_application** const hf, v3_host_application** const hc) : refcounter(1), vst3(v), initialized(false), handler(nullptr), - hostContextFromFactory(h), + hostContextFromFactory(hf), + hostContextFromComponent(hc), hostContextFromInitialize(nullptr) { - // v3_funknown, single instance + // make sure context is valid through this controller lifetime + if (hostContextFromComponent != nullptr) + v3_cpp_obj_ref(hostContextFromComponent); + + // v3_funknown, everything custom query_interface = query_interface_edit_controller; - ref = dpf_single_instance_ref; - unref = dpf_single_instance_unref; + ref = ref_edit_controller; + unref = unref_edit_controller; // v3_plugin_base base.initialize = initialize; @@ -2524,6 +2530,15 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ctrl.create_view = create_view; } + void cleanup() + { + if (hostContextFromComponent != nullptr) + { + v3_cpp_obj_unref(hostContextFromComponent); + hostContextFromComponent = nullptr; + } + } + // ---------------------------------------------------------------------------------------------------------------- // v3_funknown @@ -2577,6 +2592,26 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return V3_NO_INTERFACE; } + static uint32_t V3_API ref_edit_controller(void* self) + { + return ++(*(dpf_edit_controller**)self)->refcounter; + } + + static uint32_t V3_API unref_edit_controller(void* self) + { + dpf_edit_controller** const controllerptr = (dpf_edit_controller**)self; + dpf_edit_controller* const controller = *controllerptr; + + if (const int refcount = --controller->refcounter) + { + d_stdout("dpf_edit_controller::unref => %p | refcount %i", self, refcount); + return refcount; + } + + controller->cleanup(); + return 0; + } + // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_base @@ -2804,6 +2839,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // we require a host context for message creation v3_host_application** host = controller->hostContextFromInitialize != nullptr ? controller->hostContextFromInitialize + : controller->hostContextFromComponent != nullptr + ? controller->hostContextFromComponent : controller->hostContextFromFactory; DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); @@ -3140,7 +3177,11 @@ struct dpf_component : v3_component_cpp { #if DISTRHO_PLUGIN_HAS_UI connection = nullptr; #endif - controller = nullptr; + if (controller != nullptr) + { + controller->cleanup(); + controller = nullptr; + } if (hostContextFromFactory != nullptr) v3_cpp_obj_unref(hostContextFromFactory); @@ -3199,7 +3240,8 @@ struct dpf_component : v3_component_cpp { { if (component->controller == nullptr) component->controller = new dpf_edit_controller(component->vst3, - component->hostContextFromFactory); + component->hostContextFromFactory, + component->hostContextFromInitialize); else ++component->controller->refcounter; *iface = &component->controller; From 218dd24a9de7111148b08a6cb00601fb3a55307f Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Oct 2021 23:30:52 +0100 Subject: [PATCH 162/504] An attempt at host-side VST3 UI resize that doesnt work yet --- dgl/Window.hpp | 6 ++ dgl/src/Window.cpp | 5 ++ distrho/src/DistrhoUIInternal.hpp | 14 ++++ distrho/src/DistrhoUIVST3.cpp | 105 +++++++++++++++++++++++------- 4 files changed, 106 insertions(+), 24 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 27e72dcd..a3ed2713 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -396,6 +396,12 @@ public: */ void runAsModal(bool blockWait = false); + /** + Get the size constraint set for the Window. + @see setGeometryConstraints + */ + Size getMinimumSizeConstraint(); + /** Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically. */ diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 6f2a7dad..603e677c 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -346,6 +346,11 @@ void Window::runAsModal(bool blockWait) pData->runAsModal(blockWait); } +Size Window::getMinimumSizeConstraint() +{ + return Size(pData->minWidth, pData->minHeight); +} + void Window::setGeometryConstraints(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index ed67a6c0..a357126e 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -132,6 +132,11 @@ public: return uiData->window->getScaleFactor(); } + Size getMinimumSizeConstraint() + { + return uiData->window->getMinimumSizeConstraint(); + } + bool isVisible() const noexcept { return uiData->window->isVisible(); @@ -259,6 +264,15 @@ public: // ------------------------------------------------------------------- + /* + void setWindowSizeForVST3(const uint width, const uint height) + { + // ui->setSize(width, height); + uiData->window->setSize(width, height); + // uiData->app.idle(); + } + */ + void setWindowTitle(const char* const uiTitle) { uiData->window->setTitle(uiTitle); diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index d50bb203..c023a49c 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -16,6 +16,7 @@ #include "DistrhoUIInternal.hpp" +#include "travesty/base.h" #include "travesty/edit_controller.h" #include "travesty/host.h" #include "travesty/view.h" @@ -38,9 +39,7 @@ namespace std { /* TODO items: * - mousewheel event * - key down/up events - * - size constraints * - host-side resize - * - oddities with init and size callback (triggered too early?) */ #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) @@ -102,13 +101,15 @@ public: const intptr_t winId, const float scaleFactor, const double sampleRate, - void* const instancePointer) + void* const instancePointer, + const bool ignoreSizeCallback) : fView(view), fHostContext(host), fConnection(connection), fFrame(frame), fReadyForPluginData(false), fScaleFactor(scaleFactor), + fIgnoreSizeCallback(ignoreSizeCallback), fUI(this, winId, sampleRate, editParameterCallback, setParameterCallback, @@ -134,8 +135,18 @@ public: disconnect(); } - void reconnectIfNeeded() + void postInit(const Size& requestedSize) { + if (fIgnoreSizeCallback) + { + fIgnoreSizeCallback = false; + + /* + if (requestedSize.isValid()) + fUI.setWindowSizeForVST3(requestedSize.getWidth(), requestedSize.getHeight()); + */ + } + if (fConnection != nullptr) connect(fConnection); } @@ -163,8 +174,7 @@ public: v3_result getSize(v3_view_rect* const rect) const noexcept { - std::memset(rect, 0, sizeof(v3_view_rect)); - + rect->left = rect->top = 0; rect->right = fUI.getWidth(); rect->bottom = fUI.getHeight(); #ifdef DISTRHO_OS_MAC @@ -172,15 +182,17 @@ public: rect->right /= scaleFactor; rect->bottom /= scaleFactor; #endif - return V3_OK; } - v3_result onSize(v3_view_rect* const /*rect*/) + /* + v3_result onSize(v3_view_rect* const rect) { - // TODO + d_stdout("host->plugin onSize request %i %i", rect->right - rect->left, rect->bottom - rect->top); + fUI.setWindowSizeForVST3(rect->right - rect->left, rect->bottom - rect->top); return V3_NOT_IMPLEMENTED; } + */ v3_result onFocus(const bool state) { @@ -200,10 +212,20 @@ public: return V3_OK; } - v3_result checkSizeConstraint(v3_view_rect* const /*rect*/) + v3_result checkSizeConstraint(v3_view_rect* const rect) { - // TODO - return V3_NOT_IMPLEMENTED; + const Size size(fUI.getMinimumSizeConstraint()); + const int32_t minright = static_cast(size.getWidth()); + const int32_t minbottom = static_cast(size.getHeight()); + if (minright > rect->right || minbottom > rect->bottom) + { + rect->right = minright; + rect->bottom = minbottom; + d_stdout("host->plugin checkSizeConstraint FALSE %i %i", rect->right, rect->bottom); + return V3_FALSE; + } + d_stdout("host->plugin checkSizeConstraint TRUE %i %i", rect->right, rect->bottom); + return V3_TRUE; } // ---------------------------------------------------------------------------------------------------------------- @@ -416,6 +438,7 @@ private: // Temporary data bool fReadyForPluginData; float fScaleFactor; + bool fIgnoreSizeCallback; // Plugin UI (after VST3 stuff so the UI can call into us during its constructor) UIExporter fUI; @@ -507,7 +530,14 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); - d_stdout("from UI setSize %u %u | %p %p", width, height, fView, fFrame); + + if (fIgnoreSizeCallback) + { + d_stdout("plugin->host setSize %u %u (IGNORED)", width, height); + return; + } + + d_stdout("plugin->host setSize %u %u", width, height); #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); @@ -516,7 +546,7 @@ private: #endif v3_view_rect rect; - std::memset(&rect, 0, sizeof(rect)); + rect.left = rect.top = 0; rect.right = width; rect.bottom = height; v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); @@ -843,6 +873,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { void* const instancePointer; double sampleRate; v3_plugin_frame** frame; + Size nextSize; dpf_plugin_view(v3_host_application** const h, void* const instance, const double sr) : refcounter(1), @@ -1009,9 +1040,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { (uintptr_t)parent, scaleFactor, view->sampleRate, - view->instancePointer); + view->instancePointer, + view->nextSize.isNotNull()); - view->uivst3->reconnectIfNeeded(); + view->uivst3->postInit(view->nextSize); + view->nextSize = Size(); #ifdef DPF_VST3_USING_HOST_RUN_LOOP // register a timer host run loop stuff @@ -1125,6 +1158,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, view->instancePointer, scaleFactor); + rect->left = rect->top = 0; rect->right = tmpUI.getWidth(); rect->bottom = tmpUI.getHeight(); return V3_OK; @@ -1136,10 +1170,18 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); + // TODO make this work + return V3_NOT_IMPLEMENTED; + + /* + if (UIVst3* const uivst3 = view->uivst3) + return uivst3->onSize(rect); + + // special case: allow UI to not be attached yet, as a way to set size before window creation - return uivst3->onSize(rect); + view->nextSize = Size(rect->right - rect->left, rect->bottom - rect->top); + return V3_OK; + */ } static v3_result V3_API on_focus(void* self, v3_bool state) @@ -1172,9 +1214,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { d_stdout("dpf_plugin_view::can_resize => %p", self); // #if DISTRHO_UI_USER_RESIZABLE -// return V3_OK; +// return V3_TRUE; // #else - return V3_NOT_IMPLEMENTED; + return V3_FALSE; // #endif } @@ -1184,10 +1226,25 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *(dpf_plugin_view**)self; DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - UIVst3* const uivst3 = view->uivst3; - DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); + if (UIVst3* const uivst3 = view->uivst3) + return uivst3->checkSizeConstraint(rect); + + // special case: allow UI to not be attached yet, as a way to get size constraint before window creation - return uivst3->checkSizeConstraint(rect); + const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; + UIExporter tmpUI(nullptr, 0, view->sampleRate, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + view->instancePointer, scaleFactor); + const Size size(tmpUI.getMinimumSizeConstraint()); + const int32_t minright = static_cast(size.getWidth()); + const int32_t minbottom = static_cast(size.getHeight()); + if (minright > rect->right || minbottom > rect->bottom) + { + rect->right = minright; + rect->bottom = minbottom; + return V3_FALSE; + } + return V3_TRUE; } }; From ce92fc723b5abcfe684c49c0f2a01494091da213 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 11:58:26 +0100 Subject: [PATCH 163/504] VST3: Clear up situation with host-side resize Signed-off-by: falkTX --- dgl/Window.hpp | 2 +- dgl/src/Window.cpp | 3 +- distrho/src/DistrhoUIInternal.hpp | 13 +- distrho/src/DistrhoUIPrivateData.hpp | 6 - distrho/src/DistrhoUIVST3.cpp | 217 +++++++++++++++------------ 5 files changed, 129 insertions(+), 112 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index a3ed2713..c2046153 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -400,7 +400,7 @@ public: Get the size constraint set for the Window. @see setGeometryConstraints */ - Size getMinimumSizeConstraint(); + Size getMinimumSizeConstraint(bool& keepAspectRatio); /** Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically. diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 603e677c..776db8d6 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -346,8 +346,9 @@ void Window::runAsModal(bool blockWait) pData->runAsModal(blockWait); } -Size Window::getMinimumSizeConstraint() +Size Window::getMinimumSizeConstraint(bool& keepAspectRatio) { + keepAspectRatio = pData->keepAspectRatio; return Size(pData->minWidth, pData->minHeight); } diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index a357126e..e22a5787 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -132,9 +132,14 @@ public: return uiData->window->getScaleFactor(); } - Size getMinimumSizeConstraint() + Size getMinimumSizeConstraint(bool& keepAspectRatio) { - return uiData->window->getMinimumSizeConstraint(); + return uiData->window->getMinimumSizeConstraint(keepAspectRatio); + } + + bool isResizable() const noexcept + { + return uiData->window->isResizable(); } bool isVisible() const noexcept @@ -264,14 +269,12 @@ public: // ------------------------------------------------------------------- - /* void setWindowSizeForVST3(const uint width, const uint height) { - // ui->setSize(width, height); + ui->setSize(width, height); uiData->window->setSize(width, height); // uiData->app.idle(); } - */ void setWindowTitle(const char* const uiTitle) { diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index c3be4dc5..1b63a33e 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -38,12 +38,6 @@ # define DISTRHO_UI_USER_RESIZABLE 0 #endif -// TODO figure out how to detect host support -#if defined(DISTRHO_PLUGIN_TARGET_VST3) -# undef DISTRHO_UI_USER_RESIZABLE -# define DISTRHO_UI_USER_RESIZABLE 0 -#endif - // ----------------------------------------------------------------------- #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index c023a49c..b156616e 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -39,7 +39,6 @@ namespace std { /* TODO items: * - mousewheel event * - key down/up events - * - host-side resize */ #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) @@ -76,6 +75,42 @@ struct ScopedUTF16String { // -------------------------------------------------------------------------------------------------------------------- +static bool checkSizeConstraint(const Size& size, const bool keepAspectRatio, v3_view_rect* const rect) +{ + const int32_t minWidth = static_cast(size.getWidth()); + const int32_t minHeight = static_cast(size.getHeight()); + bool changed = false; + + if (keepAspectRatio) + { + const double ratio = static_cast(minWidth) / static_cast(minHeight); + const double reqRatio = static_cast(rect->right) / static_cast(rect->bottom); + + if (d_isNotEqual(ratio, reqRatio)) + { + changed = true; + + // fix width + if (reqRatio > ratio) + rect->right = static_cast(rect->bottom * ratio + 0.5); + // fix height + else + rect->bottom = static_cast(static_cast(rect->right) / ratio + 0.5); + } + } + + if (minWidth > rect->right || minHeight > rect->bottom) + { + changed = true; + rect->right = minWidth; + rect->bottom = minHeight; + } + + return changed; +} + +// -------------------------------------------------------------------------------------------------------------------- + /** * VST3 UI class. * @@ -102,14 +137,15 @@ public: const float scaleFactor, const double sampleRate, void* const instancePointer, - const bool ignoreSizeCallback) + const bool willResizeFromHost) : fView(view), fHostContext(host), fConnection(connection), fFrame(frame), fReadyForPluginData(false), fScaleFactor(scaleFactor), - fIgnoreSizeCallback(ignoreSizeCallback), + fIsResizingFromPlugin(false), + fIsResizingFromHost(willResizeFromHost), fUI(this, winId, sampleRate, editParameterCallback, setParameterCallback, @@ -121,9 +157,6 @@ public: instancePointer, scaleFactor) { -#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) - fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); -#endif } ~UIVst3() @@ -137,18 +170,15 @@ public: void postInit(const Size& requestedSize) { - if (fIgnoreSizeCallback) - { - fIgnoreSizeCallback = false; - - /* - if (requestedSize.isValid()) - fUI.setWindowSizeForVST3(requestedSize.getWidth(), requestedSize.getHeight()); - */ - } + if (fIsResizingFromHost && requestedSize.isValid()) + fUI.setWindowSizeForVST3(requestedSize.getWidth(), requestedSize.getHeight()); if (fConnection != nullptr) connect(fConnection); + +#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) + fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); +#endif } // ---------------------------------------------------------------------------------------------------------------- @@ -185,14 +215,20 @@ public: return V3_OK; } - /* v3_result onSize(v3_view_rect* const rect) { - d_stdout("host->plugin onSize request %i %i", rect->right - rect->left, rect->bottom - rect->top); + if (fIsResizingFromPlugin) + { + d_stdout("host->plugin onSize request %i %i (IGNORED, plugin resize active)", + rect->right - rect->left, rect->bottom - rect->top); + return V3_OK; + } + + d_stdout("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); - return V3_NOT_IMPLEMENTED; + return V3_OK; } - */ v3_result onFocus(const bool state) { @@ -212,20 +248,16 @@ public: return V3_OK; } + v3_result canResize() noexcept + { + return fUI.isResizable() ? V3_TRUE : V3_FALSE; + } + v3_result checkSizeConstraint(v3_view_rect* const rect) { - const Size size(fUI.getMinimumSizeConstraint()); - const int32_t minright = static_cast(size.getWidth()); - const int32_t minbottom = static_cast(size.getHeight()); - if (minright > rect->right || minbottom > rect->bottom) - { - rect->right = minright; - rect->bottom = minbottom; - d_stdout("host->plugin checkSizeConstraint FALSE %i %i", rect->right, rect->bottom); - return V3_FALSE; - } - d_stdout("host->plugin checkSizeConstraint TRUE %i %i", rect->right, rect->bottom); - return V3_TRUE; + bool keepAspectRatio; + const Size size(fUI.getMinimumSizeConstraint(keepAspectRatio)); + return ::checkSizeConstraint(size, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; } // ---------------------------------------------------------------------------------------------------------------- @@ -402,19 +434,21 @@ public: #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) void idleCallback() override { - if (fReadyForPluginData) - { - fReadyForPluginData = false; - requestMorePluginData(); - } - fUI.idleForVST3(); + doIdleStuff(); } #else // ---------------------------------------------------------------------------------------------------------------- // v3_timer_handler interface calls void onTimer() + { + fUI.plugin_idle(); + doIdleStuff(); + } +#endif + + void doIdleStuff() { if (fReadyForPluginData) { @@ -422,9 +456,18 @@ public: requestMorePluginData(); } - fUI.plugin_idle(); + if (fIsResizingFromHost) + { + fIsResizingFromHost = false; + d_stdout("was resizing from host, now stopped"); + } + + if (fIsResizingFromPlugin) + { + fIsResizingFromPlugin = false; + d_stdout("was resizing from plugin, now stopped"); + } } -#endif // ---------------------------------------------------------------------------------------------------------------- @@ -438,7 +481,8 @@ private: // Temporary data bool fReadyForPluginData; float fScaleFactor; - bool fIgnoreSizeCallback; + bool fIsResizingFromPlugin; + bool fIsResizingFromHost; // Plugin UI (after VST3 stuff so the UI can call into us during its constructor) UIExporter fUI; @@ -531,13 +575,13 @@ private: DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); - if (fIgnoreSizeCallback) + if (fIsResizingFromHost) { - d_stdout("plugin->host setSize %u %u (IGNORED)", width, height); + d_stdout("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); return; } - d_stdout("plugin->host setSize %u %u", width, height); + d_stdout("plugin->host setSize %u %u (OK)", width, height); #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); @@ -545,6 +589,8 @@ private: height /= scaleFactor; #endif + fIsResizingFromPlugin = true; + v3_view_rect rect; rect.left = rect.top = 0; rect.right = width; @@ -923,8 +969,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; } - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE); + dpf_plugin_view* const view = *static_cast(self); if (v3_tuid_match(v3_connection_point_iid, iid)) { @@ -951,12 +996,12 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static uint32_t V3_API ref_view(void* self) { - return ++(*(dpf_plugin_view**)self)->refcounter; + return ++(*static_cast(self))->refcounter; } static uint32_t V3_API unref_view(void* self) { - dpf_plugin_view** const viewptr = (dpf_plugin_view**)self; + dpf_plugin_view** const viewptr = static_cast(self); dpf_plugin_view* const view = *viewptr; if (const int refcount = --view->refcounter) @@ -1000,8 +1045,6 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API is_platform_type_supported(void* self, const char* platform_type) { d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); for (size_t i=0; i %p %p %s", self, parent, platform_type); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); for (size_t i=0; isampleRate, view->instancePointer, - view->nextSize.isNotNull()); + view->nextSize.isValid()); view->uivst3->postInit(view->nextSize); view->nextSize = Size(); @@ -1064,8 +1106,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API removed(void* self) { d_stdout("dpf_plugin_view::removed => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); #ifdef DPF_VST3_USING_HOST_RUN_LOOP @@ -1110,8 +1151,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_wheel(void* self, float distance) { d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); @@ -1122,8 +1162,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_key_down(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) { d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); @@ -1134,8 +1173,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_key_up(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) { d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); @@ -1146,14 +1184,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API get_size(void* self, v3_view_rect* rect) { d_stdout("dpf_plugin_view::get_size => %p", self); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); if (UIVst3* const uivst3 = view->uivst3) return uivst3->getSize(rect); - // special case: allow UI to not be attached yet, as a way to get size before window creation - const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -1166,29 +1201,19 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_size(void* self, v3_view_rect* rect) { - d_stdout("dpf_plugin_view::on_size => %p %p", self, rect); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); - - // TODO make this work - return V3_NOT_IMPLEMENTED; + dpf_plugin_view* const view = *static_cast(self); - /* if (UIVst3* const uivst3 = view->uivst3) return uivst3->onSize(rect); - // special case: allow UI to not be attached yet, as a way to set size before window creation - view->nextSize = Size(rect->right - rect->left, rect->bottom - rect->top); return V3_OK; - */ } static v3_result V3_API on_focus(void* self, v3_bool state) { d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); @@ -1198,9 +1223,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API set_frame(void* self, v3_plugin_frame** frame) { - d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); view->frame = frame; @@ -1212,39 +1235,35 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API can_resize(void* self) { - d_stdout("dpf_plugin_view::can_resize => %p", self); -// #if DISTRHO_UI_USER_RESIZABLE -// return V3_TRUE; -// #else +#if DISTRHO_UI_USER_RESIZABLE + dpf_plugin_view* const view = *static_cast(self); + + if (UIVst3* const uivst3 = view->uivst3) + return uivst3->canResize(); + + return V3_TRUE; +#else return V3_FALSE; -// #endif + + // unused + (void)self; +#endif } static v3_result V3_API check_size_constraint(void* self, v3_view_rect* rect) { - d_stdout("dpf_plugin_view::check_size_constraint => %p %p", self, rect); - dpf_plugin_view* const view = *(dpf_plugin_view**)self; - DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED); + dpf_plugin_view* const view = *static_cast(self); if (UIVst3* const uivst3 = view->uivst3) return uivst3->checkSizeConstraint(rect); - // special case: allow UI to not be attached yet, as a way to get size constraint before window creation - const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, view->instancePointer, scaleFactor); - const Size size(tmpUI.getMinimumSizeConstraint()); - const int32_t minright = static_cast(size.getWidth()); - const int32_t minbottom = static_cast(size.getHeight()); - if (minright > rect->right || minbottom > rect->bottom) - { - rect->right = minright; - rect->bottom = minbottom; - return V3_FALSE; - } - return V3_TRUE; + bool keepAspectRatio; + const Size size(tmpUI.getMinimumSizeConstraint(keepAspectRatio)); + return ::checkSizeConstraint(size, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; } }; From 2b3bf6625624890a7c930c8aa6a785583871f1d7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 12:22:34 +0100 Subject: [PATCH 164/504] Allow external UI to set geometry constraints, fix some warnings Signed-off-by: falkTX --- dgl/Window.hpp | 4 +-- dgl/src/Window.cpp | 2 +- distrho/extra/ExternalWindow.hpp | 37 +++++++++++++++++++++------- distrho/src/DistrhoUIInternal.hpp | 13 ++++++++-- distrho/src/DistrhoUIPrivateData.hpp | 6 +++++ distrho/src/DistrhoUIVST3.cpp | 21 +++++++++------- 6 files changed, 60 insertions(+), 23 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index c2046153..181f0122 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -397,10 +397,10 @@ public: void runAsModal(bool blockWait = false); /** - Get the size constraint set for the Window. + Get the geometry constraints set for the Window. @see setGeometryConstraints */ - Size getMinimumSizeConstraint(bool& keepAspectRatio); + Size getGeometryConstraints(bool& keepAspectRatio); /** Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically. diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 776db8d6..c09106fa 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -346,7 +346,7 @@ void Window::runAsModal(bool blockWait) pData->runAsModal(blockWait); } -Size Window::getMinimumSizeConstraint(bool& keepAspectRatio) +Size Window::getGeometryConstraints(bool& keepAspectRatio) { keepAspectRatio = pData->keepAspectRatio; return Size(pData->minWidth, pData->minHeight); diff --git a/distrho/extra/ExternalWindow.hpp b/distrho/extra/ExternalWindow.hpp index 53b0f541..1f1c360b 100644 --- a/distrho/extra/ExternalWindow.hpp +++ b/distrho/extra/ExternalWindow.hpp @@ -295,7 +295,8 @@ public: */ void setSize(uint width, uint height) { - DISTRHO_SAFE_ASSERT_UINT2_RETURN(width > 1 && height > 1, width, height,); + DISTRHO_SAFE_ASSERT_UINT_RETURN(width > 1, width,); + DISTRHO_SAFE_ASSERT_UINT_RETURN(height > 1, height,); if (pData.width == width && pData.height == height) return; @@ -314,10 +315,24 @@ public: { if (pData.title == title) return; + pData.title = title; titleChanged(title); } + /** + Set geometry constraints for the Window when resized by the user. + */ + void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio = false) + { + DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumWidth > 0, minimumWidth,); + DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumHeight > 0, minimumHeight,); + + pData.minWidth = minimumWidth; + pData.minHeight = minimumHeight; + pData.keepAspectRatio = keepAspectRatio; + } + /* -------------------------------------------------------------------------------------------------------- * TopLevelWidget-like calls - actions called by the host */ @@ -339,6 +354,7 @@ public: { if (pData.visible == visible) return; + pData.visible = visible; visibilityChanged(visible); } @@ -351,6 +367,7 @@ public: { if (pData.transientWinId == winId) return; + pData.transientWinId = winId; transientParentWindowChanged(winId); } @@ -388,39 +405,35 @@ protected: A callback for when the window size changes. @note WIP this might need to get fed back into the host somehow. */ - virtual void sizeChanged(uint width, uint height) + virtual void sizeChanged(uint /* width */, uint /* height */) { // unused, meant for custom implementations - return; (void)width; (void)height; } /** A callback for when the window title changes. @note WIP this might need to get fed back into the host somehow. */ - virtual void titleChanged(const char* title) + virtual void titleChanged(const char* /* title */) { // unused, meant for custom implementations - return; (void)title; } /** A callback for when the window visibility changes. @note WIP this might need to get fed back into the host somehow. */ - virtual void visibilityChanged(bool visible) + virtual void visibilityChanged(bool /* visible */) { // unused, meant for custom implementations - return; (void)visible; } /** A callback for when the transient parent window changes. */ - virtual void transientParentWindowChanged(uintptr_t winId) + virtual void transientParentWindowChanged(uintptr_t /* winId */) { // unused, meant for custom implementations - return; (void)winId; } private: @@ -533,6 +546,9 @@ private: uint height; double scaleFactor; String title; + uint minWidth; + uint minHeight; + bool keepAspectRatio; bool isQuitting; bool isStandalone; bool visible; @@ -544,6 +560,9 @@ private: height(1), scaleFactor(1.0), title(), + minWidth(0), + minHeight(0), + keepAspectRatio(false), isQuitting(false), isStandalone(false), visible(false) {} diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index e22a5787..af2764e8 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -132,9 +132,16 @@ public: return uiData->window->getScaleFactor(); } - Size getMinimumSizeConstraint(bool& keepAspectRatio) + bool getGeometryConstraints(uint& minimumWidth, uint& minimumHeight, bool& keepAspectRatio) const noexcept { - return uiData->window->getMinimumSizeConstraint(keepAspectRatio); +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + uiData->window->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); +#else + const Size size(uiData->window->getGeometryConstraints(keepAspectRatio)); + minimumWidth = size.getWidth(); + minimumHeight = size.getHeight(); +#endif + return true; } bool isResizable() const noexcept @@ -272,8 +279,10 @@ public: void setWindowSizeForVST3(const uint width, const uint height) { ui->setSize(width, height); +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI uiData->window->setSize(width, height); // uiData->app.idle(); +#endif } void setWindowTitle(const char* const uiTitle) diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 1b63a33e..1371b9d2 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -152,6 +152,12 @@ public: void setTitle(const char* const title) { ui->setTitle(title); } void setVisible(const bool visible) { ui->setVisible(visible); } uintptr_t getNativeWindowHandle() const noexcept { return ui->getNativeWindowHandle(); } + void getGeometryConstraints(uint& minimumWidth, uint& minimumHeight, bool& keepAspectRatio) const noexcept + { + minimumWidth = ui->pData.minWidth; + minimumHeight = ui->pData.minHeight; + keepAspectRatio = ui->pData.keepAspectRatio; + } DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) }; diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index b156616e..81c1e868 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -75,10 +75,11 @@ struct ScopedUTF16String { // -------------------------------------------------------------------------------------------------------------------- -static bool checkSizeConstraint(const Size& size, const bool keepAspectRatio, v3_view_rect* const rect) +static bool checkSizeConstraint(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, + v3_view_rect* const rect) { - const int32_t minWidth = static_cast(size.getWidth()); - const int32_t minHeight = static_cast(size.getHeight()); + const int32_t minWidth = static_cast(minimumWidth); + const int32_t minHeight = static_cast(minimumHeight); bool changed = false; if (keepAspectRatio) @@ -92,10 +93,10 @@ static bool checkSizeConstraint(const Size& size, const bool keepAspectRat // fix width if (reqRatio > ratio) - rect->right = static_cast(rect->bottom * ratio + 0.5); + rect->right = static_cast(rect->bottom * ratio + 0.5); // fix height else - rect->bottom = static_cast(static_cast(rect->right) / ratio + 0.5); + rect->bottom = static_cast(static_cast(rect->right) / ratio + 0.5); } } @@ -255,9 +256,10 @@ public: v3_result checkSizeConstraint(v3_view_rect* const rect) { + uint minimumWidth, minimumHeight; bool keepAspectRatio; - const Size size(fUI.getMinimumSizeConstraint(keepAspectRatio)); - return ::checkSizeConstraint(size, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; + fUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); + return ::checkSizeConstraint(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; } // ---------------------------------------------------------------------------------------------------------------- @@ -1261,9 +1263,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, view->instancePointer, scaleFactor); + uint minimumWidth, minimumHeight; bool keepAspectRatio; - const Size size(tmpUI.getMinimumSizeConstraint(keepAspectRatio)); - return ::checkSizeConstraint(size, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; + tmpUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); + return ::checkSizeConstraint(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; } }; From 8a4e6258c20059507333fe2a5387195010f9bb63 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 12:28:09 +0100 Subject: [PATCH 165/504] Fix no namespace build Signed-off-by: falkTX --- distrho/src/DistrhoUIInternal.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index af2764e8..c4e0004e 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -137,7 +137,7 @@ public: #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI uiData->window->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); #else - const Size size(uiData->window->getGeometryConstraints(keepAspectRatio)); + const DGL_NAMESPACE::Size size(uiData->window->getGeometryConstraints(keepAspectRatio)); minimumWidth = size.getWidth(); minimumHeight = size.getHeight(); #endif From e8475ede1af72769ba293a6b0a80320b28be50ce Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 12:44:07 +0100 Subject: [PATCH 166/504] One more fix Signed-off-by: falkTX --- distrho/src/DistrhoUIVST3.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 81c1e868..db991b65 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -75,8 +75,8 @@ struct ScopedUTF16String { // -------------------------------------------------------------------------------------------------------------------- -static bool checkSizeConstraint(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, - v3_view_rect* const rect) +static bool applyGeometryConstraints(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, + v3_view_rect* const rect) { const int32_t minWidth = static_cast(minimumWidth); const int32_t minHeight = static_cast(minimumHeight); @@ -259,7 +259,7 @@ public: uint minimumWidth, minimumHeight; bool keepAspectRatio; fUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); - return ::checkSizeConstraint(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; + return applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; } // ---------------------------------------------------------------------------------------------------------------- @@ -1266,7 +1266,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { uint minimumWidth, minimumHeight; bool keepAspectRatio; tmpUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); - return ::checkSizeConstraint(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; + return applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; } }; From c82d094667f0eeaf2e235f78882a577b34c781ba Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 13:19:30 +0100 Subject: [PATCH 167/504] Really fix build, dont use Size class on VST3 UI code Signed-off-by: falkTX --- distrho/src/DistrhoUIInternal.hpp | 6 ++++-- distrho/src/DistrhoUIVST3.cpp | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index c4e0004e..863cb9d2 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -276,14 +276,16 @@ public: // ------------------------------------------------------------------- +#ifdef DISTRHO_PLUGIN_TARGET_VST3 void setWindowSizeForVST3(const uint width, const uint height) { ui->setSize(width, height); -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI uiData->window->setSize(width, height); // uiData->app.idle(); -#endif +# endif } +#endif void setWindowTitle(const char* const uiTitle) { diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index db991b65..04eb2167 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -169,10 +169,10 @@ public: disconnect(); } - void postInit(const Size& requestedSize) + void postInit(const int32_t nextWidth, const int32_t nextHeight) { - if (fIsResizingFromHost && requestedSize.isValid()) - fUI.setWindowSizeForVST3(requestedSize.getWidth(), requestedSize.getHeight()); + if (fIsResizingFromHost && nextWidth > 0 && nextHeight > 0) + fUI.setWindowSizeForVST3(nextWidth, nextHeight); if (fConnection != nullptr) connect(fConnection); @@ -921,14 +921,16 @@ struct dpf_plugin_view : v3_plugin_view_cpp { void* const instancePointer; double sampleRate; v3_plugin_frame** frame; - Size nextSize; + int32_t nextWidth, nextHeight; dpf_plugin_view(v3_host_application** const h, void* const instance, const double sr) : refcounter(1), host(h), instancePointer(instance), sampleRate(sr), - frame(nullptr) + frame(nullptr), + nextWidth(0), + nextHeight(0) { // v3_funknown, everything custom query_interface = query_interface_view; @@ -1085,10 +1087,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { scaleFactor, view->sampleRate, view->instancePointer, - view->nextSize.isValid()); + view->nextWidth > 0 && view->nextHeight > 0); - view->uivst3->postInit(view->nextSize); - view->nextSize = Size(); + view->uivst3->postInit(view->nextWidth, view->nextHeight); + view->nextWidth = 0; + view->nextHeight = 0; #ifdef DPF_VST3_USING_HOST_RUN_LOOP // register a timer host run loop stuff @@ -1208,7 +1211,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->onSize(rect); - view->nextSize = Size(rect->right - rect->left, rect->bottom - rect->top); + view->nextWidth = rect->right - rect->left; + view->nextHeight = rect->bottom - rect->top; return V3_OK; } From ca84b57669f45e397e913ec5cf1cbf5f573b4b38 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 16:25:38 +0100 Subject: [PATCH 168/504] VST3: Add stubs for keyboard handling Signed-off-by: falkTX --- distrho/src/DistrhoUIInternal.hpp | 23 +++++++++++++ distrho/src/DistrhoUIVST3.cpp | 54 ++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 863cb9d2..fd79f643 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -331,6 +331,29 @@ public: ui->onCharacterInput(cev); return ret; } + + bool handlePluginKeyboardVST3(const bool press, const uint keychar, const uint keycode, const uint16_t mods) + { + DGL_NAMESPACE::Widget::KeyboardEvent ev; + ev.mod = mods; + ev.press = press; + ev.key = keychar; + ev.keycode = keycode; + + const bool ret = ui->onKeyboard(ev); + + DGL_NAMESPACE::Widget::CharacterInputEvent cev; + cev.mod = mods; + cev.keycode = keycode; + cev.character = keychar; + + // if shift modifier is on, convert a-z -> A-Z for character input + if (keychar >= 'a' && keychar <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) + cev.character -= 'a' - 'A'; + + ui->onCharacterInput(cev); + return ret; + } #endif // ------------------------------------------------------------------- diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 04eb2167..758c3c98 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -191,16 +191,62 @@ public: return V3_NOT_IMPLEMENTED; } - v3_result onKeyDown(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/) + v3_result onKeyDown(const int16_t keychar, const int16_t keycode, const int16_t modifiers) { + d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); + DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); + // TODO - return V3_NOT_IMPLEMENTED; + uint dglcode = 0; + + // TODO verify these + uint dglmods = 0; + if (modifiers & (1 << 0)) + dglmods |= kModifierShift; + if (modifiers & (1 << 1)) + dglmods |= kModifierAlt; +#ifdef DISTRHO_OS_MAC + if (modifiers & (1 << 2)) + dglmods |= kModifierSuper; + if (modifiers & (1 << 3)) + dglmods |= kModifierControl; +#else + if (modifiers & (1 << 2)) + dglmods |= kModifierControl; + if (modifiers & (1 << 3)) + dglmods |= kModifierSuper; +#endif + + return fUI.handlePluginKeyboardVST3(true, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; } - v3_result onKeyUp(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/) + v3_result onKeyUp(const int16_t keychar, const int16_t keycode, const int16_t modifiers) { + d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); + DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); + // TODO - return V3_NOT_IMPLEMENTED; + uint dglcode = 0; + + // TODO verify these + uint dglmods = 0; + if (modifiers & (1 << 0)) + dglmods |= kModifierShift; + if (modifiers & (1 << 1)) + dglmods |= kModifierAlt; +#ifdef DISTRHO_OS_MAC + if (modifiers & (1 << 2)) + dglmods |= kModifierSuper; + if (modifiers & (1 << 3)) + dglmods |= kModifierControl; +#else + if (modifiers & (1 << 2)) + dglmods |= kModifierControl; + if (modifiers & (1 << 3)) + dglmods |= kModifierSuper; +#endif + + return fUI.handlePluginKeyboardVST3(false, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; } v3_result getSize(v3_view_rect* const rect) const noexcept From e8e26eef212d2d4993022d54e96e46a533f5e62d Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 16:34:00 +0100 Subject: [PATCH 169/504] Fix no namespace build Signed-off-by: falkTX --- distrho/src/DistrhoUIVST3.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 758c3c98..660d052c 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -196,6 +196,8 @@ public: d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); + using namespace DGL_NAMESPACE; + // TODO uint dglcode = 0; @@ -225,6 +227,8 @@ public: d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); + using namespace DGL_NAMESPACE; + // TODO uint dglcode = 0; From b8f97e1c3fe7208748b297a815c4d086a9fcc593 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 19:40:23 +0100 Subject: [PATCH 170/504] Add debug prints for host context Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index b4fabf98..2ee93958 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2628,6 +2628,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { v3_host_application** host = nullptr; v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + d_stdout("dpf_edit_controller::initialize => %p %p | host %p", self, context, host); + // save it for later so we can unref it controller->hostContextFromInitialize = host; @@ -2836,6 +2838,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); + d_stdout("dpf_edit_controller::create_view => %p %s | edit-ctrl %p, host %p, factory %p", + self, name, + controller->hostContextFromInitialize, + controller->hostContextFromComponent, + controller->hostContextFromFactory); + // we require a host context for message creation v3_host_application** host = controller->hostContextFromInitialize != nullptr ? controller->hostContextFromInitialize @@ -3352,6 +3360,8 @@ struct dpf_component : v3_component_cpp { if (context != nullptr) v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + d_stdout("dpf_component::initialize => %p %s | host %p", self, context, host); + // save it for later so we can unref it component->hostContextFromInitialize = host; From 5948d8a38173da034cf19dd7cb23cd3ac131f3ab Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 20:31:12 +0100 Subject: [PATCH 171/504] Add some more debug prints Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 2ee93958..d7695f05 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -757,6 +757,10 @@ public: { res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + if (read == 0) + return V3_OK; + DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); for (int32_t i = 0; i < read; ++i) @@ -2501,6 +2505,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { hostContextFromComponent(hc), hostContextFromInitialize(nullptr) { + d_stdout("dpf_edit_controller() has contexts %p %p", + hostContextFromFactory, hostContextFromComponent); + // make sure context is valid through this controller lifetime if (hostContextFromComponent != nullptr) v3_cpp_obj_ref(hostContextFromComponent); @@ -2537,6 +2544,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { v3_cpp_obj_unref(hostContextFromComponent); hostContextFromComponent = nullptr; } + + d_stdout("dpf_edit_controller::cleanup() has contexts %p %p", + hostContextFromFactory, hostContextFromComponent); + } // ---------------------------------------------------------------------------------------------------------------- @@ -2594,6 +2605,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static uint32_t V3_API ref_edit_controller(void* self) { + d_stdout("dpf_edit_controller::ref => %p", self); return ++(*(dpf_edit_controller**)self)->refcounter; } @@ -2608,6 +2620,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return refcount; } + d_stdout("dpf_edit_controller::unref => %p is zero, doing cleanup", self); controller->cleanup(); return 0; } @@ -2833,6 +2846,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); + d_stdout("create_view has contexts %p %p", + controller->hostContextFromFactory, controller->hostContextFromComponent); + #if DISTRHO_PLUGIN_HAS_UI // plugin must be initialized PluginVst3* const vst3 = controller->vst3; @@ -3246,6 +3262,9 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_edit_controller_iid)) { + d_stdout("query_interface_component called with contexts %p %p", + component->hostContextFromFactory, component->hostContextFromInitialize); + if (component->controller == nullptr) component->controller = new dpf_edit_controller(component->vst3, component->hostContextFromFactory, @@ -3360,7 +3379,7 @@ struct dpf_component : v3_component_cpp { if (context != nullptr) v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); - d_stdout("dpf_component::initialize => %p %s | host %p", self, context, host); + d_stdout("dpf_component::initialize => %p %p | host %p", self, context, host); // save it for later so we can unref it component->hostContextFromInitialize = host; From 77f5ab64e91df1cef726ead3a04ee7ead38d1a64 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Oct 2021 21:10:29 +0100 Subject: [PATCH 172/504] Do not restrict symbols if DEBUG or SKIP_STRIPPING enabled Signed-off-by: falkTX --- Makefile.plugins.mk | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 0ba0ce4d..17c20286 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -111,6 +111,9 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Set plugin symbols to export +ifneq ($(DEBUG),true) +ifneq ($(SKIP_STRIPPING),true) + ifeq ($(MACOS),true) SYMBOLS_LADSPA = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/ladspa.exp SYMBOLS_DSSI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/dssi.exp @@ -127,7 +130,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 -else ifneq ($(DEBUG),true) +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 @@ -137,6 +140,9 @@ SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version SYMBOLS_VST3 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst3.version endif +endif +endif + # --------------------------------------------------------------------------------------------------------------------- # Handle UI stuff, disable UI support automatically From 5fedd7d39e682e85a996c5b27ba8f67789dd0d71 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 7 Oct 2021 08:50:02 +0100 Subject: [PATCH 173/504] Revert "Do not restrict symbols if DEBUG or SKIP_STRIPPING enabled" This reverts commit 77f5ab64e91df1cef726ead3a04ee7ead38d1a64. --- Makefile.plugins.mk | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 17c20286..0ba0ce4d 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -111,9 +111,6 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Set plugin symbols to export -ifneq ($(DEBUG),true) -ifneq ($(SKIP_STRIPPING),true) - ifeq ($(MACOS),true) SYMBOLS_LADSPA = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/ladspa.exp SYMBOLS_DSSI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/dssi.exp @@ -130,7 +127,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 -else +else ifneq ($(DEBUG),true) 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 @@ -140,9 +137,6 @@ SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version SYMBOLS_VST3 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst3.version endif -endif -endif - # --------------------------------------------------------------------------------------------------------------------- # Handle UI stuff, disable UI support automatically From f79095abdeb7442121f9f26adc53b87352f04e26 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 7 Oct 2021 11:00:04 +0100 Subject: [PATCH 174/504] Allow to define EXTRA_LIBS for plugin targets Signed-off-by: falkTX --- Makefile.plugins.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 0ba0ce4d..7b9dae29 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -312,7 +312,7 @@ $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp jack: $(jack) ifeq ($(HAVE_DGL),true) -$(jack): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.o $(DGL_LIB) +$(jack): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.o $(DGL_LIB) $(EXTRA_LIBS) else $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o endif @@ -355,7 +355,7 @@ lv2_dsp: $(lv2_dsp) lv2_sep: $(lv2_dsp) $(lv2_ui) ifeq ($(HAVE_DGL),true) -$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) +$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) $(EXTRA_LIBS) else $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o endif @@ -379,7 +379,7 @@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) vst2 vst: $(vst2) ifeq ($(HAVE_DGL),true) -$(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.o $(DGL_LIB) +$(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.o $(DGL_LIB) $(EXTRA_LIBS) else $(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o endif @@ -393,7 +393,7 @@ endif vst3: $(vst3) ifeq ($(HAVE_DGL),true) -$(vst3): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) +$(vst3): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) $(EXTRA_LIBS) else $(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o endif From c4e1210897e804a19b3fa35d542765b3feb0236e Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 7 Oct 2021 11:35:42 +0100 Subject: [PATCH 175/504] Make plugin objects depend on EXTRA_LIBS, add *.m/*.mm targets Signed-off-by: falkTX --- Makefile.plugins.mk | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 7b9dae29..67f8f2d8 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -238,26 +238,36 @@ all: # --------------------------------------------------------------------------------------------------------------------- # Common -$(BUILD_DIR)/%.S.o: %.S +$(BUILD_DIR)/%.S.o: %.S $(EXTRA_LIBS) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ -$(BUILD_DIR)/%.c.o: %.c +$(BUILD_DIR)/%.c.o: %.c $(EXTRA_LIBS) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ -$(BUILD_DIR)/%.cc.o: %.cc +$(BUILD_DIR)/%.cc.o: %.cc $(EXTRA_LIBS) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ -$(BUILD_DIR)/%.cpp.o: %.cpp +$(BUILD_DIR)/%.cpp.o: %.cpp $(EXTRA_LIBS) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ +$(BUILD_DIR)/%.m.o: %.m $(EXTRA_LIBS) + -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + @echo "Compiling $<" + $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -ObjC -c -o $@ + +$(BUILD_DIR)/%.mm.o: %.mm $(EXTRA_LIBS) + -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + @echo "Compiling $<" + $(SILENT)$(CC) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ + clean: rm -rf $(BUILD_DIR) rm -rf $(TARGET_DIR)/$(NAME) $(TARGET_DIR)/$(NAME)-* $(TARGET_DIR)/$(NAME).lv2 From 23f89562acbd637a23b9f0333877939ad26c0595 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 7 Oct 2021 21:20:36 +0100 Subject: [PATCH 176/504] Import alpha/tint stuff from VCV --- dgl/src/NanoVG.cpp | 1 + dgl/src/nanovg/nanovg.c | 56 ++++++++++++++++++++++++++++++++--------- dgl/src/nanovg/nanovg.h | 6 ++++- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 3ad9032c..aa252749 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -83,6 +83,7 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) #endif #include "nanovg/nanovg_gl.h" +#include "nanovg/nanovg_gl_utils.h" #if defined(NANOVG_GL2) # define nvgCreateGL nvgCreateGL2 diff --git a/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index 209c34f9..42a1b866 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/dgl/src/nanovg/nanovg.c @@ -74,7 +74,7 @@ struct NVGstate { float miterLimit; int lineJoin; int lineCap; - float alpha; + NVGcolor tint; float xform[6]; NVGscissor scissor; float fontSize; @@ -651,7 +651,7 @@ void nvgReset(NVGcontext* ctx) state->miterLimit = 10.0f; state->lineCap = NVG_BUTT; state->lineJoin = NVG_MITER; - state->alpha = 1.0f; + state->tint = nvgRGBAf(1, 1, 1, 1); nvgTransformIdentity(state->xform); state->scissor.extent[0] = -1.0f; @@ -697,9 +697,34 @@ void nvgLineJoin(NVGcontext* ctx, int join) } void nvgGlobalAlpha(NVGcontext* ctx, float alpha) +{ + nvgGlobalTint(ctx, nvgRGBAf(1, 1, 1, alpha)); +} + +void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint) +{ + NVGstate* state = nvg__getState(ctx); + state->tint = tint; +} + +NVGcolor nvgGetGlobalTint(NVGcontext* ctx) +{ + NVGstate* state = nvg__getState(ctx); + return state->tint; +} + +void nvgAlpha(NVGcontext* ctx, float alpha) +{ + NVGstate* state = nvg__getState(ctx); + state->tint.a *= alpha; +} + +void nvgTint(NVGcontext* ctx, NVGcolor tint) { NVGstate* state = nvg__getState(ctx); - state->alpha = alpha; + int i; + for (i = 0; i < 4; i++) + state->tint.rgba[i] *= tint.rgba[i]; } void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f) @@ -2234,9 +2259,11 @@ void nvgFill(NVGcontext* ctx) else nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); - // Apply global alpha - fillPaint.innerColor.a *= state->alpha; - fillPaint.outerColor.a *= state->alpha; + // Apply global tint + for (i = 0; i < 4; i++) { + fillPaint.innerColor.rgba[i] *= state->tint.rgba[i]; + fillPaint.outerColor.rgba[i] *= state->tint.rgba[i]; + } ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); @@ -2269,9 +2296,11 @@ void nvgStroke(NVGcontext* ctx) strokeWidth = ctx->fringeWidth; } - // Apply global alpha - strokePaint.innerColor.a *= state->alpha; - strokePaint.outerColor.a *= state->alpha; + // Apply global tint + for (i = 0; i < 4; i++) { + strokePaint.innerColor.rgba[i] *= state->tint.rgba[i]; + strokePaint.outerColor.rgba[i] *= state->tint.rgba[i]; + } nvg__flattenPaths(ctx); @@ -2438,15 +2467,18 @@ static int nvg__allocTextAtlas(NVGcontext* ctx) static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) { + int i; NVGstate* state = nvg__getState(ctx); NVGpaint paint = state->fill; // Render triangles. paint.image = ctx->fontImages[ctx->fontImageIdx]; - // Apply global alpha - paint.innerColor.a *= state->alpha; - paint.outerColor.a *= state->alpha; + // Apply global tint + for (i = 0; i < 4; i++) { + paint.innerColor.rgba[i] *= state->tint.rgba[i]; + paint.outerColor.rgba[i] *= state->tint.rgba[i]; + } ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth); diff --git a/dgl/src/nanovg/nanovg.h b/dgl/src/nanovg/nanovg.h index a2abfe97..962cf787 100644 --- a/dgl/src/nanovg/nanovg.h +++ b/dgl/src/nanovg/nanovg.h @@ -279,6 +279,10 @@ void nvgLineJoin(NVGcontext* ctx, int join); // Sets the transparency applied to all rendered shapes. // Already transparent paths will get proportionally more transparent as well. void nvgGlobalAlpha(NVGcontext* ctx, float alpha); +void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint); +NVGcolor nvgGetGlobalTint(NVGcontext* ctx); +void nvgAlpha(NVGcontext* ctx, float alpha); +void nvgTint(NVGcontext* ctx, NVGcolor tint); // // Transforms @@ -385,7 +389,7 @@ int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int // 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); +int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, enum NVGtexture format, const unsigned char* data); // Creates image from specified image data. // Returns handle to the image. From c17c260d08613ab46e13dc578104c74b5713a435 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 8 Oct 2021 01:49:08 +0100 Subject: [PATCH 177/504] Allow to skip building NanoVG integrated code --- dgl/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index bac32fc9..87f17eab 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -62,8 +62,11 @@ endif # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl = $(OBJS_common) \ - ../build/dgl/OpenGL.cpp.opengl.o \ - ../build/dgl/NanoVG.cpp.opengl.o + ../build/dgl/OpenGL.cpp.opengl.o + +ifneq ($(SKIP_NANOVG),true) +OBJS_opengl += ../build/dgl/NanoVG.cpp.opengl.o +endif ifeq ($(MACOS),true) OBJS_opengl += ../build/dgl/pugl.mm.opengl.o From a07aaf986f396bf5b014add962b5f38041121fdf Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 8 Oct 2021 23:07:54 +0100 Subject: [PATCH 178/504] Allow DGL_USE_RGBA macro, will setup 24 depth bits and glx RGBA Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 4 ++++ dgl/src/pugl-upstream | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 15a1d280..27bee506 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -249,7 +249,11 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo puglSetHandle(view, this); puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); +#if DGL_USE_RGBA + puglSetViewHint(view, PUGL_DEPTH_BITS, 24); +#else puglSetViewHint(view, PUGL_DEPTH_BITS, 16); +#endif puglSetViewHint(view, PUGL_STENCIL_BITS, 8); #ifdef DGL_USE_OPENGL3 puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 0fdc1905..b63bafa9 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 0fdc19059de5214973ae6ec0d775470f94ceb1c9 +Subproject commit b63bafa9cb5813729a928a6f364c997ff4e3bd6a From 299e50f826bfb91c005f52bf9ada7cd69c3f3940 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Oct 2021 00:38:28 +0100 Subject: [PATCH 179/504] Stick with NanoVG, use USE_NANOVG_FBO macro instead Signed-off-by: falkTX --- dgl/Makefile | 10 +++++----- dgl/src/NanoVG.cpp | 6 +++++- dgl/src/nanovg/nanovg.c | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 87f17eab..c1a1df1b 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -16,6 +16,9 @@ LINK_FLAGS += $(DGL_LIBS) ifeq ($(USE_OPENGL3),true) BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 endif +ifneq ($(USE_NANOVG_FBO),true) +BUILD_CXX_FLAGS += -DDGL_USE_NANOVG_FBO +endif # TODO fix these after pugl-upstream is done BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers @@ -62,11 +65,8 @@ endif # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl = $(OBJS_common) \ - ../build/dgl/OpenGL.cpp.opengl.o - -ifneq ($(SKIP_NANOVG),true) -OBJS_opengl += ../build/dgl/NanoVG.cpp.opengl.o -endif + ../build/dgl/OpenGL.cpp.opengl.o \ + ../build/dgl/NanoVG.cpp.opengl.o ifeq ($(MACOS),true) OBJS_opengl += ../build/dgl/pugl.mm.opengl.o diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index aa252749..2c37a218 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -83,7 +83,11 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) #endif #include "nanovg/nanovg_gl.h" -#include "nanovg/nanovg_gl_utils.h" + +#ifdef DGL_USE_NANOVG_FBO +# define NANOVG_FBO_VALID 1 +# include "nanovg/nanovg_gl_utils.h" +#endif #if defined(NANOVG_GL2) # define nvgCreateGL nvgCreateGL2 diff --git a/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index 42a1b866..3eb6d63a 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/dgl/src/nanovg/nanovg.c @@ -698,7 +698,8 @@ void nvgLineJoin(NVGcontext* ctx, int join) void nvgGlobalAlpha(NVGcontext* ctx, float alpha) { - nvgGlobalTint(ctx, nvgRGBAf(1, 1, 1, alpha)); + NVGstate* state = nvg__getState(ctx); + state->tint.a = alpha; } void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint) From 117e0b30eba69860f00c5553f9302168de2e4f0f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Oct 2021 00:48:13 +0100 Subject: [PATCH 180/504] Fix previous commit, add missing USE_RGBA check Signed-off-by: falkTX --- dgl/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dgl/Makefile b/dgl/Makefile index c1a1df1b..e570a949 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -16,9 +16,12 @@ LINK_FLAGS += $(DGL_LIBS) ifeq ($(USE_OPENGL3),true) BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 endif -ifneq ($(USE_NANOVG_FBO),true) +ifeq ($(USE_NANOVG_FBO),true) BUILD_CXX_FLAGS += -DDGL_USE_NANOVG_FBO endif +ifeq ($(USE_RGBA),true) +BUILD_CXX_FLAGS += -DDGL_USE_RGBA +endif # TODO fix these after pugl-upstream is done BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers From d8f66c1e06cafedf000fe75afcf67b96b3efbc6c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Oct 2021 01:05:01 +0100 Subject: [PATCH 181/504] Use the correct pugl branch Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index b63bafa9..9c2c093b 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit b63bafa9cb5813729a928a6f364c997ff4e3bd6a +Subproject commit 9c2c093b9157a8581de878b9ac3dba3f6b213b16 From a1322bc0c5814633ec3c82080540a2657f4f4486 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Oct 2021 03:15:38 +0100 Subject: [PATCH 182/504] Update NanoVG and stb-image sources, bring all VCV goodies too Signed-off-by: falkTX --- dgl/NanoVG.hpp | 1 + dgl/src/nanovg/nanovg.c | 242 ++-- dgl/src/nanovg/nanovg.h | 6 +- dgl/src/nanovg/nanovg_gl.h | 103 +- dgl/src/nanovg/stb_image.h | 2492 +++++++++++------------------------- 5 files changed, 944 insertions(+), 1900 deletions(-) diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index af33ab87..e7f96f13 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -943,6 +943,7 @@ private: inline void onDisplay() override { // NOTE maybe should use BaseWidget::getWindow().getScaleFactor() as 3rd arg ? + NanoVG::reset(); NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight()); onNanoDisplay(); NanoVG::endFrame(); diff --git a/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index 3eb6d63a..3f9c0b4b 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/dgl/src/nanovg/nanovg.c @@ -24,8 +24,11 @@ #include "nanovg.h" #define FONTSTASH_IMPLEMENTATION #include "fontstash.h" + +#ifndef NVG_NO_STB #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" +#endif #ifdef _MSC_VER #pragma warning(disable: 4100) // unreferenced formal parameter @@ -109,6 +112,14 @@ struct NVGpathCache { }; typedef struct NVGpathCache NVGpathCache; +struct NVGfontContext { // Fontstash context plus font images; shared between shared NanoVG contexts. + int refCount; + struct FONScontext* fs; + int fontImages[NVG_MAX_FONTIMAGES]; + int fontImageIdx; +}; +typedef struct NVGfontContext NVGfontContext; + struct NVGcontext { NVGparams params; float* commands; @@ -122,9 +133,7 @@ struct NVGcontext { float distTol; float fringeWidth; float devicePxRatio; - struct FONScontext* fs; - int fontImages[NVG_MAX_FONTIMAGES]; - int fontImageIdx; + NVGfontContext* fontContext; int drawCallCount; int fillTriCount; int strokeTriCount; @@ -283,7 +292,7 @@ static NVGstate* nvg__getState(NVGcontext* ctx) return &ctx->states[ctx->nstates-1]; } -NVGcontext* nvgCreateInternal(NVGparams* params) +NVGcontext* nvgCreateInternal(NVGparams* params, NVGcontext* other) // Share the fonts and images of 'other' if it's non-NULL. { FONSparams fontParams; NVGcontext* ctx = (NVGcontext*)malloc(sizeof(NVGcontext)); @@ -292,8 +301,16 @@ NVGcontext* nvgCreateInternal(NVGparams* params) memset(ctx, 0, sizeof(NVGcontext)); ctx->params = *params; - for (i = 0; i < NVG_MAX_FONTIMAGES; i++) - ctx->fontImages[i] = 0; + if (other) { + ctx->fontContext = other->fontContext; + ctx->fontContext->refCount++; + } else { + ctx->fontContext = (NVGfontContext*)malloc(sizeof(NVGfontContext)); + if (ctx->fontContext == NULL) goto error; + for (i = 0; i < NVG_MAX_FONTIMAGES; i++) + ctx->fontContext->fontImages[i] = 0; + ctx->fontContext->refCount = 1; + } ctx->commands = (float*)malloc(sizeof(float)*NVG_INIT_COMMANDS_SIZE); if (!ctx->commands) goto error; @@ -308,25 +325,27 @@ NVGcontext* nvgCreateInternal(NVGparams* params) nvg__setDevicePixelRatio(ctx, 1.0f); - if (ctx->params.renderCreate(ctx->params.userPtr) == 0) goto error; + if (ctx->params.renderCreate(ctx->params.userPtr, other ? other->params.userPtr : NULL) == 0) goto error; // Init font rendering - memset(&fontParams, 0, sizeof(fontParams)); - fontParams.width = NVG_INIT_FONTIMAGE_SIZE; - fontParams.height = NVG_INIT_FONTIMAGE_SIZE; - fontParams.flags = FONS_ZERO_TOPLEFT; - fontParams.renderCreate = NULL; - fontParams.renderUpdate = NULL; - fontParams.renderDraw = NULL; - fontParams.renderDelete = NULL; - fontParams.userPtr = NULL; - ctx->fs = fonsCreateInternal(&fontParams); - if (ctx->fs == NULL) goto error; - - // Create font texture - ctx->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, fontParams.width, fontParams.height, 0, NULL); - if (ctx->fontImages[0] == 0) goto error; - ctx->fontImageIdx = 0; + if (!other) { + memset(&fontParams, 0, sizeof(fontParams)); + fontParams.width = NVG_INIT_FONTIMAGE_SIZE; + fontParams.height = NVG_INIT_FONTIMAGE_SIZE; + fontParams.flags = FONS_ZERO_TOPLEFT; + fontParams.renderCreate = NULL; + fontParams.renderUpdate = NULL; + fontParams.renderDraw = NULL; + fontParams.renderDelete = NULL; + fontParams.userPtr = NULL; + ctx->fontContext->fs = fonsCreateInternal(&fontParams); + if (ctx->fontContext->fs == NULL) goto error; + + // Create font texture + ctx->fontContext->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, fontParams.width, fontParams.height, 0, NULL); + if (ctx->fontContext->fontImages[0] == 0) goto error; + ctx->fontContext->fontImageIdx = 0; + } return ctx; @@ -347,14 +366,18 @@ void nvgDeleteInternal(NVGcontext* ctx) if (ctx->commands != NULL) free(ctx->commands); if (ctx->cache != NULL) nvg__deletePathCache(ctx->cache); - if (ctx->fs) - fonsDeleteInternal(ctx->fs); + if (ctx->fontContext != NULL && --ctx->fontContext->refCount == 0) { + if (ctx->fontContext->fs) + fonsDeleteInternal(ctx->fontContext->fs); - for (i = 0; i < NVG_MAX_FONTIMAGES; i++) { - if (ctx->fontImages[i] != 0) { - nvgDeleteImage(ctx, ctx->fontImages[i]); - ctx->fontImages[i] = 0; + for (i = 0; i < NVG_MAX_FONTIMAGES; i++) { + if (ctx->fontContext->fontImages[i] != 0) { + nvgDeleteImage(ctx, ctx->fontContext->fontImages[i]); + ctx->fontContext->fontImages[i] = 0; + } } + + free(ctx->fontContext); } if (ctx->params.renderDelete != NULL) @@ -391,30 +414,30 @@ void nvgCancelFrame(NVGcontext* ctx) void nvgEndFrame(NVGcontext* ctx) { ctx->params.renderFlush(ctx->params.userPtr); - if (ctx->fontImageIdx != 0) { - int fontImage = ctx->fontImages[ctx->fontImageIdx]; + if (ctx->fontContext->fontImageIdx != 0) { + int fontImage = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx]; int i, j, iw, ih; // delete images that smaller than current one if (fontImage == 0) return; nvgImageSize(ctx, fontImage, &iw, &ih); - for (i = j = 0; i < ctx->fontImageIdx; i++) { - if (ctx->fontImages[i] != 0) { + for (i = j = 0; i < ctx->fontContext->fontImageIdx; i++) { + if (ctx->fontContext->fontImages[i] != 0) { int nw, nh; - nvgImageSize(ctx, ctx->fontImages[i], &nw, &nh); + nvgImageSize(ctx, ctx->fontContext->fontImages[i], &nw, &nh); if (nw < iw || nh < ih) - nvgDeleteImage(ctx, ctx->fontImages[i]); + nvgDeleteImage(ctx, ctx->fontContext->fontImages[i]); else - ctx->fontImages[j++] = ctx->fontImages[i]; + ctx->fontContext->fontImages[j++] = ctx->fontContext->fontImages[i]; } } // make current font image to first - ctx->fontImages[j++] = ctx->fontImages[0]; - ctx->fontImages[0] = fontImage; - ctx->fontImageIdx = 0; + ctx->fontContext->fontImages[j++] = ctx->fontContext->fontImages[0]; + ctx->fontContext->fontImages[0] = fontImage; + ctx->fontContext->fontImageIdx = 0; // clear all images after j for (i = j; i < NVG_MAX_FONTIMAGES; i++) - ctx->fontImages[i] = 0; + ctx->fontContext->fontImages[i] = 0; } } @@ -814,6 +837,7 @@ void nvgFillPaint(NVGcontext* ctx, NVGpaint paint) nvgTransformMultiply(state->fill.xform, state->xform); } +#ifndef NVG_NO_STB int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) { int w, h, n, image; @@ -842,6 +866,7 @@ int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int stbi_image_free(img); return image; } +#endif int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, NVGtexture format, const unsigned char* data) { @@ -2324,35 +2349,35 @@ void nvgStroke(NVGcontext* ctx) // Add fonts int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename) { - return fonsAddFont(ctx->fs, name, filename, 0); + return fonsAddFont(ctx->fontContext->fs, name, filename, 0); } int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex) { - return fonsAddFont(ctx->fs, name, filename, fontIndex); + return fonsAddFont(ctx->fontContext->fs, name, filename, fontIndex); } int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData) { - return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0); + return fonsAddFontMem(ctx->fontContext->fs, name, data, ndata, freeData, 0); } int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex) { - return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex); + return fonsAddFontMem(ctx->fontContext->fs, name, data, ndata, freeData, fontIndex); } int nvgFindFont(NVGcontext* ctx, const char* name) { if (name == NULL) return -1; - return fonsGetFontByName(ctx->fs, name); + return fonsGetFontByName(ctx->fontContext->fs, name); } int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont) { if(baseFont == -1 || fallbackFont == -1) return 0; - return fonsAddFallbackFont(ctx->fs, baseFont, fallbackFont); + return fonsAddFallbackFont(ctx->fontContext->fs, baseFont, fallbackFont); } int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont) @@ -2362,7 +2387,7 @@ int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallba void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont) { - fonsResetFallbackFont(ctx->fs, baseFont); + fonsResetFallbackFont(ctx->fontContext->fs, baseFont); } void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont) @@ -2410,7 +2435,7 @@ void nvgFontFaceId(NVGcontext* ctx, int font) void nvgFontFace(NVGcontext* ctx, const char* font) { NVGstate* state = nvg__getState(ctx); - state->fontId = fonsGetFontByName(ctx->fs, font); + state->fontId = fonsGetFontByName(ctx->fontContext->fs, font); } static float nvg__quantize(float a, float d) @@ -2427,12 +2452,12 @@ static void nvg__flushTextTexture(NVGcontext* ctx) { int dirty[4]; - if (fonsValidateTexture(ctx->fs, dirty)) { - int fontImage = ctx->fontImages[ctx->fontImageIdx]; + if (fonsValidateTexture(ctx->fontContext->fs, dirty)) { + int fontImage = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx]; // Update texture if (fontImage != 0) { int iw, ih; - const unsigned char* data = fonsGetTextureData(ctx->fs, &iw, &ih); + const unsigned char* data = fonsGetTextureData(ctx->fontContext->fs, &iw, &ih); int x = dirty[0]; int y = dirty[1]; int w = dirty[2] - dirty[0]; @@ -2446,23 +2471,23 @@ static int nvg__allocTextAtlas(NVGcontext* ctx) { int iw, ih; nvg__flushTextTexture(ctx); - if (ctx->fontImageIdx >= NVG_MAX_FONTIMAGES-1) + if (ctx->fontContext->fontImageIdx >= NVG_MAX_FONTIMAGES-1) return 0; // if next fontImage already have a texture - if (ctx->fontImages[ctx->fontImageIdx+1] != 0) - nvgImageSize(ctx, ctx->fontImages[ctx->fontImageIdx+1], &iw, &ih); + if (ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1] != 0) + nvgImageSize(ctx, ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1], &iw, &ih); else { // calculate the new font image size and create it. - nvgImageSize(ctx, ctx->fontImages[ctx->fontImageIdx], &iw, &ih); + nvgImageSize(ctx, ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx], &iw, &ih); if (iw > ih) ih *= 2; else iw *= 2; if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE) iw = ih = NVG_MAX_FONTIMAGE_SIZE; - ctx->fontImages[ctx->fontImageIdx+1] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, iw, ih, 0, NULL); + ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, iw, ih, 0, NULL); } - ++ctx->fontImageIdx; - fonsResetAtlas(ctx->fs, iw, ih); + ++ctx->fontContext->fontImageIdx; + fonsResetAtlas(ctx->fontContext->fs, iw, ih); return 1; } @@ -2473,7 +2498,7 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) NVGpaint paint = state->fill; // Render triangles. - paint.image = ctx->fontImages[ctx->fontImageIdx]; + paint.image = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx]; // Apply global tint for (i = 0; i < 4; i++) { @@ -2487,6 +2512,12 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) ctx->textTriCount += nverts/3; } +static int nvg__isTransformFlipped(const float *xform) +{ + float det = xform[0] * xform[3] - xform[2] * xform[1]; + return( det < 0); +} + float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end) { NVGstate* state = nvg__getState(ctx); @@ -2497,25 +2528,26 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* float invscale = 1.0f / scale; int cverts = 0; int nverts = 0; + int isFlipped = nvg__isTransformFlipped(state->xform); if (end == NULL) end = string + strlen(string); if (state->fontId == FONS_INVALID) return x; - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); + fonsSetSize(ctx->fontContext->fs, state->fontSize*scale); + fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale); + fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale); + fonsSetAlign(ctx->fontContext->fs, state->textAlign); + fonsSetFont(ctx->fontContext->fs, state->fontId); cverts = nvg__maxi(2, (int)(end - string)) * 6; // conservative estimate. verts = nvg__allocTempVerts(ctx, cverts); if (verts == NULL) return x; - fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED); + fonsTextIterInit(ctx->fontContext->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED); prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { + while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) { float c[4*2]; if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? if (nverts != 0) { @@ -2525,11 +2557,17 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* if (!nvg__allocTextAtlas(ctx)) break; // no memory :( iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again + fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again if (iter.prevGlyphIndex == -1) // still can not find glyph? break; } prevIter = iter; + if(isFlipped) { + float tmp; + + tmp = q.y0; q.y0 = q.y1; q.y1 = tmp; + tmp = q.t0; q.t0 = q.t1; q.t1 = tmp; + } // Transform corners. nvgTransformPoint(&c[0],&c[1], state->xform, q.x0*invscale, q.y0*invscale); nvgTransformPoint(&c[2],&c[3], state->xform, q.x1*invscale, q.y0*invscale); @@ -2604,18 +2642,18 @@ int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, if (string == end) return 0; - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); + fonsSetSize(ctx->fontContext->fs, state->fontSize*scale); + fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale); + fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale); + fonsSetAlign(ctx->fontContext->fs, state->textAlign); + fonsSetFont(ctx->fontContext->fs, state->fontId); - fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL); + fonsTextIterInit(ctx->fontContext->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL); prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { + while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) { if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again + fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again } prevIter = iter; positions[npos].str = iter.str; @@ -2668,20 +2706,20 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa if (string == end) return 0; - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); + fonsSetSize(ctx->fontContext->fs, state->fontSize*scale); + fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale); + fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale); + fonsSetAlign(ctx->fontContext->fs, state->textAlign); + fonsSetFont(ctx->fontContext->fs, state->fontId); breakRowWidth *= scale; - fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL); + fonsTextIterInit(ctx->fontContext->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL); prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { + while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) { if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again + fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again } prevIter = iter; switch (iter.codepoint) { @@ -2852,16 +2890,16 @@ float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const if (state->fontId == FONS_INVALID) return 0; - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); + fonsSetSize(ctx->fontContext->fs, state->fontSize*scale); + fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale); + fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale); + fonsSetAlign(ctx->fontContext->fs, state->textAlign); + fonsSetFont(ctx->fontContext->fs, state->fontId); - width = fonsTextBounds(ctx->fs, x*scale, y*scale, string, end, bounds); + width = fonsTextBounds(ctx->fontContext->fs, x*scale, y*scale, string, end, bounds); if (bounds != NULL) { // Use line bounds for height. - fonsLineBounds(ctx->fs, y*scale, &bounds[1], &bounds[3]); + fonsLineBounds(ctx->fontContext->fs, y*scale, &bounds[1], &bounds[3]); bounds[0] *= invscale; bounds[1] *= invscale; bounds[2] *= invscale; @@ -2896,12 +2934,12 @@ void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, co minx = maxx = x; miny = maxy = y; - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - fonsLineBounds(ctx->fs, 0, &rminy, &rmaxy); + fonsSetSize(ctx->fontContext->fs, state->fontSize*scale); + fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale); + fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale); + fonsSetAlign(ctx->fontContext->fs, state->textAlign); + fonsSetFont(ctx->fontContext->fs, state->fontId); + fonsLineBounds(ctx->fontContext->fs, 0, &rminy, &rmaxy); rminy *= invscale; rmaxy *= invscale; @@ -2947,13 +2985,13 @@ void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* l if (state->fontId == FONS_INVALID) return; - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); + fonsSetSize(ctx->fontContext->fs, state->fontSize*scale); + fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale); + fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale); + fonsSetAlign(ctx->fontContext->fs, state->textAlign); + fonsSetFont(ctx->fontContext->fs, state->fontId); - fonsVertMetrics(ctx->fs, ascender, descender, lineh); + fonsVertMetrics(ctx->fontContext->fs, ascender, descender, lineh); if (ascender != NULL) *ascender *= invscale; if (descender != NULL) diff --git a/dgl/src/nanovg/nanovg.h b/dgl/src/nanovg/nanovg.h index 962cf787..89b4dd80 100644 --- a/dgl/src/nanovg/nanovg.h +++ b/dgl/src/nanovg/nanovg.h @@ -430,7 +430,7 @@ NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, NVGcolor icol, NVGcolor ocol); -// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, +// Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, @@ -671,7 +671,7 @@ typedef struct NVGpath NVGpath; struct NVGparams { void* userPtr; int edgeAntiAlias; - int (*renderCreate)(void* uptr); + int (*renderCreate)(void* uptr, void* otherUptr); int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); int (*renderDeleteTexture)(void* uptr, int image); int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); @@ -687,7 +687,7 @@ struct NVGparams { typedef struct NVGparams NVGparams; // Constructor and destructor, called by the render back-end. -NVGcontext* nvgCreateInternal(NVGparams* params); +NVGcontext* nvgCreateInternal(NVGparams* params, NVGcontext* other); void nvgDeleteInternal(NVGcontext* ctx); NVGparams* nvgInternalParams(NVGcontext* ctx); diff --git a/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index fd4b4165..cdf0901f 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/dgl/src/nanovg/nanovg_gl.h @@ -40,9 +40,7 @@ enum NVGcreateFlags { #elif defined NANOVG_GL3_IMPLEMENTATION # define NANOVG_GL3 1 # define NANOVG_GL_IMPLEMENTATION 1 -# ifndef __APPLE__ -# define NANOVG_GL_USE_UNIFORMBUFFER 1 -# endif +# define NANOVG_GL_USE_UNIFORMBUFFER 1 #elif defined NANOVG_GLES2_IMPLEMENTATION # define NANOVG_GLES2 1 # define NANOVG_GL_IMPLEMENTATION 1 @@ -59,6 +57,7 @@ enum NVGcreateFlags { #if defined NANOVG_GL2 NVGcontext* nvgCreateGL2(int flags); +NVGcontext* nvgCreateSharedGL2(NVGcontext* other, int flags); void nvgDeleteGL2(NVGcontext* ctx); int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); @@ -69,6 +68,7 @@ GLuint nvglImageHandleGL2(NVGcontext* ctx, int image); #if defined NANOVG_GL3 NVGcontext* nvgCreateGL3(int flags); +NVGcontext* nvgCreateSharedGL3(NVGcontext* other, int flags); void nvgDeleteGL3(NVGcontext* ctx); int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); @@ -79,6 +79,7 @@ GLuint nvglImageHandleGL3(NVGcontext* ctx, int image); #if defined NANOVG_GLES2 NVGcontext* nvgCreateGLES2(int flags); +NVGcontext* nvgCreateSharedGLES2(NVGcontext* other, int flags); void nvgDeleteGLES2(NVGcontext* ctx); int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); @@ -89,6 +90,7 @@ GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image); #if defined NANOVG_GLES3 NVGcontext* nvgCreateGLES3(int flags); +NVGcontext* nvgCreateSharedGLES3(NVGcontext* other, int flags); void nvgDeleteGLES3(NVGcontext* ctx); int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); @@ -230,9 +232,18 @@ struct GLNVGfragUniforms { }; typedef struct GLNVGfragUniforms GLNVGfragUniforms; +struct GLNVGtextureContext { // Textures; shared between shared NanoVG contexts. + int refCount; + GLNVGtexture* textures; + int ntextures; + int ctextures; + int textureId; +}; +typedef struct GLNVGtextureContext GLNVGtextureContext; + struct GLNVGcontext { GLNVGshader shader; - GLNVGtexture* textures; + GLNVGtextureContext* textureContext; float view[2]; int ntextures; int ctextures; @@ -352,26 +363,26 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) GLNVGtexture* tex = NULL; int i; - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].id == 0) { - tex = &gl->textures[i]; + for (i = 0; i < gl->textureContext->ntextures; i++) { + if (gl->textureContext->textures[i].id == 0) { + tex = &gl->textureContext->textures[i]; break; } } if (tex == NULL) { - if (gl->ntextures+1 > gl->ctextures) { + if (gl->textureContext->ntextures+1 > gl->textureContext->ctextures) { GLNVGtexture* textures; - int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate - textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures); + int ctextures = glnvg__maxi(gl->textureContext->ntextures+1, 4) + gl->textureContext->ctextures/2; // 1.5x Overallocate + textures = (GLNVGtexture*)realloc(gl->textureContext->textures, sizeof(GLNVGtexture)*ctextures); if (textures == NULL) return NULL; - gl->textures = textures; - gl->ctextures = ctextures; + gl->textureContext->textures = textures; + gl->textureContext->ctextures = ctextures; } - tex = &gl->textures[gl->ntextures++]; + tex = &gl->textureContext->textures[gl->textureContext->ntextures++]; } memset(tex, 0, sizeof(*tex)); - tex->id = ++gl->textureId; + tex->id = ++gl->textureContext->textureId; return tex; } @@ -379,20 +390,20 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id) { int i; - for (i = 0; i < gl->ntextures; i++) - if (gl->textures[i].id == id) - return &gl->textures[i]; + for (i = 0; i < gl->textureContext->ntextures; i++) + if (gl->textureContext->textures[i].id == id) + return &gl->textureContext->textures[i]; return NULL; } static int glnvg__deleteTexture(GLNVGcontext* gl, int id) { int i; - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].id == id) { - if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) - glDeleteTextures(1, &gl->textures[i].tex); - memset(&gl->textures[i], 0, sizeof(gl->textures[i])); + for (i = 0; i < gl->textureContext->ntextures; i++) { + if (gl->textureContext->textures[i].id == id) { + if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) + glDeleteTextures(1, &gl->textureContext->textures[i].tex); + memset(&gl->textureContext->textures[i], 0, sizeof(gl->textureContext->textures[i])); return 1; } } @@ -506,9 +517,20 @@ static void glnvg__getUniforms(GLNVGshader* shader) static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); -static int glnvg__renderCreate(void* uptr) +static int glnvg__renderCreate(void* uptr, void* otherUptr) // Share the textures of GLNVGcontext 'otherUptr' if it's non-NULL. { GLNVGcontext* gl = (GLNVGcontext*)uptr; + + if (otherUptr) { + GLNVGcontext* other = (GLNVGcontext*)otherUptr; + gl->textureContext = other->textureContext; + gl->textureContext->refCount++; + } else { + gl->textureContext = (GLNVGtextureContext*)malloc(sizeof(GLNVGtextureContext)); + memset(gl->textureContext, 0, sizeof(GLNVGtextureContext)); + gl->textureContext->refCount = 1; + } + int align = 4; // TODO: mediump float may not be enough for GLES2 in iOS. @@ -1605,11 +1627,14 @@ static void glnvg__renderDelete(void* uptr) if (gl->vertBuf != 0) glDeleteBuffers(1, &gl->vertBuf); - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) - glDeleteTextures(1, &gl->textures[i].tex); + if (gl->textureContext != NULL && --gl->textureContext->refCount == 0) { + for (i = 0; i < gl->textureContext->ntextures; i++) { + if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) + glDeleteTextures(1, &gl->textureContext->textures[i].tex); + } + free(gl->textureContext->textures); + free(gl->textureContext); } - free(gl->textures); free(gl->paths); free(gl->verts); @@ -1629,6 +1654,28 @@ NVGcontext* nvgCreateGLES2(int flags) #elif defined NANOVG_GLES3 NVGcontext* nvgCreateGLES3(int flags) #endif +{ +#if defined NANOVG_GL2 + return nvgCreateSharedGL2(NULL, flags); +#elif defined NANOVG_GL3 + return nvgCreateSharedGL3(NULL, flags); +#elif defined NANOVG_GLES2 + return nvgCreateSharedGLES2(NULL, flags); +#elif defined NANOVG_GLES3 + return nvgCreateSharedGLES3(NULL, flags); +#endif +} + +// Share the fonts and textures of 'other' if it's non-NULL. +#if defined NANOVG_GL2 +NVGcontext* nvgCreateSharedGL2(NVGcontext* other, int flags) +#elif defined NANOVG_GL3 +NVGcontext* nvgCreateSharedGL3(NVGcontext* other, int flags) +#elif defined NANOVG_GLES2 +NVGcontext* nvgCreateSharedGLES2(NVGcontext* other, int flags) +#elif defined NANOVG_GLES3 +NVGcontext* nvgCreateSharedGLES3(NVGcontext* other, int flags) +#endif { NVGparams params; NVGcontext* ctx = NULL; @@ -1654,7 +1701,7 @@ NVGcontext* nvgCreateGLES3(int flags) gl->flags = flags; - ctx = nvgCreateInternal(¶ms); + ctx = nvgCreateInternal(¶ms, other); if (ctx == NULL) goto error; return ctx; diff --git a/dgl/src/nanovg/stb_image.h b/dgl/src/nanovg/stb_image.h index 2857f05d..e06f7a1d 100644 --- a/dgl/src/nanovg/stb_image.h +++ b/dgl/src/nanovg/stb_image.h @@ -1,5 +1,5 @@ -/* stb_image - v2.25 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk +/* stb_image - v2.10 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION @@ -21,7 +21,7 @@ avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE @@ -42,32 +42,135 @@ Full documentation under "DOCUMENTATION" below. -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) + optimize PNG + fix bug in interlaced PNG with user-specified channel count See end of file for full revision history. @@ -82,33 +185,34 @@ RECENT REVISION HISTORY: Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + urraka@github (animated gif) Junggon Kim (PNM comments) + Daniel Gibson (16-bit TGA) + + Optimizations & bugfixes + Fabian "ryg" Giesen Arseny Kapoulkine - John-Mark Allen - Carmelo J Fdez-Aguera Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt JR Smith github:darealshinji - Brad Weinberger Matvey Cherevko github:Michaelangel007 - Blazej Dariusz Roszkowski Alexander Veselov + Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson + Dave Moore Roy Eltham Hayaki Saito Phil Jordan + Won Chun Luke Graham Johan Duparc Nathan Reed + the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis + Janez Zemva John Bartholomew Michal Cichon svdijk@github + Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson + Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github + Aruelien Pocheville Thibault Reuille Cass Everitt + Ryamond Barbiero Paul Du Bois Engin Manap + Blazej Dariusz Roszkowski + Michaelangel007@github + + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -117,8 +221,10 @@ RECENT REVISION HISTORY: // DOCUMENTATION // // Limitations: +// - no 16-bit-per-channel PNG // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding +// - no 1-bit BMP // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): @@ -131,10 +237,10 @@ RECENT REVISION HISTORY: // stbi_image_free(data) // // Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is @@ -142,12 +248,11 @@ RECENT REVISION HISTORY: // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: @@ -159,26 +264,16 @@ RECENT REVISION HISTORY: // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // // =========================================================================== // -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// // Philosophy // // stb libraries are designed with the following priorities: @@ -189,15 +284,15 @@ RECENT REVISION HISTORY: // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// performance, in addition to the easy to use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. +// make more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") +// - Small footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== @@ -229,6 +324,13 @@ RECENT REVISION HISTORY: // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. @@ -237,10 +339,11 @@ RECENT REVISION HISTORY: // // HDR image support (disable by defining STBI_NO_HDR) // -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); @@ -274,7 +377,7 @@ RECENT REVISION HISTORY: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // @@ -283,41 +386,6 @@ RECENT REVISION HISTORY: // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// #ifndef STBI_NO_STDIO @@ -328,7 +396,7 @@ RECENT REVISION HISTORY: enum { - STBI_default = 0, // only used for desired_channels + STBI_default = 0, // only used for req_comp STBI_grey = 1, STBI_grey_alpha = 2, @@ -336,21 +404,17 @@ enum STBI_rgb_alpha = 4 }; -#include typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { #endif -#ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif -#endif ////////////////////////////////////////////////////////////////////////////// // @@ -368,52 +432,22 @@ typedef struct int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// #ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); #endif #endif @@ -437,7 +471,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f); // get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe +// NOT THREADSAFE STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() @@ -446,14 +480,11 @@ STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + #endif @@ -470,11 +501,6 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); @@ -539,10 +565,9 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include // ptrdiff_t on osx #include #include -#include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow +#include // ldexp #endif #ifndef STBI_NO_STDIO @@ -554,12 +579,6 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define STBI_ASSERT(x) assert(x) #endif -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - #ifndef _MSC_VER #ifdef __cplusplus @@ -571,17 +590,6 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define stbi_inline __forceinline #endif -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - #define STBI_THREAD_LOCAL _Thread_local - #elif defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) -#endif -#endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; @@ -640,14 +648,12 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI__X86_TARGET #endif -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? // gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) #define STBI_NO_SIMD #endif @@ -666,7 +672,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI_NO_SIMD #endif -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) #define STBI_SSE2 #include @@ -695,27 +701,25 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) +static int stbi__sse2_available() { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } -#endif - #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) +static int stbi__sse2_available() { - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; #endif - +} #endif #endif @@ -822,179 +826,79 @@ static void stbi__rewind(stbi__context *s) s->img_buffer_end = s->img_buffer_original_end; } -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; +// this is not threadsafe +static const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } -#ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } -#endif static void *stbi__malloc(size_t size) { return STBI_MALLOC(size); } -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -1023,63 +927,40 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static int stbi__vertically_flip_on_load_global = 0; +static int stbi__vertically_flip_on_load = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; + stbi__vertically_flip_on_load = flag_true_if_should_flip; } -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif @@ -1087,175 +968,66 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); + return stbi__tga_load(s,x,y,comp,req_comp); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) { - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } } } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; + return result; } -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +#ifndef STBI_NO_HDR static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } } } #endif #ifndef STBI_NO_STDIO -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) - return 0; - -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else @@ -1280,83 +1052,28 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req unsigned char *result; stbi__context s; stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + result = stbi__load_flip(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - #endif //!STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + return stbi__load_flip(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; + return stbi__load_flip(&s,x,y,comp,req_comp); } -#endif #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) @@ -1364,14 +1081,13 @@ static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + data = stbi__load_flip(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); @@ -1441,16 +1157,12 @@ STBIDEF int stbi_is_hdr (char const *filename) return result; } -STBIDEF int stbi_is_hdr_from_file(FILE *f) +STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; stbi__context s; stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; + return stbi__hdr_test(&s); #else STBI_NOTUSED(f); return 0; @@ -1523,9 +1235,6 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) return 0; } -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { @@ -1537,11 +1246,7 @@ stbi_inline static int stbi__at_eof(stbi__context *s) return s->img_buffer >= s->img_buffer_end; } -#endif -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else static void stbi__skip(stbi__context *s, int n) { if (n < 0) { @@ -1558,11 +1263,7 @@ static void stbi__skip(stbi__context *s, int n) } s->img_buffer += n; } -#endif -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { @@ -1586,27 +1287,18 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) } else return 0; } -#endif -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } -#endif -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } -#endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing @@ -1628,9 +1320,7 @@ static stbi__uint32 stbi__get32le(stbi__context *s) #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else + ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp @@ -1646,11 +1336,7 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } -#endif -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1659,7 +1345,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + good = (unsigned char *) stbi__malloc(req_comp * x * y); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); @@ -1669,97 +1355,37 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; default: STBI_ASSERT(0); } - #undef STBI__CASE + #undef CASE } STBI_FREE(data); return good; } -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1767,11 +1393,7 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; } STBI_FREE(data); return output; @@ -1783,9 +1405,7 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1850,7 +1470,7 @@ typedef struct stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; + stbi_uc dequant[4][64]; stbi__int16 fast_ac[4][1 << FAST_BITS]; // sizes for components, interleaved MCUs @@ -1886,9 +1506,6 @@ typedef struct int succ_high; int succ_low; int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; int scan_n, order[4]; int restart_interval, todo; @@ -1901,8 +1518,7 @@ typedef struct static int stbi__build_huffman(stbi__huffman *h, int *count) { - int i,j,k=0; - unsigned int code; + int i,j,k=0,code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) @@ -1918,7 +1534,7 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); @@ -1959,10 +1575,10 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; + if (k < m) k += (-1 << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); } } } @@ -1971,10 +1587,9 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; @@ -1987,7 +1602,7 @@ static void stbi__grow_buffer_unsafe(stbi__jpeg *j) } // (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) @@ -2040,7 +1655,7 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) } // bias[n] = (-1<s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes + x = stbi__get8(j->s); return x; } @@ -2823,7 +2438,7 @@ static void stbi__jpeg_reset(stbi__jpeg *j) j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; @@ -2955,7 +2570,7 @@ static int stbi__parse_entropy_coded_data(stbi__jpeg *z) } } -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) { int i; for (i=0; i < 64; ++i) @@ -2997,14 +2612,13 @@ static int stbi__process_marker(stbi__jpeg *z, int m) L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); + int p = q >> 4; int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; } return L==0; @@ -3037,50 +2651,12 @@ static int stbi__process_marker(stbi__jpeg *z, int m) } return L==0; } - // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); + stbi__skip(z->s, stbi__get16be(z->s)-2); return 1; } - - return stbi__err("unknown marker","Corrupt JPEG"); + return 0; } // after we see SOS @@ -3123,28 +2699,6 @@ static int stbi__process_scan_header(stbi__jpeg *z) return 1; } -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; @@ -3154,7 +2708,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; @@ -3163,12 +2717,11 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - z->rgb = 0; for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return stbi__err("bad component ID","Corrupt JPEG"); q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); @@ -3177,7 +2730,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (scan != STBI__SCAN_load) return 1; - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; @@ -3189,7 +2742,6 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; @@ -3201,27 +2753,28 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; } } @@ -3240,8 +2793,6 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); @@ -3283,15 +2834,12 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (x == 255) { j->marker = stbi__get8(j->s); break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } @@ -3510,9 +3058,38 @@ static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_ return out; } +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; @@ -3521,9 +3098,9 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3537,6 +3114,7 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc out += step; } } +#endif #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) @@ -3655,9 +3233,9 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc cons int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3683,14 +3261,18 @@ static void stbi__setup_jpeg(stbi__jpeg *j) #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } @@ -3698,7 +3280,23 @@ static void stbi__setup_jpeg(stbi__jpeg *j) // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { - stbi__free_jpeg_components(j, j->s->img_n, 0); + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } } typedef struct @@ -3711,16 +3309,9 @@ typedef struct int ypos; // which pre-expansion row we're on } stbi__resample; -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { - int n, decode_n, is_rgb; + int n, decode_n; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp @@ -3730,11 +3321,9 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + n = req_comp ? req_comp : z->s->img_n; - if (z->s->img_n == 3 && n < 3 && !is_rgb) + if (z->s->img_n == 3 && n < 3) decode_n = 1; else decode_n = z->s->img_n; @@ -3744,7 +3333,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp int k; unsigned int i,j; stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + stbi_uc *coutput[4]; stbi__resample res_comp[4]; @@ -3771,7 +3360,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample @@ -3794,39 +3383,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; @@ -3834,70 +3391,37 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp out += n; } } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + if (comp) *comp = z->s->img_n; // report original components, not output return output; } } -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + return load_jpeg_image(&j, x,y,comp,req_comp); } static int stbi__jpeg_test(stbi__context *s) { int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); stbi__rewind(s); - STBI_FREE(j); return r; } @@ -3909,18 +3433,15 @@ static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + if (comp) *comp = j->s->img_n; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; + stbi__jpeg j; + j.s = s; + return stbi__jpeg_info_raw(&j, x, y, comp); } #endif @@ -3966,7 +3487,7 @@ stbi_inline static int stbi__bit_reverse(int v, int bits) return stbi__bitreverse16(v) >> (16-bits); } -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; @@ -4109,18 +3630,18 @@ static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room return 1; } -static const int stbi__zlength_base[31] = { +static int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; -static const int stbi__zlength_extra[31]= +static int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; -static const int stbi__zdist_extra[32] = +static int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) @@ -4167,7 +3688,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) static int stbi__compute_huffman_codes(stbi__zbuf *a) { - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; @@ -4176,7 +3697,6 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { @@ -4186,35 +3706,33 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; - while (n < ntot) { + while (n < hlit + hdist) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) - c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); n += c; } } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } -static int stbi__parse_uncompressed_block(stbi__zbuf *a) +static int stbi__parse_uncomperssed_block(stbi__zbuf *a) { stbi_uc header[4]; int len,nlen,k; @@ -4252,28 +3770,13 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; @@ -4283,7 +3786,6 @@ Init algorithm: for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } -*/ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { @@ -4296,12 +3798,13 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; + if (!stbi__parse_uncomperssed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { @@ -4426,7 +3929,7 @@ static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) static int stbi__check_png_header(stbi__context *s) { - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); @@ -4437,7 +3940,6 @@ typedef struct { stbi__context *s; stbi_uc *idata, *expanded, *out; - int depth; } stbi__png; @@ -4472,40 +3974,35 @@ static int stbi__paeth(int a, int b, int c) return c; } -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { - int bytes = (depth == 16? 2 : 1); stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 i,j,stride = x*out_n; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; + stbi_uc *prior = cur - stride; int filter = *raw++; - + int filter_bytes = img_n; + int width = x; if (filter > 4) return stbi__err("invalid filter","Corrupt PNG"); @@ -4515,7 +4012,6 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r filter_bytes = 1; width = img_width_bytes; } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; @@ -4539,14 +4035,6 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r raw += img_n; cur += out_n; prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; } else { raw += 1; cur += 1; @@ -4555,47 +4043,38 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ + int nk = (width - 1)*img_n; + #define CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; } - #undef STBI__CASE + #undef CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ + #define CASE(f) \ case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; } + #undef CASE } } @@ -4671,17 +4150,6 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r } } } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } } return 1; @@ -4689,15 +4157,13 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4717,8 +4183,8 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); } } STBI_FREE(a->out); @@ -4756,37 +4222,12 @@ static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) return 1; } -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak @@ -4852,10 +4293,9 @@ static void stbi__de_iphone(stbi__png *z) stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; } else { p[0] = p[2]; p[2] = t; @@ -4874,15 +4314,14 @@ static void stbi__de_iphone(stbi__png *z) } } -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; + stbi_uc has_trans=0, tc[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; + int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; stbi__context *s = z->s; z->expanded = NULL; @@ -4907,9 +4346,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); @@ -4957,11 +4395,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger } break; } @@ -4992,7 +4427,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (scan != STBI__SCAN_load) return 1; if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); if (z->expanded == NULL) return 0; // zlib should set error @@ -5001,14 +4436,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) s->img_out_n = s->img_n+1; else s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; + if (has_trans) + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) stbi__de_iphone(z); if (pal_img_n) { @@ -5018,13 +4448,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); return 1; } @@ -5050,28 +4475,21 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) } } -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) { - void *result=NULL; + unsigned char *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } *x = p->s->img_x; *y = p->s->img_y; - if (n) *n = p->s->img_n; + if (n) *n = p->s->img_out_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; @@ -5080,11 +4498,11 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, st return result; } -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__png p; p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); + return stbi__do_png(&p, x,y,comp,req_comp); } static int stbi__png_test(stbi__context *s) @@ -5113,19 +4531,6 @@ static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) p.s = s; return stbi__png_info_raw(&p, x, y, comp); } - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} #endif // Microsoft/Windows BMP image @@ -5159,11 +4564,11 @@ static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; return n; } @@ -5177,34 +4582,27 @@ static int stbi__bitcount(unsigned int a) return a & 0xff; } -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; } typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; - int extra_read; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) @@ -5216,9 +4614,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) stbi__get16le(s); // discard reserved info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); @@ -5229,6 +4625,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); info->bpp = stbi__get16le(s); + if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); @@ -5245,6 +4642,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) stbi__get32le(s); } if (info->bpp == 16 || info->bpp == 32) { + info->mr = info->mg = info->mb = 0; if (compress == 0) { if (info->bpp == 32) { info->mr = 0xffu << 16; @@ -5261,7 +4659,6 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); - info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? @@ -5293,7 +4690,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, all_a; @@ -5301,9 +4698,8 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int psize=0,i,j,width; int flip_vertically, pad, target; stbi__bmp_data info; - STBI_NOTUSED(ri); - info.all_a = 255; + info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) return NULL; // error code already set @@ -5318,29 +4714,19 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz == 12) { if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; + psize = (info.offset - 14 - 24) / 3; } else { if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - STBI_ASSERT(info.offset == (s->img_buffer - s->buffer_start)); + psize = (info.offset - 14 - info.hsz) >> 2; } - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else target = s->img_n; // if they want monochrome, we'll post-convert - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.bpp < 16) { int z=0; @@ -5352,56 +4738,36 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; } - stbi__skip(s, pad); + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; } + stbi__skip(s, pad); } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); + stbi__skip(s, info.offset - 14 - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; @@ -5436,7 +4802,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; + int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); @@ -5448,7 +4814,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req stbi__skip(s, pad); } } - + // if alpha channel is all 0s, replace with all 255s if (target == 4 && all_a == 0) for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) @@ -5460,7 +4826,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; + t = p1[i], p1[i] = p2[i], p2[i] = t; } } } @@ -5484,14 +4850,14 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) { // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; + if(is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { case 8: return STBI_grey; case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough + // else: fall-through case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough + return STBI_rgb; + case 24: // fall-through case 32: return bits_per_pixel/8; default: return 0; } @@ -5594,18 +4960,18 @@ errorEnd: } // read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) { - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 px = stbi__get16le(s); stbi__uint16 fiveBitMask = 31; // we have 3 channels with 5bits each int r = (px >> 10) & fiveBitMask; int g = (px >> 5) & fiveBitMask; int b = px & fiveBitMask; // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); + out[0] = (r * 255)/31; + out[1] = (g * 255)/31; + out[2] = (b * 255)/31; // some people claim that the most significant bit might be used for alpha // (possibly if an alpha-bit is set in the "image descriptor byte") @@ -5613,7 +4979,7 @@ static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { // read in the TGA header stuff int tga_offset = stbi__get8(s); @@ -5635,13 +5001,10 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; - unsigned char raw_data[4] = {0}; + unsigned char raw_data[4]; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5663,10 +5026,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req *y = tga_height; if (comp) *comp = tga_comp; - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) @@ -5685,7 +5045,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); @@ -5805,7 +5165,6 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -5822,53 +5181,14 @@ static int stbi__psd_test(stbi__context *s) return r; } -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { - int pixelCount; + int pixelCount; int channelCount, compression; - int channel, i; + int channel, i, count, len; int bitdepth; int w,h; stbi_uc *out; - STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" @@ -5925,18 +5245,8 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - + out = (stbi_uc *) stbi__malloc(4 * w*h); if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; @@ -5953,7 +5263,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // Else if n is 128, noop. // Endloop - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); @@ -5968,86 +5278,67 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + // where each channel consists of an 8-bit value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; if (channel >= channelCount) { // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); + // Read the data. + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); } } } } - // convert to desired output format if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); + out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } @@ -6231,13 +5522,10 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c return result; } -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) { stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; + int i, x,y; for (i=0; i<92; ++i) stbi__get8(s); @@ -6245,14 +5533,14 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c x = stbi__get16be(s); y = stbi__get16be(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + result = (stbi_uc *) stbi__malloc(x*y*4); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -6289,13 +5577,11 @@ typedef struct typedef struct { int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; + stbi__gif_lzw codes[4096]; stbi_uc *color_table; int parse, step; int lflags; @@ -6303,7 +5589,6 @@ typedef struct int max_x, max_y; int cur_x, cur_y; int line_size; - int delay; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) @@ -6364,22 +5649,19 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); + stbi__gif g; + if (!stbi__gif_header(s, &g, comp, 1)) { stbi__rewind( s ); return 0; } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); + if (x) *x = g.w; + if (y) *y = g.h; return 1; } static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; - int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6388,12 +5670,10 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - + p = &g->out[g->cur_x + g->cur_y]; c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; + + if (c[3] >= 128) { p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6467,16 +5747,11 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) stbi__skip(s,len); return g->out; } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); if (oldcode >= 0) { p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; @@ -6498,77 +5773,59 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) } } +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + // this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) { - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); + int i; + stbi_uc *prev_out = 0; - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + break; } - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - for (;;) { - int tag = stbi__get8(s); - switch (tag) { + switch (stbi__get8(s)) { case 0x2C: /* Image Descriptor */ { + int prev_trans = -1; stbi__int32 x, y, w, h; stbi_uc *o; @@ -6587,13 +5844,6 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i g->cur_x = g->start_x; g->cur_y = g->start_y; - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - g->lflags = stbi__get8(s); if (g->lflags & 0x40) { @@ -6608,24 +5858,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } g->color_table = (stbi_uc *) g->pal; } else return stbi__errpuc("missing color table", "Corrupt GIF"); o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; return o; } @@ -6633,35 +5878,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); } else { stbi__skip(s, len); break; } } - while ((len = stbi__get8(s)) != 0) { + while ((len = stbi__get8(s)) != 0) stbi__skip(s, len); - } break; } @@ -6672,103 +5901,26 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i return stbi__errpuc("unknown code", "Corrupt GIF"); } } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } - else - out = (stbi_uc*) tmp; - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } + STBI_NOTUSED(req_comp); } -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + u = stbi__gif_load_next(s, &g, comp, req_comp); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); + else if (g.out) + STBI_FREE(g.out); return u; } @@ -6783,24 +5935,20 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) +static int stbi__hdr_test_core(stbi__context *s) { + const char *signature = "#?RADIANCE\n"; int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); + return 0; return 1; } static int stbi__hdr_test(stbi__context* s) { - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + int r = stbi__hdr_test_core(s); stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } return r; } @@ -6854,7 +6002,7 @@ static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) } } -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { char buffer[STBI__HDR_BUFLEN]; char *token; @@ -6865,12 +6013,10 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re int len; unsigned char count, value; int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); + // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header @@ -6899,13 +6045,8 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); // Load image data // image data is stored as some number of sca @@ -6944,29 +6085,20 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); for (k = 0; k < 4; ++k) { - int nleft; i = 0; - while ((nleft = width - i) > 0) { + while (i < width) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -6975,8 +6107,7 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } - if (scanline) - STBI_FREE(scanline); + STBI_FREE(scanline); } return hdr_data; @@ -6987,11 +6118,6 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; if (stbi__hdr_test(s) == 0) { stbi__rewind( s ); @@ -7033,19 +6159,14 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) void *p; stbi__bmp_data info; - info.all_a = 255; + info.all_a = 255; p = stbi__bmp_parse_header(s, &info); stbi__rewind( s ); if (p == NULL) return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } + *x = s->img_x; + *y = s->img_y; + *comp = info.ma ? 4 : 3; return 1; } #endif @@ -7053,10 +6174,7 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; + int channelCount; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; @@ -7073,8 +6191,7 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) } *y = stbi__get32be(s); *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { + if (stbi__get16be(s) != 8) { stbi__rewind( s ); return 0; } @@ -7085,45 +6202,14 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) *comp = 4; return 1; } - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} #endif #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { - int act_comp=0,num_packets=0,chained,dummy; + int act_comp=0,num_packets=0,chained; stbi__pic_packet packets[10]; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { stbi__rewind(s); return 0; @@ -7199,22 +6285,16 @@ static int stbi__pnm_test(stbi__context *s) return 1; } -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi_uc *out; - STBI_NOTUSED(ri); - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; - *x = s->img_x; *y = s->img_y; - if (comp) *comp = s->img_n; + *comp = s->img_n; - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); @@ -7263,20 +6343,16 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { - int maxv, dummy; + int maxv; char c, p, t; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); + stbi__rewind( s ); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); + stbi__rewind( s ); return 0; } @@ -7342,19 +6418,6 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { @@ -7376,27 +6439,6 @@ STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) fseek(f,pos,SEEK_SET); return r; } - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) @@ -7413,51 +6455,10 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int return stbi__info_main(&s,x,y,comp); } -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - #endif // STB_IMAGE_IMPLEMENTATION /* revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED 2.09 (2016-01-16) allow comments in PNM files 16-bit-per-pixel TGA (not bit-per-component) @@ -7611,46 +6612,3 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user 0.50 (2006-11-19) first released version */ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ From 65eb8c098d6ed2c3407b761e8b967f82e0a7b8a7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Oct 2021 21:31:46 +0100 Subject: [PATCH 183/504] Fix Windows builds with DGL_USE_NANOVG_FBO Signed-off-by: falkTX --- dgl/src/NanoVG.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 2c37a218..1faa60f1 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -54,6 +54,18 @@ DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) +# ifdef DGL_USE_NANOVG_FBO +DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) +DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) +DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) +DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) +DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) +DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) +DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) +DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) +DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) +DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) +# endif # ifdef DGL_USE_OPENGL3 DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray) @@ -150,6 +162,18 @@ DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) +# ifdef DGL_USE_NANOVG_FBO +DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) +DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) +DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) +DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) +DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) +DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) +DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) +DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) +DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) +DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) +# endif # ifdef DGL_USE_OPENGL3 DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) DGL_EXT(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray) From b79311a0b373301a399370762d0c70f9938a5487 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 10 Oct 2021 12:42:44 +0100 Subject: [PATCH 184/504] Fix top-level events Signed-off-by: falkTX --- dgl/src/TopLevelWidget.cpp | 20 ++++++++++---------- dgl/src/TopLevelWidgetPrivateData.cpp | 20 -------------------- dgl/src/WindowPrivateData.cpp | 10 +++++----- 3 files changed, 15 insertions(+), 35 deletions(-) diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index d714324c..556d9bd7 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -95,29 +95,29 @@ void TopLevelWidget::setGeometryConstraints(const uint minimumWidth, // -------------------------------------------------------------------------------------------------------------------- -bool TopLevelWidget::onKeyboard(const KeyboardEvent&) +bool TopLevelWidget::onKeyboard(const KeyboardEvent& ev) { - return false; + return pData->keyboardEvent(ev); } -bool TopLevelWidget::onCharacterInput(const CharacterInputEvent&) +bool TopLevelWidget::onCharacterInput(const CharacterInputEvent& ev) { - return false; + return pData->characterInputEvent(ev); } -bool TopLevelWidget::onMouse(const MouseEvent&) +bool TopLevelWidget::onMouse(const MouseEvent& ev) { - return false; + return pData->mouseEvent(ev); } -bool TopLevelWidget::onMotion(const MotionEvent&) +bool TopLevelWidget::onMotion(const MotionEvent& ev) { - return false; + return pData->motionEvent(ev); } -bool TopLevelWidget::onScroll(const ScrollEvent&) +bool TopLevelWidget::onScroll(const ScrollEvent& ev) { - return false; + return pData->scrollEvent(ev); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 29514bb6..fc862250 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -42,10 +42,6 @@ bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) if (! selfw->pData->visible) return false; - // give top-level widget chance to catch this event first - if (self->onKeyboard(ev)) - return true; - // propagate event to all subwidgets recursively return selfw->pData->giveKeyboardEventForSubWidgets(ev); } @@ -56,10 +52,6 @@ bool TopLevelWidget::PrivateData::characterInputEvent(const CharacterInputEvent& if (! selfw->pData->visible) return false; - // give top-level widget chance to catch this event first - if (self->onCharacterInput(ev)) - return true; - // propagate event to all subwidgets recursively return selfw->pData->giveCharacterInputEventForSubWidgets(ev); } @@ -82,10 +74,6 @@ bool TopLevelWidget::PrivateData::mouseEvent(const MouseEvent& ev) rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); } - // give top-level widget chance to catch this event first - if (self->onMouse(ev)) - return true; - // propagate event to all subwidgets recursively return selfw->pData->giveMouseEventForSubWidgets(rev); } @@ -108,10 +96,6 @@ bool TopLevelWidget::PrivateData::motionEvent(const MotionEvent& ev) rev.absolutePos.setY(ev.absolutePos.getY() / autoScaleFactor); } - // give top-level widget chance to catch this event first - if (self->onMotion(ev)) - return true; - // propagate event to all subwidgets recursively return selfw->pData->giveMotionEventForSubWidgets(rev); } @@ -136,10 +120,6 @@ bool TopLevelWidget::PrivateData::scrollEvent(const ScrollEvent& ev) rev.delta.setY(ev.delta.getY() / autoScaleFactor); } - // give top-level widget chance to catch this event first - if (self->onScroll(ev)) - return true; - // propagate event to all subwidgets recursively return selfw->pData->giveScrollEventForSubWidgets(rev); } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 27bee506..0b1b549b 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -822,7 +822,7 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) { TopLevelWidget* const widget(*rit); - if (widget->isVisible() && widget->pData->keyboardEvent(ev)) + if (widget->isVisible() && widget->onKeyboard(ev)) break; } #endif @@ -840,7 +840,7 @@ void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) { TopLevelWidget* const widget(*rit); - if (widget->isVisible() && widget->pData->characterInputEvent(ev)) + if (widget->isVisible() && widget->onCharacterInput(ev)) break; } #endif @@ -858,7 +858,7 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) { TopLevelWidget* const widget(*rit); - if (widget->isVisible() && widget->pData->mouseEvent(ev)) + if (widget->isVisible() && widget->onMouse(ev)) break; } #endif @@ -876,7 +876,7 @@ void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) { TopLevelWidget* const widget(*rit); - if (widget->isVisible() && widget->pData->motionEvent(ev)) + if (widget->isVisible() && widget->onMotion(ev)) break; } #endif @@ -894,7 +894,7 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) { TopLevelWidget* const widget(*rit); - if (widget->isVisible() && widget->pData->scrollEvent(ev)) + if (widget->isVisible() && widget->onScroll(ev)) break; } #endif From 402254e469ec6b00a1595d3b14c758aec9b70b10 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 10 Oct 2021 13:56:27 +0100 Subject: [PATCH 185/504] Fix nano subwidgets getting double offset Signed-off-by: falkTX --- dgl/src/WidgetPrivateData.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 3cbe644a..150d11c2 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -112,18 +112,15 @@ bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev) if (subWidgets.size() == 0) return false; - double x = ev.absolutePos.getX(); - double y = ev.absolutePos.getY(); + const double x = ev.absolutePos.getX(); + const double y = ev.absolutePos.getY(); if (SubWidget* const selfw = dynamic_cast(self)) { if (selfw->pData->needsViewportScaling) { - x -= selfw->getAbsoluteX(); - y -= selfw->getAbsoluteY(); - - ev.absolutePos.setX(x); - ev.absolutePos.setY(y); + ev.absolutePos.setX(x - selfw->getAbsoluteX()); + ev.absolutePos.setY(y - selfw->getAbsoluteY()); } } @@ -151,18 +148,15 @@ bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev) if (subWidgets.size() == 0) return false; - double x = ev.absolutePos.getX(); - double y = ev.absolutePos.getY(); + const double x = ev.absolutePos.getX(); + const double y = ev.absolutePos.getY(); if (SubWidget* const selfw = dynamic_cast(self)) { if (selfw->pData->needsViewportScaling) { - x -= selfw->getAbsoluteX(); - y -= selfw->getAbsoluteY(); - - ev.absolutePos.setX(x); - ev.absolutePos.setY(y); + ev.absolutePos.setX(x - selfw->getAbsoluteX()); + ev.absolutePos.setY(y - selfw->getAbsoluteY()); } } @@ -190,18 +184,15 @@ bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev) if (subWidgets.size() == 0) return false; - double x = ev.absolutePos.getX(); - double y = ev.absolutePos.getY(); + const double x = ev.absolutePos.getX(); + const double y = ev.absolutePos.getY(); if (SubWidget* const selfw = dynamic_cast(self)) { if (selfw->pData->needsViewportScaling) { - x -= selfw->getAbsoluteX(); - y -= selfw->getAbsoluteY(); - - ev.absolutePos.setX(x); - ev.absolutePos.setY(y); + ev.absolutePos.setX(x - selfw->getAbsoluteX()); + ev.absolutePos.setY(y - selfw->getAbsoluteY()); } } From c62c8e4c2a0956cd92ace8aa925e69be673678b2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Oct 2021 08:32:53 +0100 Subject: [PATCH 186/504] Use ImGui from DPF-Widgets repo Signed-off-by: falkTX --- examples/ImguiSimpleGain/DistrhoPluginInfo.h | 4 +- examples/ImguiSimpleGain/ImGuiSrc.cpp | 35 --- examples/ImguiSimpleGain/ImGuiUI.cpp | 314 ------------------- examples/ImguiSimpleGain/ImGuiUI.hpp | 56 ---- examples/ImguiSimpleGain/Makefile | 19 +- examples/ImguiSimpleGain/UISimpleGain.cpp | 44 +-- 6 files changed, 31 insertions(+), 441 deletions(-) delete mode 100644 examples/ImguiSimpleGain/ImGuiSrc.cpp delete mode 100644 examples/ImguiSimpleGain/ImGuiUI.cpp delete mode 100644 examples/ImguiSimpleGain/ImGuiUI.hpp diff --git a/examples/ImguiSimpleGain/DistrhoPluginInfo.h b/examples/ImguiSimpleGain/DistrhoPluginInfo.h index 0deddd7b..091f5e27 100644 --- a/examples/ImguiSimpleGain/DistrhoPluginInfo.h +++ b/examples/ImguiSimpleGain/DistrhoPluginInfo.h @@ -131,7 +131,7 @@ This path must be relative to dpf/distrho/DistrhoUI.hpp @see DISTRHO_UI_USE_CUSTOM */ -#define DISTRHO_UI_CUSTOM_INCLUDE_PATH "ImGuiUI.hpp" +#define DISTRHO_UI_CUSTOM_INCLUDE_PATH "DearImGui.hpp" /** The top-level-widget typedef to use for the custom toolkit. @@ -140,6 +140,6 @@ and define widget type as e.g. DGL_NAMESPACE::MyCustomTopLevelWidget. @see DISTRHO_UI_USE_CUSTOM */ -#define DISTRHO_UI_CUSTOM_WIDGET_TYPE DGL_NAMESPACE::ImGuiUI +#define DISTRHO_UI_CUSTOM_WIDGET_TYPE DGL_NAMESPACE::ImGuiTopLevelWidget #define DISTRHO_UI_USER_RESIZABLE 1 diff --git a/examples/ImguiSimpleGain/ImGuiSrc.cpp b/examples/ImguiSimpleGain/ImGuiSrc.cpp deleted file mode 100644 index ad4c106c..00000000 --- a/examples/ImguiSimpleGain/ImGuiSrc.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2021 Jean Pierre Cimalando - * - * 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 -#if !defined(IMGUI_GL2) && !defined(IMGUI_GL3) -# define IMGUI_GL2 1 -#endif -#if defined(IMGUI_GL2) -# include -#elif defined(IMGUI_GL3) -# include -#endif - -#include -#include -#include -#include -#if defined(IMGUI_GL2) -#include -#elif defined(IMGUI_GL3) -#include -#endif diff --git a/examples/ImguiSimpleGain/ImGuiUI.cpp b/examples/ImguiSimpleGain/ImGuiUI.cpp deleted file mode 100644 index fae28d5f..00000000 --- a/examples/ImguiSimpleGain/ImGuiUI.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2021 Jean Pierre Cimalando - * - * 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 "Application.hpp" - -#include - -#include "ImGuiUI.hpp" -#include -#if !defined(IMGUI_GL2) && !defined(IMGUI_GL3) -# define IMGUI_GL2 1 -#endif -#if defined(IMGUI_GL2) -# include -#elif defined(IMGUI_GL3) -# include -#endif -#include -#include - -START_NAMESPACE_DGL - -struct ImGuiUI::Impl -{ - explicit Impl(ImGuiUI* self); - ~Impl(); - - void setupGL(); - void cleanupGL(); - - static int mouseButtonToImGui(int button); - - ImGuiUI* fSelf = nullptr; - ImGuiContext* fContext = nullptr; - Color fBackgroundColor{0.25f, 0.25f, 0.25f}; - int fRepaintIntervalMs = 15; - - using Clock = std::chrono::steady_clock; - Clock::time_point fLastRepainted; - bool fWasEverPainted = false; -}; - -ImGuiUI::ImGuiUI(Window& windowToMapTo) - : TopLevelWidget(windowToMapTo), - fImpl(new ImGuiUI::Impl(this)) -{ - getApp().addIdleCallback(this); -} - -ImGuiUI::~ImGuiUI() -{ - delete fImpl; -} - -void ImGuiUI::setBackgroundColor(Color color) -{ - fImpl->fBackgroundColor = color; -} - -void ImGuiUI::setRepaintInterval(int intervalMs) -{ - fImpl->fRepaintIntervalMs = intervalMs; -} - -void ImGuiUI::onDisplay() -{ - ImGui::SetCurrentContext(fImpl->fContext); - -#if defined(IMGUI_GL2) - ImGui_ImplOpenGL2_NewFrame(); -#elif defined(IMGUI_GL3) - ImGui_ImplOpenGL3_NewFrame(); -#endif - - ImGui::NewFrame(); - onImGuiDisplay(); - ImGui::Render(); - - ImGuiIO &io = ImGui::GetIO(); - - glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); - - Color backgroundColor = fImpl->fBackgroundColor; - glClearColor( - backgroundColor.red, backgroundColor.green, - backgroundColor.blue, backgroundColor.alpha); - glClear(GL_COLOR_BUFFER_BIT); - glLoadIdentity(); - -#if defined(IMGUI_GL2) - ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); -#elif defined(IMGUI_GL3) - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); -#endif - - fImpl->fLastRepainted = Impl::Clock::now(); - fImpl->fWasEverPainted = true; -} - -bool ImGuiUI::onKeyboard(const KeyboardEvent& event) -{ - ImGui::SetCurrentContext(fImpl->fContext); - ImGuiIO &io = ImGui::GetIO(); - - if (event.press) - io.AddInputCharacter(event.key); - - int imGuiKey = event.key; - if (imGuiKey >= 0 && imGuiKey < 128) - { - if (imGuiKey >= 'a' && imGuiKey <= 'z') - imGuiKey = imGuiKey - 'a' + 'A'; - io.KeysDown[imGuiKey] = event.press; - } - - return io.WantCaptureKeyboard; -} - -bool ImGuiUI::onSpecial(const SpecialEvent& event) -{ - ImGui::SetCurrentContext(fImpl->fContext); - ImGuiIO &io = ImGui::GetIO(); - - int imGuiKey = IM_ARRAYSIZE(io.KeysDown) - event.key; - io.KeysDown[imGuiKey] = event.press; - - switch (event.key) - { - case kKeyShift: - io.KeyShift = event.press; - break; - case kKeyControl: - io.KeyCtrl = event.press; - break; - case kKeyAlt: - io.KeyAlt = event.press; - break; - case kKeySuper: - io.KeySuper = event.press; - break; - default: - break; - } - - return io.WantCaptureKeyboard; -} - -bool ImGuiUI::onMouse(const MouseEvent& event) -{ - ImGui::SetCurrentContext(fImpl->fContext); - ImGuiIO &io = ImGui::GetIO(); - - int imGuiButton = Impl::mouseButtonToImGui(event.button); - if (imGuiButton != -1) - io.MouseDown[imGuiButton] = event.press; - - return io.WantCaptureMouse; -} - -bool ImGuiUI::onMotion(const MotionEvent& event) -{ - ImGui::SetCurrentContext(fImpl->fContext); - ImGuiIO &io = ImGui::GetIO(); - - // FIXME - const double scaleFactor = 1; // getScaleFactor(); - io.MousePos.x = std::round(scaleFactor * event.pos.getX()); - io.MousePos.y = std::round(scaleFactor * event.pos.getY()); - - return false; -} - -bool ImGuiUI::onScroll(const ScrollEvent& event) -{ - ImGui::SetCurrentContext(fImpl->fContext); - ImGuiIO &io = ImGui::GetIO(); - - io.MouseWheel += event.delta.getY(); - io.MouseWheelH += event.delta.getX(); - - return io.WantCaptureMouse; -} - -void ImGuiUI::onResize(const ResizeEvent& event) -{ - TopLevelWidget::onResize(event); - - const uint width = event.size.getWidth(); - const uint height = event.size.getHeight(); - - ImGui::SetCurrentContext(fImpl->fContext); - ImGuiIO &io = ImGui::GetIO(); - - const double scaleFactor = getScaleFactor(); - io.DisplaySize.x = std::round(scaleFactor * width); - io.DisplaySize.y = std::round(scaleFactor * height); -} - -void ImGuiUI::idleCallback() -{ - bool shouldRepaint; - - if (fImpl->fWasEverPainted) - { - Impl::Clock::duration elapsed = - Impl::Clock::now() - fImpl->fLastRepainted; - std::chrono::milliseconds elapsedMs = - std::chrono::duration_cast(elapsed); - shouldRepaint = elapsedMs.count() > fImpl->fRepaintIntervalMs; - } - else - { - shouldRepaint = true; - } - - if (shouldRepaint) - repaint(); -} - -ImGuiUI::Impl::Impl(ImGuiUI* self) - : fSelf(self) -{ - setupGL(); -} - -ImGuiUI::Impl::~Impl() -{ - cleanupGL(); -} - -void ImGuiUI::Impl::setupGL() -{ - DISTRHO_SAFE_ASSERT_RETURN(glewInit() == 0,); - - IMGUI_CHECKVERSION(); - fContext = ImGui::CreateContext(); - ImGui::SetCurrentContext(fContext); - - ImGuiIO &io = ImGui::GetIO(); - const double scaleFactor = fSelf->getScaleFactor(); - io.DisplaySize.x = std::round(scaleFactor * fSelf->getWidth()); - io.DisplaySize.y = std::round(scaleFactor * fSelf->getHeight()); - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; - io.IniFilename = nullptr; - - io.KeyMap[ImGuiKey_Tab] = '\t'; - io.KeyMap[ImGuiKey_LeftArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyLeft; - io.KeyMap[ImGuiKey_RightArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyRight; - io.KeyMap[ImGuiKey_UpArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyUp; - io.KeyMap[ImGuiKey_DownArrow] = IM_ARRAYSIZE(io.KeysDown) - kKeyDown; - io.KeyMap[ImGuiKey_PageUp] = IM_ARRAYSIZE(io.KeysDown) - kKeyPageUp; - io.KeyMap[ImGuiKey_PageDown] = IM_ARRAYSIZE(io.KeysDown) - kKeyPageDown; - io.KeyMap[ImGuiKey_Home] = IM_ARRAYSIZE(io.KeysDown) - kKeyHome; - io.KeyMap[ImGuiKey_End] = IM_ARRAYSIZE(io.KeysDown) - kKeyEnd; - io.KeyMap[ImGuiKey_Insert] = IM_ARRAYSIZE(io.KeysDown) - kKeyInsert; - io.KeyMap[ImGuiKey_Delete] = 127; - io.KeyMap[ImGuiKey_Backspace] = '\b'; - io.KeyMap[ImGuiKey_Space] = ' '; - io.KeyMap[ImGuiKey_Enter] = '\r'; - io.KeyMap[ImGuiKey_Escape] = 27; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - -#if defined(IMGUI_GL2) - ImGui_ImplOpenGL2_Init(); -#elif defined(IMGUI_GL3) - ImGui_ImplOpenGL3_Init(); -#endif -} - -void ImGuiUI::Impl::cleanupGL() -{ - ImGui::SetCurrentContext(fContext); -#if defined(IMGUI_GL2) - ImGui_ImplOpenGL2_Shutdown(); -#elif defined(IMGUI_GL3) - ImGui_ImplOpenGL3_Shutdown(); -#endif - ImGui::DestroyContext(fContext); -} - -int ImGuiUI::Impl::mouseButtonToImGui(int button) -{ - switch (button) - { - default: - return -1; - case 1: - return 0; - case 2: - return 2; - case 3: - return 1; - } -} - -END_NAMESPACE_DGL diff --git a/examples/ImguiSimpleGain/ImGuiUI.hpp b/examples/ImguiSimpleGain/ImGuiUI.hpp deleted file mode 100644 index 589d3bef..00000000 --- a/examples/ImguiSimpleGain/ImGuiUI.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2021 Jean Pierre Cimalando - * - * 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. - */ - -#pragma once - -#include "TopLevelWidget.hpp" -#include "Color.hpp" - -#ifndef DGL_OPENGL -# error ImGUI is only available in OpenGL mode -#endif - -START_NAMESPACE_DGL - -/** - ImGui user interface class. -*/ -class ImGuiUI : public TopLevelWidget, - public IdleCallback { -public: - ImGuiUI(Window& windowToMapTo); - ~ImGuiUI() override; - void setBackgroundColor(Color color); - void setRepaintInterval(int intervalMs); - -protected: - virtual void onImGuiDisplay() = 0; - - virtual void onDisplay() override; - virtual bool onKeyboard(const KeyboardEvent& event) override; - virtual bool onSpecial(const SpecialEvent& event) override; - virtual bool onMouse(const MouseEvent& event) override; - virtual bool onMotion(const MotionEvent& event) override; - virtual bool onScroll(const ScrollEvent& event) override; - virtual void onResize(const ResizeEvent& event) override; - virtual void idleCallback() override; - -private: - struct Impl; - Impl* fImpl; -}; - -END_NAMESPACE_DGL diff --git a/examples/ImguiSimpleGain/Makefile b/examples/ImguiSimpleGain/Makefile index 5912cf18..c709f20b 100644 --- a/examples/ImguiSimpleGain/Makefile +++ b/examples/ImguiSimpleGain/Makefile @@ -17,35 +17,24 @@ FILES_DSP = \ FILES_UI = \ UISimpleGain.cpp \ - ImGuiUI.cpp \ - ImGuiSrc.cpp + ../../../DPF-Widgets/opengl/DearImGui.cpp # -------------------------------------------------------------- # Do some magic include ../../Makefile.plugins.mk -BUILD_CXX_FLAGS += -I../../../imgui -I../../../imgui/backends -BUILD_CXX_FLAGS += $(shell $(PKG_CONFIG) glew --cflags) -LINK_FLAGS += $(shell $(PKG_CONFIG) glew --libs) +# path to DPF-Widgets +BUILD_CXX_FLAGS += -I../../../DPF-Widgets/opengl/ # -------------------------------------------------------------- # Enable all selected plugin types -ifeq ($(HAVE_OPENGL),true) +TARGETS += jack lv2_sep vst3 -TARGETS += jack - -ifneq ($(MACOS_OR_WINDOWS),true) ifeq ($(HAVE_LIBLO),true) TARGETS += dssi endif # HAVE_LIBLO -endif # MACOS_OR_WINDOWS - -TARGETS += lv2_sep -TARGETS += vst - -endif # HAVE_OPENGL all: $(TARGETS) diff --git a/examples/ImguiSimpleGain/UISimpleGain.cpp b/examples/ImguiSimpleGain/UISimpleGain.cpp index 70af1d5c..130d4fd8 100644 --- a/examples/ImguiSimpleGain/UISimpleGain.cpp +++ b/examples/ImguiSimpleGain/UISimpleGain.cpp @@ -25,8 +25,6 @@ */ #include "UISimpleGain.hpp" -#include -// #include "Window.hpp" START_NAMESPACE_DISTRHO @@ -34,13 +32,13 @@ START_NAMESPACE_DISTRHO // Init / Deinit UISimpleGain::UISimpleGain() -: UI(600, 400) + : UI(600, 400) { setGeometryConstraints(600, 400, true); } -UISimpleGain::~UISimpleGain() { - +UISimpleGain::~UISimpleGain() +{ } // ----------------------------------------------------------------------- @@ -50,13 +48,15 @@ UISimpleGain::~UISimpleGain() { A parameter has changed on the plugin side. This is called by the host to inform the UI about parameter changes. */ -void UISimpleGain::parameterChanged(uint32_t index, float value) { +void UISimpleGain::parameterChanged(uint32_t index, float value) +{ params[index] = value; - switch (index) { - case PluginSimpleGain::paramGain: - // do something when Gain param is set, such as update a widget - break; + switch (index) + { + case PluginSimpleGain::paramGain: + // do something when Gain param is set, such as update a widget + break; } (void)value; @@ -66,9 +66,12 @@ void UISimpleGain::parameterChanged(uint32_t index, float value) { A program has been loaded on the plugin side. This is called by the host to inform the UI about program changes. */ -void UISimpleGain::programLoaded(uint32_t index) { - if (index < presetCount) { - for (int i=0; i < PluginSimpleGain::paramCount; i++) { +void UISimpleGain::programLoaded(uint32_t index) +{ + if (index < presetCount) + { + for (int i=0; i < PluginSimpleGain::paramCount; i++) + { // set values for each parameter and update their widgets parameterChanged(i, factoryPresets[index].params[i]); } @@ -78,7 +81,8 @@ void UISimpleGain::programLoaded(uint32_t index) { /** Optional callback to inform the UI about a sample rate change on the plugin side. */ -void UISimpleGain::sampleRateChanged(double newSampleRate) { +void UISimpleGain::sampleRateChanged(double newSampleRate) +{ (void)newSampleRate; } @@ -89,10 +93,11 @@ void UISimpleGain::sampleRateChanged(double newSampleRate) { /** A function called to draw the view contents. */ -void UISimpleGain::onImGuiDisplay() { - float width = getWidth(); - float height = getHeight(); - float margin = 20.0f; +void UISimpleGain::onImGuiDisplay() +{ + const float width = getWidth(); + const float height = getHeight(); + const float margin = 20.0f; ImGui::SetNextWindowPos(ImVec2(margin, margin)); ImGui::SetNextWindowSize(ImVec2(width - 2 * margin, height - 2 * margin)); @@ -121,7 +126,8 @@ void UISimpleGain::onImGuiDisplay() { // ----------------------------------------------------------------------- -UI* createUI() { +UI* createUI() +{ return new UISimpleGain(); } From 1c19c75b8f2b4a83741c4c92e486c1fde873050e Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Oct 2021 09:05:06 +0100 Subject: [PATCH 187/504] Some attention to ImguiSimpleGain, make it follow other examples Signed-off-by: falkTX --- .../CParamSmooth.hpp | 4 + .../DistrhoPluginInfo.h | 4 +- .../Makefile | 4 +- .../PluginSimpleGain.cpp | 121 +++++++----------- .../PluginSimpleGain.hpp | 70 ++++------ .../UISimpleGain.cpp | 68 +++------- .../UISimpleGain.hpp | 13 +- 7 files changed, 101 insertions(+), 183 deletions(-) rename examples/{ImguiSimpleGain => ImGuiSimpleGain}/CParamSmooth.hpp (94%) rename examples/{ImguiSimpleGain => ImGuiSimpleGain}/DistrhoPluginInfo.h (98%) rename examples/{ImguiSimpleGain => ImGuiSimpleGain}/Makefile (93%) rename examples/{ImguiSimpleGain => ImGuiSimpleGain}/PluginSimpleGain.cpp (58%) rename examples/{ImguiSimpleGain => ImGuiSimpleGain}/PluginSimpleGain.hpp (69%) rename examples/{ImguiSimpleGain => ImGuiSimpleGain}/UISimpleGain.cpp (65%) rename examples/{ImguiSimpleGain => ImGuiSimpleGain}/UISimpleGain.hpp (86%) diff --git a/examples/ImguiSimpleGain/CParamSmooth.hpp b/examples/ImGuiSimpleGain/CParamSmooth.hpp similarity index 94% rename from examples/ImguiSimpleGain/CParamSmooth.hpp rename to examples/ImGuiSimpleGain/CParamSmooth.hpp index b2b96ce0..82c9488e 100644 --- a/examples/ImguiSimpleGain/CParamSmooth.hpp +++ b/examples/ImGuiSimpleGain/CParamSmooth.hpp @@ -30,6 +30,10 @@ public: } } + inline void flush() { + z = 0.0f; + } + inline float process(float in) { return z = (in * b) + (z * a); } diff --git a/examples/ImguiSimpleGain/DistrhoPluginInfo.h b/examples/ImGuiSimpleGain/DistrhoPluginInfo.h similarity index 98% rename from examples/ImguiSimpleGain/DistrhoPluginInfo.h rename to examples/ImGuiSimpleGain/DistrhoPluginInfo.h index 091f5e27..cbcbc84e 100644 --- a/examples/ImguiSimpleGain/DistrhoPluginInfo.h +++ b/examples/ImGuiSimpleGain/DistrhoPluginInfo.h @@ -22,7 +22,7 @@ This is used to identify your plugin before a Plugin instance can be created. @note This macro is required. */ -#define DISTRHO_PLUGIN_NAME "ImguiSimpleGain" +#define DISTRHO_PLUGIN_NAME "ImGuiSimpleGain" /** Number of audio inputs the plugin has. @@ -93,7 +93,7 @@ @see Plugin::initProgramName(uint32_t, String&) @see Plugin::loadProgram(uint32_t) */ -#define DISTRHO_PLUGIN_WANT_PROGRAMS 1 +#define DISTRHO_PLUGIN_WANT_PROGRAMS 0 /** Wherever the plugin uses internal non-parameter data. diff --git a/examples/ImguiSimpleGain/Makefile b/examples/ImGuiSimpleGain/Makefile similarity index 93% rename from examples/ImguiSimpleGain/Makefile rename to examples/ImGuiSimpleGain/Makefile index c709f20b..5dc2401f 100644 --- a/examples/ImguiSimpleGain/Makefile +++ b/examples/ImGuiSimpleGain/Makefile @@ -7,7 +7,7 @@ # -------------------------------------------------------------- # Project name, used for binaries -NAME = d_ImguiSimpleGain +NAME = d_ImGuiSimpleGain # -------------------------------------------------------------- # Files to build @@ -30,7 +30,7 @@ BUILD_CXX_FLAGS += -I../../../DPF-Widgets/opengl/ # -------------------------------------------------------------- # Enable all selected plugin types -TARGETS += jack lv2_sep vst3 +TARGETS += jack lv2_sep vst2 vst3 ifeq ($(HAVE_LIBLO),true) TARGETS += dssi diff --git a/examples/ImguiSimpleGain/PluginSimpleGain.cpp b/examples/ImGuiSimpleGain/PluginSimpleGain.cpp similarity index 58% rename from examples/ImguiSimpleGain/PluginSimpleGain.cpp rename to examples/ImGuiSimpleGain/PluginSimpleGain.cpp index 6c38238b..5cb19c3c 100644 --- a/examples/ImguiSimpleGain/PluginSimpleGain.cpp +++ b/examples/ImGuiSimpleGain/PluginSimpleGain.cpp @@ -31,109 +31,65 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- PluginSimpleGain::PluginSimpleGain() - : Plugin(paramCount, presetCount, 0) // paramCount param(s), presetCount program(s), 0 states + : Plugin(paramCount, 0, 0), // parameters, programs, states + fSampleRate(getSampleRate()), + fGainDB(0.0f), + fGainLinear(1.0f) { - smooth_gain = new CParamSmooth(20.0f, getSampleRate()); - - for (unsigned p = 0; p < paramCount; ++p) { - Parameter param; - initParameter(p, param); - setParameterValue(p, param.ranges.def); - } -} - -PluginSimpleGain::~PluginSimpleGain() { - delete smooth_gain; + fSmoothGain = new CParamSmooth(20.0f, fSampleRate); } // ----------------------------------------------------------------------- // Init -void PluginSimpleGain::initParameter(uint32_t index, Parameter& parameter) { - if (index >= paramCount) - return; +void PluginSimpleGain::initParameter(uint32_t index, Parameter& parameter) +{ + DISTRHO_SAFE_ASSERT_RETURN(index == 0,); parameter.ranges.min = -90.0f; parameter.ranges.max = 30.0f; parameter.ranges.def = -0.0f; - parameter.unit = "db"; parameter.hints = kParameterIsAutomable; - - switch (index) { - case paramGain: - parameter.name = "Gain (dB)"; - parameter.shortName = "Gain"; - parameter.symbol = "gain"; - break; - } -} - -/** - Set the name of the program @a index. - This function will be called once, shortly after the plugin is created. -*/ -void PluginSimpleGain::initProgramName(uint32_t index, String& programName) { - if (index < presetCount) { - programName = factoryPresets[index].name; - } + parameter.name = "Gain"; + parameter.shortName = "Gain"; + parameter.symbol = "gain"; + parameter.unit = "dB"; } // ----------------------------------------------------------------------- // Internal data -/** - Optional callback to inform the plugin about a sample rate change. -*/ -void PluginSimpleGain::sampleRateChanged(double newSampleRate) { - fSampleRate = newSampleRate; - smooth_gain->setSampleRate(newSampleRate); -} - /** Get the current value of a parameter. */ -float PluginSimpleGain::getParameterValue(uint32_t index) const { - return fParams[index]; +float PluginSimpleGain::getParameterValue(uint32_t index) const +{ + DISTRHO_SAFE_ASSERT_RETURN(index == 0, 0.0f); + + return fGainDB; } /** Change a parameter value. */ -void PluginSimpleGain::setParameterValue(uint32_t index, float value) { - fParams[index] = value; - - switch (index) { - case paramGain: - gain = DB_CO(CLAMP(fParams[paramGain], -90.0, 30.0)); - break; - } -} +void PluginSimpleGain::setParameterValue(uint32_t index, float value) +{ + DISTRHO_SAFE_ASSERT_RETURN(index == 0,); -/** - Load a program. - The host may call this function from any context, - including realtime processing. -*/ -void PluginSimpleGain::loadProgram(uint32_t index) { - if (index < presetCount) { - for (int i=0; i < paramCount; i++) { - setParameterValue(i, factoryPresets[index].params[i]); - } - } + fGainDB = value; + fGainLinear = DB_CO(CLAMP(value, -90.0, 30.0)); } // ----------------------------------------------------------------------- // Process -void PluginSimpleGain::activate() { - // plugin is activated +void PluginSimpleGain::activate() +{ + fSmoothGain->flush(); } - - -void PluginSimpleGain::run(const float** inputs, float** outputs, - uint32_t frames) { - +void PluginSimpleGain::run(const float** inputs, float** outputs, uint32_t frames) +{ // get the left and right audio inputs const float* const inpL = inputs[0]; const float* const inpR = inputs[1]; @@ -143,16 +99,29 @@ void PluginSimpleGain::run(const float** inputs, float** outputs, float* const outR = outputs[1]; // apply gain against all samples - for (uint32_t i=0; i < frames; ++i) { - float gainval = smooth_gain->process(gain); - outL[i] = inpL[i] * gainval; - outR[i] = inpR[i] * gainval; + for (uint32_t i=0; i < frames; ++i) + { + const float gain = fSmoothGain->process(fGainLinear); + outL[i] = inpL[i] * gain; + outR[i] = inpR[i] * gain; } } // ----------------------------------------------------------------------- -Plugin* createPlugin() { +/** + Optional callback to inform the plugin about a sample rate change. +*/ +void PluginSimpleGain::sampleRateChanged(double newSampleRate) +{ + fSampleRate = newSampleRate; + fSmoothGain->setSampleRate(newSampleRate); +} + +// ----------------------------------------------------------------------- + +Plugin* createPlugin() +{ return new PluginSimpleGain(); } diff --git a/examples/ImguiSimpleGain/PluginSimpleGain.hpp b/examples/ImGuiSimpleGain/PluginSimpleGain.hpp similarity index 69% rename from examples/ImguiSimpleGain/PluginSimpleGain.hpp rename to examples/ImGuiSimpleGain/PluginSimpleGain.hpp index 69da27d0..63c33443 100644 --- a/examples/ImguiSimpleGain/PluginSimpleGain.hpp +++ b/examples/ImGuiSimpleGain/PluginSimpleGain.hpp @@ -29,6 +29,7 @@ #include "DistrhoPlugin.hpp" #include "CParamSmooth.hpp" +#include "extra/ScopedPointer.hpp" START_NAMESPACE_DISTRHO @@ -59,57 +60,50 @@ public: PluginSimpleGain(); - ~PluginSimpleGain(); - protected: // ------------------------------------------------------------------- // Information - const char* getLabel() const noexcept override { + const char* getLabel() const noexcept override + { return "SimpleGain"; } - const char* getDescription() const override { - return "A simple audio volume gain plugin"; - } - - const char* getMaker() const noexcept override { - return "example.com"; + const char* getDescription() const override + { + return "A simple audio volume gain plugin with ImGui for its GUI"; } - const char* getHomePage() const override { - return "https://example.com/plugins/simplegain"; + const char* getMaker() const noexcept override + { + return "Jean Pierre Cimalando, falkTX"; } - const char* getLicense() const noexcept override { - return "https://spdx.org/licenses/MIT"; + const char* getLicense() const noexcept override + { + return "MIT"; } - uint32_t getVersion() const noexcept override { - return d_version(0, 1, 0); + uint32_t getVersion() const noexcept override + { + return d_version(1, 0, 0); } - // Go to: - // - // http://service.steinberg.de/databases/plugin.nsf/plugIn - // - // Get a proper plugin UID and fill it in here! - int64_t getUniqueId() const noexcept override { - return d_cconst('a', 'b', 'c', 'd'); + int64_t getUniqueId() const noexcept override + { + return d_cconst('d', 'I', 'm', 'G'); } // ------------------------------------------------------------------- // Init void initParameter(uint32_t index, Parameter& parameter) override; - void initProgramName(uint32_t index, String& programName) override; // ------------------------------------------------------------------- // Internal data float getParameterValue(uint32_t index) const override; void setParameterValue(uint32_t index, float value) override; - void loadProgram(uint32_t index) override; // ------------------------------------------------------------------- // Optional @@ -121,39 +115,19 @@ protected: // Process void activate() override; - void run(const float**, float** outputs, uint32_t frames) override; - // ------------------------------------------------------------------- private: - float fParams[paramCount]; - double fSampleRate; - float gain; - CParamSmooth *smooth_gain; + double fSampleRate; + float fGainDB; + float fGainLinear; + ScopedPointer fSmoothGain; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginSimpleGain) }; -struct Preset { - const char* name; - float params[PluginSimpleGain::paramCount]; -}; - -const Preset factoryPresets[] = { - { - "Unity Gain", - {0.0f} - } - //,{ - // "Another preset", // preset name - // {-14.0f, ...} // array of presetCount float param values - //} -}; - -const uint presetCount = sizeof(factoryPresets) / sizeof(Preset); - // ----------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/examples/ImguiSimpleGain/UISimpleGain.cpp b/examples/ImGuiSimpleGain/UISimpleGain.cpp similarity index 65% rename from examples/ImguiSimpleGain/UISimpleGain.cpp rename to examples/ImGuiSimpleGain/UISimpleGain.cpp index 130d4fd8..d7684436 100644 --- a/examples/ImguiSimpleGain/UISimpleGain.cpp +++ b/examples/ImGuiSimpleGain/UISimpleGain.cpp @@ -32,13 +32,15 @@ START_NAMESPACE_DISTRHO // Init / Deinit UISimpleGain::UISimpleGain() - : UI(600, 400) + : UI(600, 400), + fGain(0.0f), + fResizeHandle(this) { setGeometryConstraints(600, 400, true); -} -UISimpleGain::~UISimpleGain() -{ + // hide handle if UI is resizable + if (isResizable()) + fResizeHandle.hide(); } // ----------------------------------------------------------------------- @@ -50,46 +52,15 @@ UISimpleGain::~UISimpleGain() */ void UISimpleGain::parameterChanged(uint32_t index, float value) { - params[index] = value; - - switch (index) - { - case PluginSimpleGain::paramGain: - // do something when Gain param is set, such as update a widget - break; - } + DISTRHO_SAFE_ASSERT_RETURN(index == 0,); - (void)value; -} - -/** - A program has been loaded on the plugin side. - This is called by the host to inform the UI about program changes. -*/ -void UISimpleGain::programLoaded(uint32_t index) -{ - if (index < presetCount) - { - for (int i=0; i < PluginSimpleGain::paramCount; i++) - { - // set values for each parameter and update their widgets - parameterChanged(i, factoryPresets[index].params[i]); - } - } -} - -/** - Optional callback to inform the UI about a sample rate change on the plugin side. -*/ -void UISimpleGain::sampleRateChanged(double newSampleRate) -{ - (void)newSampleRate; + fGain = value; + repaint(); } // ----------------------------------------------------------------------- // Widget callbacks - /** A function called to draw the view contents. */ @@ -97,28 +68,27 @@ void UISimpleGain::onImGuiDisplay() { const float width = getWidth(); const float height = getHeight(); - const float margin = 20.0f; + const float margin = 20.0f * getScaleFactor(); ImGui::SetNextWindowPos(ImVec2(margin, margin)); ImGui::SetNextWindowSize(ImVec2(width - 2 * margin, height - 2 * margin)); - if (ImGui::Begin("Simple gain")) { - static char aboutText[256] = - "This is a demo plugin made with ImGui.\n"; + if (ImGui::Begin("Simple gain", nullptr, ImGuiWindowFlags_NoResize)) + { + static char aboutText[256] = "This is a demo plugin made with ImGui.\n"; ImGui::InputTextMultiline("About", aboutText, sizeof(aboutText)); - float& gain = params[PluginSimpleGain::paramGain]; - if (ImGui::SliderFloat("Gain (dB)", &gain, -90.0f, 30.0f)) + if (ImGui::SliderFloat("Gain (dB)", &fGain, -90.0f, 30.0f)) { if (ImGui::IsItemActivated()) - { - editParameter(PluginSimpleGain::paramGain, true); - } - setParameterValue(PluginSimpleGain::paramGain, gain); + editParameter(0, true); + + setParameterValue(0, fGain); } + if (ImGui::IsItemDeactivated()) { - editParameter(PluginSimpleGain::paramGain, false); + editParameter(0, false); } } ImGui::End(); diff --git a/examples/ImguiSimpleGain/UISimpleGain.hpp b/examples/ImGuiSimpleGain/UISimpleGain.hpp similarity index 86% rename from examples/ImguiSimpleGain/UISimpleGain.hpp rename to examples/ImGuiSimpleGain/UISimpleGain.hpp index cf28dd8e..03beabff 100644 --- a/examples/ImguiSimpleGain/UISimpleGain.hpp +++ b/examples/ImGuiSimpleGain/UISimpleGain.hpp @@ -28,24 +28,25 @@ #define UI_SIMPLEGAIN_H #include "DistrhoUI.hpp" -#include "PluginSimpleGain.hpp" +#include "../generic/ResizeHandle.hpp" START_NAMESPACE_DISTRHO -class UISimpleGain : public UI { +class UISimpleGain : public UI +{ public: UISimpleGain(); - ~UISimpleGain(); protected: + // DSP/Plugin callbacks void parameterChanged(uint32_t, float value) override; - void programLoaded(uint32_t index) override; - void sampleRateChanged(double newSampleRate) override; + // Widget callbacks void onImGuiDisplay() override; private: - float params[PluginSimpleGain::paramCount] {}; + float fGain; + ResizeHandle fResizeHandle; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UISimpleGain) }; From 87986a6f343a937dea4f3db3a591beacea8251e2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 12 Oct 2021 14:21:31 +0100 Subject: [PATCH 188/504] Fix Window resize using old size in some situations Signed-off-by: falkTX --- dgl/src/pugl.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 10c4c933..edf83a08 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -354,17 +354,18 @@ PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint { view->defaultWidth = width; view->defaultHeight = height; + view->frame.width = width; + view->frame.height = height; -#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) - // replace the 2 views setFrame with setFrameSize - const PuglRect frame = { view->frame.x, view->frame.y, (double)width, (double)height }; +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) + // replace setFrame with setFrameSize PuglInternals* const impl = view->impl; - // Update view frame to exactly the requested frame in Pugl coordinates - view->frame = frame; - + const PuglRect frame = view->frame; const NSRect framePx = rectToNsRect(frame); const NSRect framePt = nsRectToPoints(view, framePx); + if (impl->window) { // Resize window to fit new content rect @@ -418,8 +419,6 @@ PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint } #endif - view->frame.width = width; - view->frame.height = height; return PUGL_SUCCESS; } From 7611f937ee41e0da34716933af0039b4d2d13e54 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Oct 2021 10:15:27 +0100 Subject: [PATCH 189/504] Add NanoVG::globalTint, some code comments --- dgl/ImageBase.hpp | 2 +- dgl/ImageBaseWidgets.hpp | 51 +++++++++++++++++++++++++++++++++--- dgl/NanoVG.hpp | 6 ++++- dgl/src/ImageBaseWidgets.cpp | 8 +++--- dgl/src/NanoVG.cpp | 6 +++++ 5 files changed, 64 insertions(+), 9 deletions(-) diff --git a/dgl/ImageBase.hpp b/dgl/ImageBase.hpp index 36c2ab28..e7e84792 100644 --- a/dgl/ImageBase.hpp +++ b/dgl/ImageBase.hpp @@ -39,7 +39,7 @@ enum ImageFormat { It is an abstract class that provides the common methods to build on top. Cairo and OpenGL Image classes are based upon this one. - @see Image + @see CairoImage, OpenGLImage */ class ImageBase { diff --git a/dgl/ImageBaseWidgets.hpp b/dgl/ImageBaseWidgets.hpp index 44011a92..1a4fbbea 100644 --- a/dgl/ImageBaseWidgets.hpp +++ b/dgl/ImageBaseWidgets.hpp @@ -25,13 +25,36 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- +/** + DGL Image About Window class. + + This is a Window attached (transient) to another Window that simply shows an Image as its content. + It is typically used for "about this project" style pop-up Windows. + + Pressing 'Esc' or clicking anywhere on the window will automatically close it. + + @see CairoImageAboutWindow, OpenGLImageAboutWindow, Window::runAsModal(bool) + */ template class ImageBaseAboutWindow : public StandaloneWindow { public: - explicit ImageBaseAboutWindow(Window& parentWindow, const ImageType& image = ImageType()); - explicit ImageBaseAboutWindow(TopLevelWidget* parentTopLevelWidget, const ImageType& image = ImageType()); - + /** + Constructor taking an existing Window as the parent transient window and an optional image. + If @a image is valid, the about window size will match the image size. + */ + explicit ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image = ImageType()); + + /** + Constructor taking a top-level-widget's Window as the parent transient window and an optional image. + If @a image is valid, the about window size will match the image size. + */ + explicit ImageBaseAboutWindow(TopLevelWidget* topLevelWidget, const ImageType& image = ImageType()); + + /** + Set a new image to use as background for this window. + Window size will adjust to match the image size. + */ void setImage(const ImageType& image); protected: @@ -47,6 +70,16 @@ private: // -------------------------------------------------------------------------------------------------------------------- +/** + DGL Image Button class. + + This is a typical button, where the drawing comes from a pregenerated set of images. + The button can be under "normal", "hover" and "down" states, with one separate image possible for each. + + The event logic for this button comes from the ButtonEventHandler class. + + @see CairoImageButton, OpenGLImageButton + */ template class ImageBaseButton : public SubWidget, public ButtonEventHandler @@ -81,6 +114,18 @@ private: // -------------------------------------------------------------------------------------------------------------------- +/** + DGL Image Knob class. + + This is a typical knob/dial, where the drawing comes from a pregenerated image "filmstrip". + The knob's "filmstrip" image can be either horizontal or vertical, + with the number of steps automatically based on the largest value (ie, horizontal if width>height, vertical if height>width). + There are no different images for "hover" or "down" states. + + The event logic for this knob comes from the KnobEventHandler class. + + @see CairoImageKnob, OpenGLImageKnob + */ template class ImageBaseKnob : public SubWidget, public KnobEventHandler diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index e7f96f13..b3483e97 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -444,6 +444,11 @@ public: */ void globalAlpha(float alpha); + /** + Sets the color tint applied to all rendered shapes. + */ + void globalTint(Color tint); + /* -------------------------------------------------------------------- * Transforms */ @@ -943,7 +948,6 @@ private: inline void onDisplay() override { // NOTE maybe should use BaseWidget::getWindow().getScaleFactor() as 3rd arg ? - NanoVG::reset(); NanoVG::beginFrame(BaseWidget::getWidth(), BaseWidget::getHeight()); onNanoDisplay(); NanoVG::endFrame(); diff --git a/dgl/src/ImageBaseWidgets.cpp b/dgl/src/ImageBaseWidgets.cpp index 039f7f15..bb16f0ce 100644 --- a/dgl/src/ImageBaseWidgets.cpp +++ b/dgl/src/ImageBaseWidgets.cpp @@ -22,8 +22,8 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- template -ImageBaseAboutWindow::ImageBaseAboutWindow(Window& parentWindow, const ImageType& image) - : StandaloneWindow(parentWindow.getApp(), parentWindow), +ImageBaseAboutWindow::ImageBaseAboutWindow(Window& transientParentWindow, const ImageType& image) + : StandaloneWindow(transientParentWindow.getApp(), transientParentWindow), img(image) { setResizable(false); @@ -39,8 +39,8 @@ ImageBaseAboutWindow::ImageBaseAboutWindow(Window& parentWindow, cons } template -ImageBaseAboutWindow::ImageBaseAboutWindow(TopLevelWidget* const parentTopLevelWidget, const ImageType& image) - : StandaloneWindow(parentTopLevelWidget->getApp(), parentTopLevelWidget->getWindow()), +ImageBaseAboutWindow::ImageBaseAboutWindow(TopLevelWidget* const topLevelWidget, const ImageType& image) + : StandaloneWindow(topLevelWidget->getApp(), topLevelWidget->getWindow()), img(image) { setResizable(false); diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 1faa60f1..8f6fa854 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -513,6 +513,12 @@ void NanoVG::globalAlpha(float alpha) nvgGlobalAlpha(fContext, alpha); } +void NanoVG::globalTint(Color tint) +{ + if (fContext != nullptr) + nvgGlobalTint(fContext, tint); +} + // ----------------------------------------------------------------------- // Transforms From bea5f6f9c39c4977b641234f3c812ac1f7298bf9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Oct 2021 11:06:07 +0100 Subject: [PATCH 190/504] Use EXTRA_DEPENDENCIES instead of EXTRA_LIBS in a few places --- Makefile.plugins.mk | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 67f8f2d8..7f0609d5 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -238,32 +238,32 @@ all: # --------------------------------------------------------------------------------------------------------------------- # Common -$(BUILD_DIR)/%.S.o: %.S $(EXTRA_LIBS) +$(BUILD_DIR)/%.S.o: %.S $(EXTRA_DEPENDENCIES) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ -$(BUILD_DIR)/%.c.o: %.c $(EXTRA_LIBS) +$(BUILD_DIR)/%.c.o: %.c $(EXTRA_DEPENDENCIES) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ -$(BUILD_DIR)/%.cc.o: %.cc $(EXTRA_LIBS) +$(BUILD_DIR)/%.cc.o: %.cc $(EXTRA_DEPENDENCIES) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ -$(BUILD_DIR)/%.cpp.o: %.cpp $(EXTRA_LIBS) +$(BUILD_DIR)/%.cpp.o: %.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ -$(BUILD_DIR)/%.m.o: %.m $(EXTRA_LIBS) +$(BUILD_DIR)/%.m.o: %.m $(EXTRA_DEPENDENCIES) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -ObjC -c -o $@ -$(BUILD_DIR)/%.mm.o: %.mm $(EXTRA_LIBS) +$(BUILD_DIR)/%.mm.o: %.mm $(EXTRA_DEPENDENCIES) -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ @@ -291,27 +291,27 @@ $(DPF_PATH)/build/libdgl-vulkan.a: AS_PUGL_NAMESPACE = $(subst -,_,$(1)) -$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp +$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoPluginMain.cpp ($*)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ -$(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp +$(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUIMain.cpp ($*)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ -$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm +$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUI_macOS.mm ($*)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DPUGL_NAMESPACE=$(call AS_PUGL_NAMESPACE,$*) -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations -I$(DPF_PATH)/dgl/src -I$(DPF_PATH)/dgl/src/pugl-upstream/include -ObjC++ -c -o $@ -$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp +$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoPluginMain.cpp (JACK)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK $(JACK_FLAGS) -c -o $@ -$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp +$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUIMain.cpp (DSSI)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(LIBLO_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@ From 306c680c6ecb3241011ce46dbe27824e8c7189fc Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Oct 2021 17:07:47 +0100 Subject: [PATCH 191/504] Fix JACK MIDI usage after the jackbridge switch Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 55cdaa56..1448addd 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -422,7 +422,7 @@ protected: for (uint32_t i=0; i < eventCount; ++i) { - if (jackbridge_midi_event_get(&jevent, midiInBuf, i) != 0) + if (! jackbridge_midi_event_get(&jevent, midiInBuf, i)) break; // Check if message is control change on channel 1 @@ -735,7 +735,7 @@ private: return jackbridge_midi_event_write(fPortMidiOutBuffer, midiEvent.frame, midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data, - midiEvent.size) == 0; + midiEvent.size); } static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) From d56d568e7650069ad74dc713fb0680b952a4a4f1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Oct 2021 12:07:49 +0100 Subject: [PATCH 192/504] Put EXTRA_LIBS in compiler command instead of object list Signed-off-by: falkTX --- Makefile.plugins.mk | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 7f0609d5..bc2d8264 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -322,13 +322,13 @@ $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(E jack: $(jack) ifeq ($(HAVE_DGL),true) -$(jack): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.o $(DGL_LIB) $(EXTRA_LIBS) +$(jack): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.o $(DGL_LIB) else $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating JACK standalone for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(JACK_LIBS) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(JACK_LIBS) -o $@ # --------------------------------------------------------------------------------------------------------------------- # LADSPA @@ -365,13 +365,13 @@ lv2_dsp: $(lv2_dsp) lv2_sep: $(lv2_dsp) $(lv2_ui) ifeq ($(HAVE_DGL),true) -$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) $(EXTRA_LIBS) +$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) else $(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) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) -o $@ $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o -@mkdir -p $(shell dirname $@) @@ -389,13 +389,13 @@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) vst2 vst: $(vst2) ifeq ($(HAVE_DGL),true) -$(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.o $(DGL_LIB) $(EXTRA_LIBS) +$(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.o $(DGL_LIB) else $(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST2 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@ # --------------------------------------------------------------------------------------------------------------------- # VST3 @@ -403,13 +403,13 @@ endif vst3: $(vst3) ifeq ($(HAVE_DGL),true) -$(vst3): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) $(EXTRA_LIBS) +$(vst3): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) else $(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST3 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ # --------------------------------------------------------------------------------------------------------------------- From 7bed00d806b6f262d0f8783c531b3545e3798635 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Oct 2021 20:51:41 +0100 Subject: [PATCH 193/504] Fix build of vst3 with external uis Signed-off-by: falkTX --- distrho/src/DistrhoUIInternal.hpp | 4 +- distrho/src/DistrhoUIVST3.cpp | 64 ++++++++++++++++++++----------- examples/EmbedExternalUI/Makefile | 1 + 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index fd79f643..a9e9a6b1 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -254,7 +254,7 @@ public: // ------------------------------------------------------------------- -#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs) { uiData->window->addIdleCallback(cb, timerFrequencyInMs); @@ -281,8 +281,8 @@ public: { ui->setSize(width, height); # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + // NOTE in external uis, the ui and window refer to the same object uiData->window->setSize(width, height); - // uiData->app.idle(); # endif } #endif diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 660d052c..e3031d2b 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -125,7 +125,7 @@ static bool applyGeometryConstraints(const uint minimumWidth, const uint minimum * The low-level VST3 stuff comes after. */ class UIVst3 -#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) : public IdleCallback #endif { @@ -162,7 +162,7 @@ public: ~UIVst3() { -#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) fUI.removeIdleCallbackForVST3(this); #endif if (fConnection != nullptr) @@ -177,7 +177,7 @@ public: if (fConnection != nullptr) connect(fConnection); -#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); #endif } @@ -185,6 +185,7 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view interface calls +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI v3_result onWheel(float /*distance*/) { // TODO @@ -207,17 +208,17 @@ public: dglmods |= kModifierShift; if (modifiers & (1 << 1)) dglmods |= kModifierAlt; -#ifdef DISTRHO_OS_MAC +# ifdef DISTRHO_OS_MAC if (modifiers & (1 << 2)) dglmods |= kModifierSuper; if (modifiers & (1 << 3)) dglmods |= kModifierControl; -#else +# else if (modifiers & (1 << 2)) dglmods |= kModifierControl; if (modifiers & (1 << 3)) dglmods |= kModifierSuper; -#endif +# endif return fUI.handlePluginKeyboardVST3(true, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; } @@ -238,21 +239,28 @@ public: dglmods |= kModifierShift; if (modifiers & (1 << 1)) dglmods |= kModifierAlt; -#ifdef DISTRHO_OS_MAC +# ifdef DISTRHO_OS_MAC if (modifiers & (1 << 2)) dglmods |= kModifierSuper; if (modifiers & (1 << 3)) dglmods |= kModifierControl; -#else +# else if (modifiers & (1 << 2)) dglmods |= kModifierControl; if (modifiers & (1 << 3)) dglmods |= kModifierSuper; -#endif +# endif return fUI.handlePluginKeyboardVST3(false, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; } + v3_result onFocus(const bool state) + { + fUI.notifyFocusChanged(state); + return V3_OK; + } +#endif + v3_result getSize(v3_view_rect* const rect) const noexcept { rect->left = rect->top = 0; @@ -281,18 +289,6 @@ public: return V3_OK; } - v3_result onFocus(const bool state) - { -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - fUI.notifyFocusChanged(state); - return V3_OK; -#else - return V3_NOT_IMPLEMENTED; - // unused - (void)state; -#endif - } - v3_result setFrame(v3_plugin_frame** const frame) noexcept { fFrame = frame; @@ -483,7 +479,7 @@ public: // ---------------------------------------------------------------------------------------------------------------- // special idle callback on macOS and Windows -#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS) +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) void idleCallback() override { fUI.idleForVST3(); @@ -1205,6 +1201,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_wheel(void* self, float distance) { +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); dpf_plugin_view* const view = *static_cast(self); @@ -1212,10 +1209,16 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onWheel(distance); +#else + return V3_NOT_IMPLEMENTED; + // unused + (void)self; (void)distance; +#endif } static v3_result V3_API on_key_down(void* self, int16_t key_char, int16_t key_code, 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); dpf_plugin_view* const view = *static_cast(self); @@ -1223,10 +1226,16 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onKeyDown(key_char, key_code, modifiers); +#else + return V3_NOT_IMPLEMENTED; + // unused + (void)self; (void)key_char; (void)key_code; (void)modifiers; +#endif } static v3_result V3_API on_key_up(void* self, int16_t key_char, int16_t key_code, 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); dpf_plugin_view* const view = *static_cast(self); @@ -1234,6 +1243,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onKeyUp(key_char, key_code, modifiers); +#else + return V3_NOT_IMPLEMENTED; + // unused + (void)self; (void)key_char; (void)key_code; (void)modifiers; +#endif } static v3_result V3_API get_size(void* self, v3_view_rect* rect) @@ -1268,6 +1282,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { static v3_result V3_API on_focus(void* self, v3_bool state) { +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI d_stdout("dpf_plugin_view::on_focus => %p %u", self, state); dpf_plugin_view* const view = *static_cast(self); @@ -1275,6 +1290,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); return uivst3->onFocus(state); +#else + return V3_NOT_IMPLEMENTED; + // unused + (void)self; (void)state; +#endif } static v3_result V3_API set_frame(void* self, v3_plugin_frame** frame) diff --git a/examples/EmbedExternalUI/Makefile b/examples/EmbedExternalUI/Makefile index 5fe1f5ef..48ca2a9e 100644 --- a/examples/EmbedExternalUI/Makefile +++ b/examples/EmbedExternalUI/Makefile @@ -35,6 +35,7 @@ TARGETS += jack TARGETS += dssi TARGETS += lv2_sep TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) From 2711cbefa481c3ebed1ca9dbda190bf915d8b0b9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Oct 2021 20:53:22 +0100 Subject: [PATCH 194/504] Fix VST3 plugin filename for windows builds with cmake Closes #340 Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index a0b316b0..b32d84d3 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -414,7 +414,7 @@ function(dpf__build_vst3 NAME DGL_LIBRARY) SUFFIX "") elseif(WIN32) set_target_properties("${NAME}-vst3" PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/${vst3_arch}-win/$<0:>") + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/${vst3_arch}-win/$<0:>" SUFFIX ".vst3") else() set_target_properties("${NAME}-vst3" PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/${vst3_arch}-linux/$<0:>") From cac786dcf66488dddd812488a0ee7ed454f99f57 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 16 Oct 2021 18:52:03 +0100 Subject: [PATCH 195/504] Fix widget margins for a few special cases Signed-off-by: falkTX --- dgl/src/SubWidget.cpp | 19 ++++++++++++++----- dgl/src/WidgetPrivateData.cpp | 12 ++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index 32c93221..d0f38917 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -35,8 +35,8 @@ template bool SubWidget::contains(const T x, const T y) const noexcept { return Rectangle(0, 0, - static_cast(getWidth()) - pData->margin.getX(), - static_cast(getHeight()) - pData->margin.getY()).contains(x, y); + static_cast(getWidth()), + static_cast(getHeight())).contains(x, y); } template @@ -67,9 +67,18 @@ Rectangle SubWidget::getAbsoluteArea() const noexcept Rectangle SubWidget::getConstrainedAbsoluteArea() const noexcept { - return Rectangle(static_cast(std::max(0, getAbsoluteX())), - static_cast(std::max(0, getAbsoluteY())), - getSize()); + const int x = getAbsoluteX(); + const int y = getAbsoluteY(); + + if (x >= 0 && y >= 0) + return Rectangle(x, y, getSize()); + + const int xOffset = std::min(0, x); + const int yOffset = std::min(0, y); + const int width = std::max(0, static_cast(getWidth()) + xOffset); + const int height = std::max(0, static_cast(getHeight()) + yOffset); + + return Rectangle(0, 0, static_cast(width), static_cast(height)); } void SubWidget::setAbsoluteX(const int x) noexcept diff --git a/dgl/src/WidgetPrivateData.cpp b/dgl/src/WidgetPrivateData.cpp index 150d11c2..59b87901 100644 --- a/dgl/src/WidgetPrivateData.cpp +++ b/dgl/src/WidgetPrivateData.cpp @@ -119,8 +119,8 @@ bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev) { if (selfw->pData->needsViewportScaling) { - ev.absolutePos.setX(x - selfw->getAbsoluteX()); - ev.absolutePos.setY(y - selfw->getAbsoluteY()); + ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX()); + ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY()); } } @@ -155,8 +155,8 @@ bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev) { if (selfw->pData->needsViewportScaling) { - ev.absolutePos.setX(x - selfw->getAbsoluteX()); - ev.absolutePos.setY(y - selfw->getAbsoluteY()); + ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX()); + ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY()); } } @@ -191,8 +191,8 @@ bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev) { if (selfw->pData->needsViewportScaling) { - ev.absolutePos.setX(x - selfw->getAbsoluteX()); - ev.absolutePos.setY(y - selfw->getAbsoluteY()); + ev.absolutePos.setX(x - selfw->getAbsoluteX() + selfw->getMargin().getX()); + ev.absolutePos.setY(y - selfw->getAbsoluteY() + selfw->getMargin().getY()); } } From 2f1b5127cc2da84e3c3f155808efb34d650b3489 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 16 Oct 2021 23:46:56 +0100 Subject: [PATCH 196/504] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 77927d98..a8090013 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ bin/ build/ docs/ utils/lv2_ttl_generator +utils/lv2_ttl_generator.dSYM/ From 2ebf89eef32bc81210c75aa1c572cb32c5e052bc Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 17 Oct 2021 03:09:49 +0100 Subject: [PATCH 197/504] Cleanup --- Makefile.plugins.mk | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index bc2d8264..050f71e5 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -238,32 +238,32 @@ all: # --------------------------------------------------------------------------------------------------------------------- # Common -$(BUILD_DIR)/%.S.o: %.S $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/%.S.o: %.S -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ -$(BUILD_DIR)/%.c.o: %.c $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/%.c.o: %.c -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ -$(BUILD_DIR)/%.cc.o: %.cc $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/%.cc.o: %.cc -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ -$(BUILD_DIR)/%.cpp.o: %.cpp $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/%.cpp.o: %.cpp -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ -$(BUILD_DIR)/%.m.o: %.m $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/%.m.o: %.m -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -ObjC -c -o $@ -$(BUILD_DIR)/%.mm.o: %.mm $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/%.mm.o: %.mm -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ From eca8056dc29865bbcbcab0dc6494b20dd459c7ad Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 18 Oct 2021 22:30:22 +0100 Subject: [PATCH 198/504] Cleanup after nanovg updates Signed-off-by: falkTX --- dgl/src/nanovg/nanovg_gl.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index cdf0901f..dc541ff2 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/dgl/src/nanovg/nanovg_gl.h @@ -245,9 +245,6 @@ struct GLNVGcontext { GLNVGshader shader; GLNVGtextureContext* textureContext; float view[2]; - int ntextures; - int ctextures; - int textureId; GLuint vertBuf; #if defined NANOVG_GL3 GLuint vertArr; From 74f4898b2af69e31368007bca06bfc63bd984d43 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 00:07:26 +0100 Subject: [PATCH 199/504] Define functions for plugin format and binary/bundle path, WIP --- distrho/DistrhoPlugin.hpp | 12 +++++++ distrho/DistrhoPluginUtils.hpp | 13 ++++++++ distrho/DistrhoUI.hpp | 13 ++++++++ distrho/src/DistrhoPlugin.cpp | 10 ++++++ distrho/src/DistrhoPluginInternal.hpp | 16 +++++++++ distrho/src/DistrhoUI.cpp | 10 ++++++ distrho/src/DistrhoUIPrivateData.hpp | 6 ++++ distrho/src/DistrhoUtils.cpp | 47 +++++++++++++++++++++++++++ 8 files changed, 127 insertions(+) create mode 100644 distrho/src/DistrhoUtils.cpp diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index e0b7ccb1..741efe75 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -837,6 +837,18 @@ public: */ double getSampleRate() const noexcept; + /** + Get the absolute filename of the plugin binary. + Under certain systems or plugin formats the binary will be inside the plugin bundle. + */ + const char* getBinaryFilename() const noexcept; + + /** + Get the bundle path where the plugin resides. + Can return null if the plugin is not available in a bundle (if it is a single binary). + */ + const char* getBundlePath() const noexcept; + #if DISTRHO_PLUGIN_WANT_TIMEPOS /** Get the current host transport time position.@n diff --git a/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index 47eb0e66..cb27effa 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/distrho/DistrhoPluginUtils.hpp @@ -22,6 +22,19 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- +// misc functions + +/** + Get a string representation of the current plugin format we are building against.@n + This can be "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2" or "VST3".@n + This string is purely informational and must not be used to tweak plugin behaviour. + + @note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT. +*/ +const char* getPluginFormatName() noexcept; + +// ----------------------------------------------------------------------------------------------------------- +// Plugin helper classes /** Handy class to help keep audio buffer in sync with incoming MIDI events. diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 8dda7fae..95e1d832 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -131,6 +131,19 @@ public: */ double getSampleRate() const noexcept; + /** + Get the absolute filename of the UI binary.@n + Under certain systems or plugin formats the binary will be inside the plugin bundle.@n + Also, in some formats or setups, this file might not be the same as the DSP/plugin side (because of DSP/UI separation). + */ + const char* getBinaryFilename() const noexcept; + + /** + Get the bundle path where the UI resides.@n + Can return null if the UI is not available in a bundle (if it is a single binary). + */ + const char* getBundlePath() const noexcept; + /** editParameter. diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 693c4913..cd504412 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -100,6 +100,16 @@ double Plugin::getSampleRate() const noexcept return pData->sampleRate; } +const char* Plugin::getBinaryFilename() const noexcept +{ + return pData->binaryFilename; +} + +const char* Plugin::getBundlePath() const noexcept +{ + return pData->bundlePath; +} + #if DISTRHO_PLUGIN_WANT_TIMEPOS const TimePosition& Plugin::getTimePosition() const noexcept { diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 753d3e08..c6ebde30 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -122,6 +122,8 @@ struct Plugin::PrivateData { uint32_t bufferSize; double sampleRate; + char* binaryFilename; + char* bundlePath; bool canRequestParameterValueChanges; PrivateData() noexcept @@ -151,6 +153,8 @@ struct Plugin::PrivateData { requestParameterValueChangeCallbackFunc(nullptr), bufferSize(d_lastBufferSize), sampleRate(d_lastSampleRate), + binaryFilename(nullptr), + bundlePath(nullptr), canRequestParameterValueChanges(d_lastCanRequestParameterValueChanges) { DISTRHO_SAFE_ASSERT(bufferSize != 0); @@ -225,6 +229,18 @@ struct Plugin::PrivateData { stateDefValues = nullptr; } #endif + + if (binaryFilename != nullptr) + { + std::free(binaryFilename); + binaryFilename = nullptr; + } + + if (bundlePath != nullptr) + { + std::free(bundlePath); + bundlePath = nullptr; + } } #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 64802841..920a8725 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -217,6 +217,16 @@ double UI::getSampleRate() const noexcept return uiData->sampleRate; } +const char* UI::getBinaryFilename() const noexcept +{ + return uiData->binaryFilename; +} + +const char* UI::getBundlePath() const noexcept +{ + return uiData->bundlePath; +} + void UI::editParameter(uint32_t index, bool started) { uiData->editParamCallback(index + uiData->parameterOffset, started); diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 1371b9d2..9e479a5c 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -308,6 +308,8 @@ struct UI::PrivateData { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) char* uiStateFileKeyRequest; #endif + char* binaryFilename; + char* bundlePath; // Callbacks void* callbacksPtr; @@ -331,6 +333,8 @@ struct UI::PrivateData { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) uiStateFileKeyRequest(nullptr), #endif + binaryFilename(nullptr), + bundlePath(nullptr), callbacksPtr(nullptr), editParamCallbackFunc(nullptr), setParamCallbackFunc(nullptr), @@ -370,6 +374,8 @@ struct UI::PrivateData { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) std::free(uiStateFileKeyRequest); #endif + std::free(binaryFilename); + std::free(bundlePath); } void editParamCallback(const uint32_t rindex, const bool started) diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp new file mode 100644 index 00000000..d743fd51 --- /dev/null +++ b/distrho/src/DistrhoUtils.cpp @@ -0,0 +1,47 @@ +/* + * 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. + */ + +START_NAMESPACE_DISTRHO + +#ifdef DISTRHO_PLUGIN_TARGET_JACK +#endif + +// ----------------------------------------------------------------------- + +const char* getPluginFormatName() noexcept +{ +#if defined(DISTRHO_PLUGIN_TARGET_CARLA) + return "Carla"; +#elif defined(DISTRHO_PLUGIN_TARGET_JACK) + return "JACK/Standalone"; +#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA) + return "LADSPA"; +#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) + return "DSSI"; +#elif defined(DISTRHO_PLUGIN_TARGET_LV2) + return "LV2"; +#elif defined(DISTRHO_PLUGIN_TARGET_VST2) + return "VST2"; +#elif defined(DISTRHO_PLUGIN_TARGET_VST3) + return "VST3"; +#else + return "Unknown"; +#endif +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO From 2c910526192f959e8156a8a4d31298e48f2e7ed4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 01:51:23 +0100 Subject: [PATCH 200/504] Start of getBinaryFilename implementation; Cleanup --- distrho/DistrhoPlugin.hpp | 7 +--- distrho/DistrhoPluginMain.cpp | 7 ++++ distrho/DistrhoPluginUtils.hpp | 9 ++++- distrho/DistrhoUI.hpp | 8 +--- distrho/DistrhoUIMain.cpp | 9 +++++ distrho/src/DistrhoPlugin.cpp | 12 ++---- distrho/src/DistrhoPluginInternal.hpp | 23 ++++-------- distrho/src/DistrhoPluginJACK.cpp | 8 ++-- distrho/src/DistrhoPluginLADSPA+DSSI.cpp | 14 +++---- distrho/src/DistrhoPluginLV2.cpp | 17 +++++---- distrho/src/DistrhoPluginLV2export.cpp | 8 ++-- distrho/src/DistrhoPluginVST2.cpp | 26 ++++++------- distrho/src/DistrhoPluginVST3.cpp | 26 ++++++------- distrho/src/DistrhoUI.cpp | 13 +++---- distrho/src/DistrhoUIInternal.hpp | 6 +-- distrho/src/DistrhoUIPrivateData.hpp | 3 -- distrho/src/DistrhoUtils.cpp | 47 ++++++++++++++++++++++++ 17 files changed, 143 insertions(+), 100 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 741efe75..cac54c3b 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -837,15 +837,10 @@ public: */ double getSampleRate() const noexcept; - /** - Get the absolute filename of the plugin binary. - Under certain systems or plugin formats the binary will be inside the plugin bundle. - */ - const char* getBinaryFilename() const noexcept; - /** Get the bundle path where the plugin resides. Can return null if the plugin is not available in a bundle (if it is a single binary). + @see getBinaryFilename */ const char* getBundlePath() const noexcept; diff --git a/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp index ab6f0efd..0335f2c7 100644 --- a/distrho/DistrhoPluginMain.cpp +++ b/distrho/DistrhoPluginMain.cpp @@ -32,3 +32,10 @@ #else # error unsupported format #endif + +#if defined(DISTRHO_PLUGIN_TARGET_JACK) +# define DISTRHO_IS_STANDALONE 1 +#else +# define DISTRHO_IS_STANDALONE 0 +#endif +#include "src/DistrhoUtils.cpp" diff --git a/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index cb27effa..e2d15d7b 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/distrho/DistrhoPluginUtils.hpp @@ -22,7 +22,14 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- -// misc functions +// plugin related utilities + +/** + Get the absolute filename of the plugin DSP/UI binary.@n + Under certain systems or plugin formats the binary will be inside the plugin bundle.@n + Also, in some formats or setups, the DSP and UI binaries are in different files. +*/ +const char* getBinaryFilename(); /** Get a string representation of the current plugin format we are building against.@n diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 95e1d832..2c121788 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -131,16 +131,10 @@ public: */ double getSampleRate() const noexcept; - /** - Get the absolute filename of the UI binary.@n - Under certain systems or plugin formats the binary will be inside the plugin bundle.@n - Also, in some formats or setups, this file might not be the same as the DSP/plugin side (because of DSP/UI separation). - */ - const char* getBinaryFilename() const noexcept; - /** Get the bundle path where the UI resides.@n Can return null if the UI is not available in a bundle (if it is a single binary). + @see getBinaryFilename */ const char* getBundlePath() const noexcept; diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index ef3aba28..0751bbb8 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -31,3 +31,12 @@ #else # error unsupported format #endif + +#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +# if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) +# define DISTRHO_IS_STANDALONE 1 +# else +# define DISTRHO_IS_STANDALONE 0 +# endif +# include "src/DistrhoUtils.cpp" +#endif diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index cd504412..2daa9ed2 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -21,9 +21,10 @@ START_NAMESPACE_DISTRHO /* ------------------------------------------------------------------------------------------------------------ * Static data, see DistrhoPluginInternal.hpp */ -uint32_t d_lastBufferSize = 0; -double d_lastSampleRate = 0.0; -bool d_lastCanRequestParameterValueChanges = false; +uint32_t d_nextBufferSize = 0; +double d_nextSampleRate = 0.0; +const char* d_nextBundlePath = nullptr; +bool d_nextCanRequestParameterValueChanges = false; /* ------------------------------------------------------------------------------------------------------------ * Static fallback data, see DistrhoPluginInternal.hpp */ @@ -100,11 +101,6 @@ double Plugin::getSampleRate() const noexcept return pData->sampleRate; } -const char* Plugin::getBinaryFilename() const noexcept -{ - return pData->binaryFilename; -} - const char* Plugin::getBundlePath() const noexcept { return pData->bundlePath; diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index c6ebde30..208dc34f 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -31,9 +31,10 @@ static const uint32_t kMaxMidiEvents = 512; // ----------------------------------------------------------------------- // Static data, see DistrhoPlugin.cpp -extern uint32_t d_lastBufferSize; -extern double d_lastSampleRate; -extern bool d_lastCanRequestParameterValueChanges; +extern uint32_t d_nextBufferSize; +extern double d_nextSampleRate; +extern const char* d_nextBundlePath; +extern bool d_nextCanRequestParameterValueChanges; // ----------------------------------------------------------------------- // DSP callbacks @@ -122,7 +123,6 @@ struct Plugin::PrivateData { uint32_t bufferSize; double sampleRate; - char* binaryFilename; char* bundlePath; bool canRequestParameterValueChanges; @@ -151,11 +151,10 @@ struct Plugin::PrivateData { callbacksPtr(nullptr), writeMidiCallbackFunc(nullptr), requestParameterValueChangeCallbackFunc(nullptr), - bufferSize(d_lastBufferSize), - sampleRate(d_lastSampleRate), - binaryFilename(nullptr), - bundlePath(nullptr), - canRequestParameterValueChanges(d_lastCanRequestParameterValueChanges) + bufferSize(d_nextBufferSize), + sampleRate(d_nextSampleRate), + bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr), + canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges) { DISTRHO_SAFE_ASSERT(bufferSize != 0); DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); @@ -230,12 +229,6 @@ struct Plugin::PrivateData { } #endif - if (binaryFilename != nullptr) - { - std::free(binaryFilename); - binaryFilename = nullptr; - } - if (bundlePath != nullptr) { std::free(bundlePath); diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 1448addd..db82f3d0 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -115,7 +115,7 @@ public: #if DISTRHO_PLUGIN_HAS_UI fUI(this, 0, // winId - d_lastSampleRate, + d_nextSampleRate, nullptr, // edit param setParameterValueCallback, setStateCallback, @@ -804,9 +804,9 @@ int main() initSignalHandler(); - d_lastBufferSize = jackbridge_get_buffer_size(client); - d_lastSampleRate = jackbridge_get_sample_rate(client); - d_lastCanRequestParameterValueChanges = true; + d_nextBufferSize = jackbridge_get_buffer_size(client); + d_nextSampleRate = jackbridge_get_sample_rate(client); + d_nextCanRequestParameterValueChanges = true; const PluginJack p(client); diff --git a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp index 6acd028f..f3492ccb 100644 --- a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp +++ b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp @@ -418,9 +418,9 @@ private: static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, ulong sampleRate) { - if (d_lastBufferSize == 0) - d_lastBufferSize = 2048; - d_lastSampleRate = sampleRate; + if (d_nextBufferSize == 0) + d_nextBufferSize = 2048; + d_nextSampleRate = sampleRate; return new PluginLadspaDssi(); } @@ -551,11 +551,11 @@ static const struct DescriptorInitializer DescriptorInitializer() { // Create dummy plugin to get data from - d_lastBufferSize = 512; - d_lastSampleRate = 44100.0; + d_nextBufferSize = 512; + d_nextSampleRate = 44100.0; const PluginExporter plugin(nullptr, nullptr, nullptr); - d_lastBufferSize = 0; - d_lastSampleRate = 0.0; + d_nextBufferSize = 0; + d_nextSampleRate = 0.0; // Get port count, init ulong port = 0; diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 849438b6..98b9d884 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -1296,7 +1296,7 @@ private: // ----------------------------------------------------------------------- -static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features) +static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char* bundlePath, const LV2_Feature* const* features) { const LV2_Options_Option* options = nullptr; const LV2_URID_Map* uridMap = nullptr; @@ -1339,7 +1339,7 @@ static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, cons mod_license_check(features, DISTRHO_PLUGIN_URI); #endif - d_lastBufferSize = 0; + d_nextBufferSize = 0; bool usingNominal = false; for (int i=0; options[i].key != 0; ++i) @@ -1348,7 +1348,7 @@ static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, cons { if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int)) { - d_lastBufferSize = *(const int*)options[i].value; + d_nextBufferSize = *(const int*)options[i].value; usingNominal = true; } else @@ -1361,7 +1361,7 @@ static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, cons if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength)) { if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int)) - d_lastBufferSize = *(const int*)options[i].value; + d_nextBufferSize = *(const int*)options[i].value; else d_stderr("Host provides maxBlockLength but has wrong value type"); @@ -1369,14 +1369,15 @@ static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, cons } } - if (d_lastBufferSize == 0) + if (d_nextBufferSize == 0) { d_stderr("Host does not provide nominalBlockLength or maxBlockLength options"); - d_lastBufferSize = 2048; + d_nextBufferSize = 2048; } - d_lastSampleRate = sampleRate; - d_lastCanRequestParameterValueChanges = ctrlInPortChangeReq != nullptr; + d_nextSampleRate = sampleRate; + d_nextBundlePath = bundlePath; + d_nextCanRequestParameterValueChanges = ctrlInPortChangeReq != nullptr; return new PluginLv2(sampleRate, uridMap, worker, ctrlInPortChangeReq, usingNominal); } diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 1456696e..36402eaf 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -224,11 +224,11 @@ void lv2_generate_ttl(const char* const basename) USE_NAMESPACE_DISTRHO // Dummy plugin to get data from - d_lastBufferSize = 512; - d_lastSampleRate = 44100.0; + d_nextBufferSize = 512; + d_nextSampleRate = 44100.0; PluginExporter plugin(nullptr, nullptr, nullptr); - d_lastBufferSize = 0; - d_lastSampleRate = 0.0; + d_nextBufferSize = 0; + d_nextSampleRate = 0.0; const String pluginDLL(basename); const String pluginTTL(pluginDLL + ".ttl"); diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 841dab42..b8121c10 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1403,9 +1403,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t if (doInternalInit) { // set valid but dummy values - d_lastBufferSize = 512; - d_lastSampleRate = 44100.0; - d_lastCanRequestParameterValueChanges = true; + d_nextBufferSize = 512; + d_nextSampleRate = 44100.0; + d_nextCanRequestParameterValueChanges = true; } // Create dummy plugin to get data from @@ -1414,9 +1414,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t if (doInternalInit) { // unset - d_lastBufferSize = 0; - d_lastSampleRate = 0.0; - d_lastCanRequestParameterValueChanges = false; + d_nextBufferSize = 0; + d_nextSampleRate = 0.0; + d_nextCanRequestParameterValueChanges = false; *(PluginExporter**)ptr = &plugin; return 0; @@ -1437,15 +1437,15 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t audioMasterCallback audioMaster = (audioMasterCallback)obj->audioMaster; - d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); - d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); - d_lastCanRequestParameterValueChanges = true; + d_nextBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f); + d_nextSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f); + d_nextCanRequestParameterValueChanges = true; // some hosts are not ready at this point or return 0 buffersize/samplerate - if (d_lastBufferSize == 0) - d_lastBufferSize = 2048; - if (d_lastSampleRate <= 0.0) - d_lastSampleRate = 44100.0; + if (d_nextBufferSize == 0) + d_nextBufferSize = 2048; + if (d_nextSampleRate <= 0.0) + d_nextSampleRate = 44100.0; obj->plugin = new PluginVst(audioMaster, effect); return 1; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index d7695f05..29df4edd 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -3094,8 +3094,8 @@ struct dpf_audio_processor : v3_audio_processor_cpp { PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - d_lastBufferSize = setup->max_block_size; - d_lastSampleRate = setup->sample_rate; + d_nextBufferSize = setup->max_block_size; + d_nextSampleRate = setup->sample_rate; return processor->vst3->setupProcessing(setup); } @@ -3389,12 +3389,12 @@ struct dpf_component : v3_component_cpp { host = component->hostContextFromFactory; // default early values - if (d_lastBufferSize == 0) - d_lastBufferSize = 2048; - if (d_lastSampleRate <= 0.0) - d_lastSampleRate = 44100.0; + if (d_nextBufferSize == 0) + d_nextBufferSize = 2048; + if (d_nextSampleRate <= 0.0) + d_nextSampleRate = 44100.0; - d_lastCanRequestParameterValueChanges = true; + d_nextCanRequestParameterValueChanges = true; component->vst3 = new PluginVst3(host); return V3_OK; @@ -3541,13 +3541,13 @@ struct dpf_component : v3_component_cpp { static const PluginExporter& _getPluginInfo() { - d_lastBufferSize = 512; - d_lastSampleRate = 44100.0; - d_lastCanRequestParameterValueChanges = true; + d_nextBufferSize = 512; + d_nextSampleRate = 44100.0; + d_nextCanRequestParameterValueChanges = true; static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr); - d_lastBufferSize = 0; - d_lastSampleRate = 0.0; - d_lastCanRequestParameterValueChanges = false; + d_nextBufferSize = 0; + d_nextSampleRate = 0.0; + d_nextCanRequestParameterValueChanges = false; return gPluginInfo; } diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 920a8725..c4797e53 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -32,14 +32,16 @@ START_NAMESPACE_DISTRHO -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI /* ------------------------------------------------------------------------------------------------------------ * Static data, see DistrhoUIInternal.hpp */ +const char* g_nextBundlePath = nullptr; +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI uintptr_t g_nextWindowId = 0; double g_nextScaleFactor = 1.0; -const char* g_nextBundlePath = nullptr; +#endif +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI /* ------------------------------------------------------------------------------------------------------------ * get global scale factor */ @@ -217,11 +219,6 @@ double UI::getSampleRate() const noexcept return uiData->sampleRate; } -const char* UI::getBinaryFilename() const noexcept -{ - return uiData->binaryFilename; -} - const char* UI::getBundlePath() const noexcept { return uiData->bundlePath; @@ -270,7 +267,7 @@ void* UI::getPluginInstancePointer() const noexcept #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI /* ------------------------------------------------------------------------------------------------------------ - * External UI helpers */ + * External UI helpers (static calls) */ const char* UI::getNextBundlePath() noexcept { diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index a9e9a6b1..e4372bd7 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -24,10 +24,10 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- // Static data, see DistrhoUI.cpp +extern const char* g_nextBundlePath; #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI extern uintptr_t g_nextWindowId; extern double g_nextScaleFactor; -extern const char* g_nextBundlePath; #endif // ----------------------------------------------------------------------- @@ -77,19 +77,19 @@ public: uiData->setSizeCallbackFunc = setSizeCall; uiData->fileRequestCallbackFunc = fileRequestCall; + g_nextBundlePath = bundlePath; #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI g_nextWindowId = winId; g_nextScaleFactor = scaleFactor; - g_nextBundlePath = bundlePath; #endif UI::PrivateData::s_nextPrivateData = uiData; UI* const uiPtr = createUI(); + g_nextBundlePath = nullptr; #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI g_nextWindowId = 0; g_nextScaleFactor = 0.0; - g_nextBundlePath = nullptr; #else // enter context called in the PluginWindow constructor, see DistrhoUIPrivateData.hpp uiData->window->leaveContext(); diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 9e479a5c..0b03160a 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -308,7 +308,6 @@ struct UI::PrivateData { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) char* uiStateFileKeyRequest; #endif - char* binaryFilename; char* bundlePath; // Callbacks @@ -333,7 +332,6 @@ struct UI::PrivateData { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) uiStateFileKeyRequest(nullptr), #endif - binaryFilename(nullptr), bundlePath(nullptr), callbacksPtr(nullptr), editParamCallbackFunc(nullptr), @@ -374,7 +372,6 @@ struct UI::PrivateData { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) std::free(uiStateFileKeyRequest); #endif - std::free(binaryFilename); std::free(bundlePath); } diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index d743fd51..aaf59af2 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -14,6 +14,28 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef DISTRHO_IS_STANDALONE +# error Wrong build configuration +#endif + +#include "extra/String.hpp" + +#ifndef DISTRHO_OS_WINDOWS +# include +#endif + +#if defined(DISTRHO_OS_WINDOWS) && !DISTRHO_IS_STANDALONE +static HINSTANCE hInstance = nullptr; + +DISTRHO_PLUGIN_EXPORT +BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) + hInstance = hInst; + return 1; +} +#endif + START_NAMESPACE_DISTRHO #ifdef DISTRHO_PLUGIN_TARGET_JACK @@ -21,6 +43,31 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- +const char* getBinaryFilename() +{ + static String filename; + + if (filename.isNotEmpty()) + return filename; + +#ifdef DISTRHO_OS_WINDOWS +# if DISTRHO_IS_STANDALONE + // TODO +# else + CHAR filenameBuf[MAX_PATH + 256]; + filenameBuf[0] = '\0'; + GetModuleFileName(hInstance, filenameBuf, sizeof(filenameBuf)); + filename = filenameBuf; +# endif +#else + Dl_info info; + dladdr((void*)getBinaryFilename, &info); + filename = info.dli_fname; +#endif + + return filename; +} + const char* getPluginFormatName() noexcept { #if defined(DISTRHO_PLUGIN_TARGET_CARLA) From 26eb9dd4e461d311f021ac26237d176e2cfb14dc Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 02:29:34 +0100 Subject: [PATCH 201/504] Implement Plugin::getBundlePath() for VST2 --- distrho/src/DistrhoPluginVST2.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index b8121c10..c76e11da 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -15,6 +15,7 @@ */ #include "DistrhoPluginInternal.hpp" +#include "DistrhoPluginUtils.hpp" #include "../extra/ScopedSafeLocale.hpp" #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI @@ -1680,6 +1681,32 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0) return nullptr; + // 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 (tmpPath.endsWith(".vst")) + { + bundlePath = tmpPath; + d_nextBundlePath = bundlePath.buffer(); + } +#endif + } + // first internal init PluginExporter* plugin = nullptr; vst_dispatcherCallback(nullptr, -1729, 0xdead, 0xf00d, &plugin, 0.0f); From 1af66e49db01b5e2a5f90e85fe748c162bb83a48 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 02:35:56 +0100 Subject: [PATCH 202/504] Make getBinaryFilename work in standalone windows too --- distrho/src/DistrhoUtils.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index aaf59af2..0c087c27 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -38,9 +38,6 @@ BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) START_NAMESPACE_DISTRHO -#ifdef DISTRHO_PLUGIN_TARGET_JACK -#endif - // ----------------------------------------------------------------------- const char* getBinaryFilename() @@ -51,14 +48,13 @@ const char* getBinaryFilename() return filename; #ifdef DISTRHO_OS_WINDOWS -# if DISTRHO_IS_STANDALONE - // TODO -# else - CHAR filenameBuf[MAX_PATH + 256]; +# if !DISTRHO_IS_STANDALONE + constexpr const HINSTANCE hInstance = nullptr; +# endif + CHAR filenameBuf[MAX_PATH]; filenameBuf[0] = '\0'; GetModuleFileName(hInstance, filenameBuf, sizeof(filenameBuf)); filename = filenameBuf; -# endif #else Dl_info info; dladdr((void*)getBinaryFilename, &info); From 1da1c811c765689790c8bf4290b2f6939f8046f3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 12:02:03 +0100 Subject: [PATCH 203/504] Start of getResourcePath utility Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 1 + distrho/DistrhoPluginUtils.hpp | 18 ++++++++++++ distrho/src/DistrhoPluginVST2.cpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 15 ++++++++++ distrho/src/DistrhoUtils.cpp | 46 ++++++++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index cac54c3b..5b2cdb83 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -841,6 +841,7 @@ public: Get the bundle path where the plugin resides. Can return null if the plugin is not available in a bundle (if it is a single binary). @see getBinaryFilename + @see getResourcePath */ const char* getBundlePath() const noexcept; diff --git a/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index e2d15d7b..da63cb63 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/distrho/DistrhoPluginUtils.hpp @@ -40,6 +40,24 @@ const char* getBinaryFilename(); */ const char* getPluginFormatName() noexcept; +/** + Get the path to where resources are stored within the plugin bundle.@n + Requires a valid plugin bundle path. + + Returns a path inside the bundle where the plugin is meant to store its resources in.@n + This path varies between systems and plugin formats, like so: + + - LV2: /resources (can be stored anywhere inside the bundle really, DPF just uses this one) + - VST2 macOS: /Contents/Resources + - VST2 non-macOS: /resources (see note) + + The other non-mentioned formats do not support bundles.@n + + @note For VST2 on non-macOS systems, this assumes you have your plugin inside a dedicated directory + rather than only shipping with the binary (e.g. /myplugin.dll) +*/ +const char* getResourcePath(const char* bundlePath) noexcept; + // ----------------------------------------------------------------------------------------------------------- // Plugin helper classes diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index c76e11da..147bf0c1 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -15,7 +15,7 @@ */ #include "DistrhoPluginInternal.hpp" -#include "DistrhoPluginUtils.hpp" +#include "../DistrhoPluginUtils.hpp" #include "../extra/ScopedSafeLocale.hpp" #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 29df4edd..d4d797e4 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -15,6 +15,7 @@ */ #include "DistrhoPluginInternal.hpp" +#include "../DistrhoPluginUtils.hpp" #include "../extra/ScopedPointer.hpp" #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI @@ -3851,6 +3852,20 @@ bool ENTRYFNNAME(void*); bool ENTRYFNNAME(void*) { + // find plugin bundle + static String bundlePath; + if (bundlePath.isEmpty()) + { + String tmpPath(getBinaryFilename()); + tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); + tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); + DISTRHO_SAFE_ASSERT_RETURN(tmpPath.endsWith("/Contents"), true); + + tmpPath.truncate(tmpPath.rfind('/')); + bundlePath = tmpPath; + d_nextBundlePath = bundlePath.buffer(); + } + return true; } diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index 0c087c27..c3e00651 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -18,7 +18,7 @@ # error Wrong build configuration #endif -#include "extra/String.hpp" +#include "../extra/String.hpp" #ifndef DISTRHO_OS_WINDOWS # include @@ -85,6 +85,50 @@ const char* getPluginFormatName() noexcept #endif } +const char* getResourcePath(const char* const bundlePath) noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(bundlePath != nullptr, nullptr); + +#if defined(DISTRHO_PLUGIN_TARGET_LV2) + static String bundlePathLV2; + + if (bundlePathLV2.isEmpty()) + { + bundlePathLV2 += bundlePath; + bundlePathLV2 += DISTRHO_OS_SEP_STR "resources"; + } + + return bundlePathLV2.buffer(); +#elif defined(DISTRHO_PLUGIN_TARGET_VST2) + static String bundlePathVST2; + + if (bundlePathVST2.isEmpty()) + { + bundlePathVST2 += bundlePath; +# ifdef DISTRHO_OS_MAC + bundlePathVST2 += "/Contents/Resources"; +# else + DISTRHO_SAFE_ASSERT_RETURN(bundlePathVST2.endsWith(".vst"), nullptr); + bundlePathVST2 += DISTRHO_OS_SEP_STR "resources"; +# endif + } + + return bundlePathVST2.buffer(); +#elif defined(DISTRHO_PLUGIN_TARGET_VST3) + static String bundlePathVST3; + + if (bundlePathVST3.isEmpty()) + { + bundlePathVST3 += bundlePath; + bundlePathVST3 += "/Contents/Resources"; + } + + return bundlePathVST3.buffer(); +#endif + + return nullptr; +} + // ----------------------------------------------------------------------- END_NAMESPACE_DISTRHO From b9a7aaa58745d51937a36080e532093b6a212945 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 12:13:00 +0100 Subject: [PATCH 204/504] Fix build after latest changes Signed-off-by: falkTX --- Makefile.plugins.mk | 6 +++++- distrho/DistrhoUIMain.cpp | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 050f71e5..666cb89e 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -51,7 +51,6 @@ JACK_LIBS += -framework CoreAudio -framework CoreFoundation else ifeq ($(WINDOWS),true) JACK_LIBS += -lksuser -lmfplat -lmfuuid -lole32 -lwinmm -lwmcodecdspuuid else ifneq ($(HAIKU),true) -JACK_LIBS = -ldl ifeq ($(HAVE_ALSA),true) JACK_FLAGS += $(ALSA_FLAGS) JACK_LIBS += $(ALSA_LIBS) @@ -68,6 +67,11 @@ endif # backwards compat BASE_FLAGS += -DHAVE_JACK +# always needed +ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +LINK_FLAGS += -ldl +endif + # --------------------------------------------------------------------------------------------------------------------- # Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index 0751bbb8..f186e6a5 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -32,8 +32,8 @@ # error unsupported format #endif -#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS -# if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) +#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && !DISTRHO_PLUGIN_TARGET_JACK && !DISTRHO_PLUGIN_TARGET_VST2 && !DISTRHO_PLUGIN_TARGET_VST3 +# ifdef DISTRHO_PLUGIN_TARGET_DSSI # define DISTRHO_IS_STANDALONE 1 # else # define DISTRHO_IS_STANDALONE 0 From 4ecbd24570b780363bf3cb588fb23bb210ee944f Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 13:54:58 +0100 Subject: [PATCH 205/504] Fix build Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 12 +++++++----- distrho/src/DistrhoUtils.cpp | 4 +++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index b32d84d3..6c8e6724 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -122,6 +122,10 @@ function(dpf_add_plugin NAME) target_include_directories("${NAME}" PUBLIC "${DPF_ROOT_DIR}/distrho") + if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) + target_link_libraries("${NAME}" PRIVATE "dl") + endif() + if(_dgl_library) # make sure that all code will see DGL_* definitions target_link_libraries("${NAME}" PUBLIC @@ -135,6 +139,9 @@ function(dpf_add_plugin NAME) if(_dgl_library) dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI}) target_link_libraries("${NAME}-ui" PUBLIC "${NAME}" ${_dgl_library}) + if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) + target_link_libraries("${NAME}-ui" PRIVATE "dl") + endif() # add the files containing Objective-C classes, recompiled under namespace dpf__add_plugin_specific_ui_sources("${NAME}-ui") else() @@ -185,11 +192,6 @@ function(dpf__build_jack NAME DGL_LIBRARY) RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" OUTPUT_NAME "${NAME}") - # Note: libjack will be linked at runtime - if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) - target_link_libraries("${NAME}-jack" PRIVATE "dl") - endif() - # for RtAudio native fallback if(APPLE) find_library(APPLE_COREAUDIO_FRAMEWORK "CoreAudio") diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index c3e00651..fe5d7573 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -20,7 +20,9 @@ #include "../extra/String.hpp" -#ifndef DISTRHO_OS_WINDOWS +#ifdef DISTRHO_OS_WINDOWS +# include +#else # include #endif From abdb896a2a0e42cef67c6149e87988d43f7e46f1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 13:58:33 +0100 Subject: [PATCH 206/504] Only use AudioMidiSyncHelper when num-outputs > 0 Signed-off-by: falkTX --- distrho/DistrhoPluginUtils.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index da63cb63..bd0273d7 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/distrho/DistrhoPluginUtils.hpp @@ -61,6 +61,7 @@ const char* getResourcePath(const char* bundlePath) noexcept; // ----------------------------------------------------------------------------------------------------------- // Plugin helper classes +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 /** Handy class to help keep audio buffer in sync with incoming MIDI events. To use it, create a local variable (on the stack) and call nextEvent() until it returns false. @@ -189,6 +190,7 @@ private: uint32_t remainingMidiEventCount; uint32_t totalFramesUsed; }; +#endif // ----------------------------------------------------------------------------------------------------------- From 70ccd68870cbda9c081863d8b47dfbdafff3bd9d Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 14:48:06 +0100 Subject: [PATCH 207/504] Fix a typo Signed-off-by: falkTX --- distrho/src/DistrhoUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index fe5d7573..153f0731 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -50,7 +50,7 @@ const char* getBinaryFilename() return filename; #ifdef DISTRHO_OS_WINDOWS -# if !DISTRHO_IS_STANDALONE +# if DISTRHO_IS_STANDALONE constexpr const HINSTANCE hInstance = nullptr; # endif CHAR filenameBuf[MAX_PATH]; From d5267f11d9fb3deee3ab4367f1c42fd281fb6fa9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 15:02:11 +0100 Subject: [PATCH 208/504] Fix C++98 build Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 98b9d884..60378f2a 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -37,10 +37,6 @@ # include "libmodla.h" #endif -#ifdef noexcept -# undef noexcept -#endif - #include #ifndef DISTRHO_PLUGIN_URI From 01261953a36c3eedca5fefb374ed2e98c58ca85d Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 15:11:32 +0100 Subject: [PATCH 209/504] Fix no namespace build Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index d4d797e4..48dcb64b 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -3853,6 +3853,7 @@ bool ENTRYFNNAME(void*); bool ENTRYFNNAME(void*) { // find plugin bundle + USE_NAMESPACE_DISTRHO; static String bundlePath; if (bundlePath.isEmpty()) { From 437d080a68e032dbe7b192bfa1f9f6a1adc693c9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 16:24:21 +0100 Subject: [PATCH 210/504] Setup bundle path for LV2 ttl export Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 36402eaf..dd4aa285 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -15,6 +15,7 @@ */ #include "DistrhoPluginInternal.hpp" +#include "../DistrhoPluginUtils.hpp" #include "lv2/atom.h" #include "lv2/buf-size.h" @@ -223,6 +224,10 @@ void lv2_generate_ttl(const char* const basename) { USE_NAMESPACE_DISTRHO + String bundlePath(getBinaryFilename()); + bundlePath.truncate(bundlePath.rfind(DISTRHO_OS_SEP)); + d_nextBundlePath = bundlePath.buffer(); + // Dummy plugin to get data from d_nextBufferSize = 512; d_nextSampleRate = 44100.0; From a88514b734f01bf4eed68e6e2097712ff01a60fe Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 21 Oct 2021 16:24:44 +0100 Subject: [PATCH 211/504] Add new USE_VST2_BUNDLE makefile option, WIP Signed-off-by: falkTX --- Makefile.plugins.mk | 41 ++++++++++++++++++++++------------- utils/generate-vst-bundles.sh | 3 +++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 666cb89e..ce4490ba 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -72,29 +72,40 @@ ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) LINK_FLAGS += -ldl endif +# --------------------------------------------------------------------------------------------------------------------- +# Set files to build + +OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o) +OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o) + +ifeq ($(MACOS),true) +OBJS_UI += $(BUILD_DIR)/DistrhoUI_macOS_$(NAME).mm.o +endif + +# --------------------------------------------------------------------------------------------------------------------- +# Set VST2 filename, either single binary or inside a bundle + +ifeq ($(MACOS),true) +VST2_FILENAME = $(NAME).vst/Contents/$(NAME) +else ifeq ($(USE_VST2_BUNDLE),true) +VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT) +else +VST2_FILENAME = $(NAME)-vst$(LIB_EXT) +endif + # --------------------------------------------------------------------------------------------------------------------- # Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html ifeq ($(LINUX),true) -VST3_FILENAME = $(TARGET_PROCESSOR)-linux/$(NAME).so +VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so endif ifeq ($(MACOS),true) ifneq ($(MACOS_OLD),true) -VST3_FILENAME = MacOS/$(NAME) +VST3_FILENAME = $(NAME).vst3/Contents/MacOS/$(NAME) endif endif ifeq ($(WINDOWS),true) -VST3_FILENAME = $(TARGET_PROCESSOR)-win/$(NAME).vst3 -endif - -# --------------------------------------------------------------------------------------------------------------------- -# Set files to build - -OBJS_DSP = $(FILES_DSP:%=$(BUILD_DIR)/%.o) -OBJS_UI = $(FILES_UI:%=$(BUILD_DIR)/%.o) - -ifeq ($(MACOS),true) -OBJS_UI += $(BUILD_DIR)/DistrhoUI_macOS_$(NAME).mm.o +VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-win/$(NAME).vst3 endif # --------------------------------------------------------------------------------------------------------------------- @@ -107,9 +118,9 @@ dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT) lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) -vst2 = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) +vst2 = $(TARGET_DIR)/$(VST2_FILENAME) ifneq ($(VST3_FILENAME),) -vst3 = $(TARGET_DIR)/$(NAME).vst3/Contents/$(VST3_FILENAME) +vst3 = $(TARGET_DIR)/$(VST3_FILENAME) endif # --------------------------------------------------------------------------------------------------------------------- diff --git a/utils/generate-vst-bundles.sh b/utils/generate-vst-bundles.sh index e1d73f25..148c849d 100755 --- a/utils/generate-vst-bundles.sh +++ b/utils/generate-vst-bundles.sh @@ -1,5 +1,8 @@ #!/bin/bash +echo "WARNING: generate-vst-bundles.sh script is no longer used" +exit 0 + set -e if [ -d bin ]; then From e2dcc29bfb65341f51736ef4e09b01baedb0d068 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Oct 2021 00:41:27 +0100 Subject: [PATCH 212/504] Always follow symlinks when creating macOS package --- utils/package-osx-bundles.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/package-osx-bundles.sh b/utils/package-osx-bundles.sh index 12b62e77..cb3ae62d 100755 --- a/utils/package-osx-bundles.sh +++ b/utils/package-osx-bundles.sh @@ -17,9 +17,10 @@ rm -rf vst2 rm -rf vst3 mkdir lv2 vst2 vst3 -mv *.lv2 lv2/ -mv *.vst vst2/ -mv *.vst3 vst3/ +cp -RL *.lv2 lv2/ +cp -RL *.vst vst2/ +cp -RL *.vst3 vst3/ +rm -rf *.lv2 *.vst *.vst3 pkgbuild \ --identifier "studio.kx.distrho.plugins.${SNAME}.lv2bundles" \ From 2d1ec00e27c7ac2f5f1fc84ebaacf0a49b220502 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Oct 2021 01:01:54 +0100 Subject: [PATCH 213/504] Proper setup of macOS VST bundles after the recent changes --- Makefile.plugins.mk | 36 ++++++++++++++++++++---- utils/plugin.vst/Contents/MacOS/deleteme | 1 - 2 files changed, 30 insertions(+), 7 deletions(-) delete mode 100644 utils/plugin.vst/Contents/MacOS/deleteme diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index ce4490ba..d75b2739 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -86,7 +86,8 @@ endif # Set VST2 filename, either single binary or inside a bundle ifeq ($(MACOS),true) -VST2_FILENAME = $(NAME).vst/Contents/$(NAME) +VST2_CONTENTS = $(NAME).vst/Contents +VST2_FILENAME = $(VST2_CONTENTS)/MacOS/$(NAME) else ifeq ($(USE_VST2_BUNDLE),true) VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT) else @@ -100,9 +101,8 @@ ifeq ($(LINUX),true) VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so endif ifeq ($(MACOS),true) -ifneq ($(MACOS_OLD),true) -VST3_FILENAME = $(NAME).vst3/Contents/MacOS/$(NAME) -endif +VST3_CONTENTS = $(NAME).vst3/Contents +VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) endif ifeq ($(WINDOWS),true) VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-win/$(NAME).vst3 @@ -123,6 +123,15 @@ ifneq ($(VST3_FILENAME),) vst3 = $(TARGET_DIR)/$(VST3_FILENAME) endif +ifeq ($(MACOS),true) +vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist +vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/PkgInfo +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 +endif + # --------------------------------------------------------------------------------------------------------------------- # Set plugin symbols to export @@ -401,7 +410,7 @@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) # --------------------------------------------------------------------------------------------------------------------- # VST2 -vst2 vst: $(vst2) +vst2 vst: $(vst2) $(vst2files) ifeq ($(HAVE_DGL),true) $(vst2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.o $(DGL_LIB) @@ -415,7 +424,7 @@ endif # --------------------------------------------------------------------------------------------------------------------- # VST3 -vst3: $(vst3) +vst3: $(vst3) $(vst3files) ifeq ($(HAVE_DGL),true) $(vst3): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.o $(DGL_LIB) @@ -426,6 +435,21 @@ endif @echo "Creating VST3 plugin for $(NAME)" $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ +# --------------------------------------------------------------------------------------------------------------------- +# macOS files + +$(TARGET_DIR)/%/Contents/Info.plist: $(DPF_PATH)/utils/plugin.vst/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 + -@mkdir -p $(shell dirname $@) + $(SILENT)cp $< $@ + +$(TARGET_DIR)/%/Resources/empty.lproj: $(DPF_PATH)/utils/plugin.vst/Contents/Resources/empty.lproj + -@mkdir -p $(shell dirname $@) + $(SILENT)cp $< $@ + # --------------------------------------------------------------------------------------------------------------------- -include $(OBJS_DSP:%.o=%.d) diff --git a/utils/plugin.vst/Contents/MacOS/deleteme b/utils/plugin.vst/Contents/MacOS/deleteme deleted file mode 100644 index 8d1c8b69..00000000 --- a/utils/plugin.vst/Contents/MacOS/deleteme +++ /dev/null @@ -1 +0,0 @@ - From fe8f0eb69c3fa93bf3a0d5185105e409426b056b Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 23 Oct 2021 20:14:15 +0100 Subject: [PATCH 214/504] Do not use audio input device in RtAudio fallback if not needed --- distrho/src/jackbridge/RtAudioBridge.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index 0699b243..bf42738f 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -90,9 +90,14 @@ struct RtAudioBridge { uint rtAudioBufferFrames = 512; +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 RtAudio::StreamParameters inParams; + RtAudio::StreamParameters* const inParamsPtr = &inParams; inParams.deviceId = rtAudio->getDefaultInputDevice(); inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS; +#else + RtAudio::StreamParameters* const inParamsPtr = nullptr; +#endif RtAudio::StreamParameters outParams; outParams.deviceId = rtAudio->getDefaultOutputDevice(); @@ -102,7 +107,7 @@ struct RtAudioBridge { opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_ALSA_USE_DEFAULT; try { - rtAudio->openStream(&outParams, &inParams, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, RtAudioCallback, this, &opts, nullptr); + rtAudio->openStream(&outParams, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, RtAudioCallback, this, &opts, nullptr); } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); handle = rtAudio; From 145f3f9a252cf76d79da451ccfdca1c32ede0050 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 24 Oct 2021 14:23:32 +0100 Subject: [PATCH 215/504] Add some more basic documentation Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 117 ++++++++++++++++++++++++++++++-------- distrho/DistrhoPlugin.hpp | 5 +- 2 files changed, 96 insertions(+), 26 deletions(-) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 6805f4a1..e5aea8ca 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -31,23 +31,39 @@ START_NAMESPACE_DISTRHO It allows developers to create plugins with custom UIs using a simple C++ API.@n The framework facilitates exporting various different plugin formats from the same code-base. - DPF can build for LADSPA, DSSI, LV2 and VST2 formats.@n + DPF can build for LADSPA, DSSI, LV2, VST2 and VST3 formats.@n A JACK/Standalone mode is also available, allowing you to quickly test plugins. @section Macros You start by creating a "DistrhoPluginInfo.h" file describing the plugin via macros, see @ref PluginMacros.@n - This file is included in the main DPF code to select which features to activate for each plugin format. + This file is included during compilation of the main DPF code to select which features to activate for each plugin format. For example, a plugin (with %UI) that use states will require LV2 hosts to support Atom and Worker extensions for - message passing from the %UI to the plugin.@n + message passing from the %UI to the (DSP) plugin.@n If your plugin does not make use of states, the Worker extension is not set as a required feature. @section Plugin The next step is to create your plugin code by subclassing DPF's Plugin class.@n You need to pass the number of parameters in the constructor and also the number of programs and states, if any. - Here's an example of an audio plugin that simply mutes the host output: + Do note all of DPF code is within its own C++ namespace (@b DISTRHO for DSP/plugin stuff, @b DGL for UI stuff).@n + You can use @ref START_NAMESPACE_DISTRHO / @ref END_NAMESPACE_DISTRHO combo around your code, or globally set @ref USE_NAMESPACE_DISTRHO.@n + These are defined as compiler macros so that you can override the namespace name during build. When in doubt, just follow the examples. + + @section Examples + Let's begin with some examples.@n + Here is one of a stereo audio plugin that simply mutes the host output: @code + /* Make DPF related classes available for us to use without any extra namespace references */ + USE_NAMESPACE_DISTRHO; + + /** + Our custom plugin class. + Subclassing `Plugin` from DPF is how this all works. + + By default, only information-related functions and `run` are pure virtual (that is, must be reimplemented). + When enabling certain features (such as programs or states, more on that below), a few extra functions also need to be reimplemented. + */ class MutePlugin : public Plugin { public: @@ -111,7 +127,6 @@ START_NAMESPACE_DISTRHO /** Run/process function for plugins without MIDI input. - NOTE: Some parameters might be null if there are no audio inputs or outputs. */ void run(const float**, float** outputs, uint32_t frames) override { @@ -123,11 +138,20 @@ START_NAMESPACE_DISTRHO std::memset(outL, 0, sizeof(float)*frames); std::memset(outR, 0, sizeof(float)*frames); } - }; + + /** + Create an instance of the Plugin class. + This is the entry point for DPF plugins. + DPF will call this to either create an instance of your plugin for the host or to fetch some initial information for internal caching. + */ + Plugin* createPlugin() + { + return new MutePlugin(); + } @endcode - See the Plugin class for more information and to understand what each function does. + See the Plugin class for more information. @section Parameters A plugin is nothing without parameters.@n @@ -135,12 +159,12 @@ START_NAMESPACE_DISTRHO 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. - Input parameters are "read-only": the plugin can read them but not change them. - (the exception being when changing programs, more on that below)@n + 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 It's the host responsibility to save, restore and set input parameters. Output parameters can be changed at anytime by the plugin.@n - The host will simply read their values and not change them. + The host will simply read their values and never change them. Here's an example of an audio plugin that has 1 input parameter: @code @@ -250,8 +274,8 @@ START_NAMESPACE_DISTRHO See the Parameter struct for more information about parameters. @section Programs - Programs in DPF refer to plugin-side presets (usually called "factory presets"), - an initial set of presets provided by plugin authors included in the actual plugin. + Programs in DPF refer to plugin-side presets (usually called "factory presets").@n + This is meant as an initial set of presets provided by plugin authors included in the actual plugin. To use programs you must first enable them by setting @ref DISTRHO_PLUGIN_WANT_PROGRAMS to 1 in your DistrhoPluginInfo.h file.@n When enabled you'll need to override 2 new function in your plugin code, @@ -331,12 +355,9 @@ START_NAMESPACE_DISTRHO */ void initProgramName(uint32_t index, String& programName) { - switch(index) - { - case 0: - programName = "Default"; - break; - } + // we only have one program so we can skip checking the index + + programName = "Default"; } /* ---------------------------------------------------------------------------------------- @@ -377,13 +398,10 @@ START_NAMESPACE_DISTRHO */ void loadProgram(uint32_t index) { - switch(index) - { - case 0: - fGainL = 1.0f; - fGainR = 1.0f; - break; - } + // same as before, ignore index check + + fGainL = 1.0f; + fGainR = 1.0f; } /* ---------------------------------------------------------------------------------------- @@ -774,6 +792,55 @@ START_NAMESPACE_DISTRHO /** @} */ +/* ------------------------------------------------------------------------------------------------------------ + * Namespace Macros */ + +/** + @defgroup NamespaceMacros Namespace Macros + + C Macros to use and customize DPF namespaces. + + These are macros that serve as helpers around C++ namespaces, and also as a way to set custom namespaces during a build. + @{ + */ + +/** + Compiler macro that sets the C++ namespace for DPF plugins.@n + If unset during build, it will use the name @b DISTRHO by default. + + Unless you know exactly what you are doing, you do need to modify this value.@n + The only probable useful case for customizing it is if you are building a big collection of very similar DPF-based plugins in your application.@n + For example, having 2 different versions of the same plugin that should behave differently but still exist within the same binary. + + On macOS (where due to Objective-C restrictions all code that interacts with Cocoa needs to be in a flat namespace), + DPF will automatically use the plugin name as prefix to flat namespace functions in order to avoid conflicts. + + So, basically, it is DPF's job to make sure plugin binaries are 100% usable as-is.@n + You typically do not need to care about this at all. + */ +#define DISTRHO_NAMESPACE DISTRHO + +/** + Compiler macro that begins the C++ namespace for @b DISTRHO, as needed for (the DSP side of) plugins.@n + All classes in DPF are within this namespace except for UI/graphics stuff. + @see END_NAMESPACE_DISTRHO + */ +#define START_NAMESPACE_DISTRHO namespace DISTRHO_NAMESPACE { + +/** + Close the namespace previously started by @ref START_NAMESPACE_DISTRHO.@n + This doesn't really need to be a macro, it is just prettier/more consistent that way. + */ +#define END_NAMESPACE_DISTRHO } + +/** + Make the @b DISTRHO namespace available in the current function scope.@n + This is not set by default in order to avoid conflicts with commonly used names such as "Parameter" and "Plugin". + */ +#define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE; + +/** @} */ + // ----------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 5b2cdb83..c59a9e34 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -1102,7 +1102,10 @@ private: */ /** - TODO. + Create an instance of the Plugin class.@n + This is the entry point for DPF plugins.@n + DPF will call this to either create an instance of your plugin for the host + or to fetch some initial information for internal caching. */ extern Plugin* createPlugin(); From 1d66bb2dd633aa6df30850bae411484e3f4434fe Mon Sep 17 00:00:00 2001 From: lucianoiam Date: Sun, 24 Oct 2021 23:23:12 +0200 Subject: [PATCH 216/504] Return abs. path in getBinaryFilename() Linux/Jack (#344) * Return abs. path in getBinaryFilename() Linux/Jack Ensure this function always returns an absolute path by calling realpath() * Adjust includes in b1294ec --- distrho/src/DistrhoUtils.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index 153f0731..7086ae0a 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -24,6 +24,8 @@ # include #else # include +# include +# include #endif #if defined(DISTRHO_OS_WINDOWS) && !DISTRHO_IS_STANDALONE @@ -60,7 +62,8 @@ const char* getBinaryFilename() #else Dl_info info; dladdr((void*)getBinaryFilename, &info); - filename = info.dli_fname; + char filenameBuf[PATH_MAX]; + filename = realpath(info.dli_fname, filenameBuf); #endif return filename; From 4603181a0a6545e7344a78a9416b36c0ff7e9ce3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 25 Oct 2021 10:32:45 +0100 Subject: [PATCH 217/504] Make utility functions compatible with doxygen Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 9 ++- distrho/DistrhoPluginUtils.hpp | 24 +++++-- distrho/DistrhoUtils.hpp | 115 ++++++++++++++++++++------------- dpf.doxygen | 1 + 4 files changed, 99 insertions(+), 50 deletions(-) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index e5aea8ca..e58f25cb 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -839,7 +839,14 @@ START_NAMESPACE_DISTRHO */ #define USE_NAMESPACE_DISTRHO using namespace DISTRHO_NAMESPACE; -/** @} */ +/* TODO + * + * DISTRHO_MACRO_AS_STRING_VALUE + * DISTRHO_MACRO_AS_STRING + * DISTRHO_PROPER_CPP11_SUPPORT + * DONT_SET_USING_DISTRHO_NAMESPACE + * + */ // ----------------------------------------------------------------------------------------------------------- diff --git a/distrho/DistrhoPluginUtils.hpp b/distrho/DistrhoPluginUtils.hpp index bd0273d7..b9ed642e 100644 --- a/distrho/DistrhoPluginUtils.hpp +++ b/distrho/DistrhoPluginUtils.hpp @@ -21,8 +21,14 @@ START_NAMESPACE_DISTRHO -// ----------------------------------------------------------------------------------------------------------- -// plugin related utilities +/* ------------------------------------------------------------------------------------------------------------ + * Plugin related utilities */ + +/** + @defgroup PluginRelatedUtilities Plugin related utilities + + @{ + */ /** Get the absolute filename of the plugin DSP/UI binary.@n @@ -58,8 +64,16 @@ const char* getPluginFormatName() noexcept; */ const char* getResourcePath(const char* bundlePath) noexcept; -// ----------------------------------------------------------------------------------------------------------- -// Plugin helper classes +/** @} */ + +/* ------------------------------------------------------------------------------------------------------------ + * Plugin helper classes */ + +/** + @defgroup PluginHelperClasses Plugin helper classes + + @{ + */ #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 /** @@ -192,6 +206,8 @@ private: }; #endif +/** @} */ + // ----------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/distrho/DistrhoUtils.hpp b/distrho/DistrhoUtils.hpp index 5d2a4cdc..ad3499e2 100644 --- a/distrho/DistrhoUtils.hpp +++ b/distrho/DistrhoUtils.hpp @@ -58,11 +58,18 @@ inline float round(float __x) #define DISTRHO_MACRO_AS_STRING_VALUE(MACRO) #MACRO #define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO) -// ----------------------------------------------------------------------- -// misc functions +/* ------------------------------------------------------------------------------------------------------------ + * misc functions */ -/* - * Return a 64-bit number from 4 8-bit numbers. +/** + @defgroup MiscellaneousFunctions Miscellaneous functions + + @{ + */ + +/** + Return a 32-bit number from 4 8-bit numbers.@n + The return type is a int64_t for better compatibility with plugin formats that use such numbers. */ static inline constexpr int64_t d_cconst(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d) noexcept @@ -70,8 +77,8 @@ int64_t d_cconst(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_ return (a << 24) | (b << 16) | (c << 8) | (d << 0); } -/* - * Return an hexadecimal representation of a MAJ.MIN.MICRO version number. +/** + Return an hexadecimal representation of a MAJ.MIN.MICRO version number. */ static inline constexpr uint32_t d_version(const uint8_t major, const uint8_t minor, const uint8_t micro) noexcept @@ -79,18 +86,26 @@ uint32_t d_version(const uint8_t major, const uint8_t minor, const uint8_t micro return uint32_t(major << 16) | uint32_t(minor << 8) | (micro << 0); } -/* - * Dummy function. +/** + Dummy, no-op function. */ static inline void d_pass() noexcept {} -// ----------------------------------------------------------------------- -// string print functions +/** @} */ -/* - * Print a string to stdout with newline (gray color). - * Does nothing if DEBUG is not defined. +/* ------------------------------------------------------------------------------------------------------------ + * string print functions */ + +/** + @defgroup StringPrintFunctions String print functions + + @{ + */ + +/** + Print a string to stdout with newline (gray color). + Does nothing if DEBUG is not defined. */ #ifndef DEBUG # define d_debug(...) @@ -109,8 +124,8 @@ void d_debug(const char* const fmt, ...) noexcept } #endif -/* - * Print a string to stdout with newline. +/** + Print a string to stdout with newline. */ static inline void d_stdout(const char* const fmt, ...) noexcept @@ -124,8 +139,8 @@ void d_stdout(const char* const fmt, ...) noexcept } catch (...) {} } -/* - * Print a string to stderr with newline. +/** + Print a string to stderr with newline. */ static inline void d_stderr(const char* const fmt, ...) noexcept @@ -139,8 +154,8 @@ void d_stderr(const char* const fmt, ...) noexcept } catch (...) {} } -/* - * Print a string to stderr with newline (red color). +/** + Print a string to stderr with newline (red color). */ static inline void d_stderr2(const char* const fmt, ...) noexcept @@ -155,8 +170,8 @@ void d_stderr2(const char* const fmt, ...) noexcept } catch (...) {} } -/* - * Print a safe assertion error message. +/** + Print a safe assertion error message. */ static inline void d_safe_assert(const char* const assertion, const char* const file, const int line) noexcept @@ -164,8 +179,8 @@ void d_safe_assert(const char* const assertion, const char* const file, const in d_stderr2("assertion failure: \"%s\" in file %s, line %i", assertion, file, line); } -/* - * Print a safe assertion error message, with 1 extra signed integer value. +/** + Print a safe assertion error message, with 1 extra signed integer value. */ static inline void d_safe_assert_int(const char* const assertion, const char* const file, @@ -174,8 +189,8 @@ void d_safe_assert_int(const char* const assertion, const char* const file, d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %i", assertion, file, line, value); } -/* - * Print a safe assertion error message, with 1 extra unsigned integer value. +/** + Print a safe assertion error message, with 1 extra unsigned integer value. */ static inline void d_safe_assert_uint(const char* const assertion, const char* const file, @@ -184,8 +199,8 @@ void d_safe_assert_uint(const char* const assertion, const char* const file, d_stderr2("assertion failure: \"%s\" in file %s, line %i, value %u", assertion, file, line, value); } -/* - * Print a safe assertion error message, with 2 extra signed integer values. +/** + Print a safe assertion error message, with 2 extra signed integer values. */ static inline void d_safe_assert_int2(const char* const assertion, const char* const file, @@ -194,8 +209,8 @@ void d_safe_assert_int2(const char* const assertion, const char* const file, d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %i, v2 %i", assertion, file, line, v1, v2); } -/* - * Print a safe assertion error message, with 2 extra unsigned integer values. +/** + Print a safe assertion error message, with 2 extra unsigned integer values. */ static inline void d_safe_assert_uint2(const char* const assertion, const char* const file, @@ -204,8 +219,8 @@ void d_safe_assert_uint2(const char* const assertion, const char* const file, d_stderr2("assertion failure: \"%s\" in file %s, line %i, v1 %u, v2 %u", assertion, file, line, v1, v2); } -/* - * Print a safe assertion error message, with a custom error message. +/** + Print a safe assertion error message, with a custom error message. */ static inline void d_custom_safe_assert(const char* const message, const char* const assertion, const char* const file, @@ -214,8 +229,8 @@ void d_custom_safe_assert(const char* const message, const char* const assertion d_stderr2("assertion failure: %s, condition \"%s\" in file %s, line %i", message, assertion, file, line); } -/* - * Print a safe exception error message. +/** + Print a safe exception error message. */ static inline void d_safe_exception(const char* const exception, const char* const file, const int line) noexcept @@ -223,12 +238,20 @@ void d_safe_exception(const char* const exception, const char* const file, const d_stderr2("exception caught: \"%s\" in file %s, line %i", exception, file, line); } -// ----------------------------------------------------------------------- -// math functions +/** @} */ -/* - * Safely compare two floating point numbers. - * Returns true if they match. +/* ------------------------------------------------------------------------------------------------------------ + * math functions */ + +/** + @defgroup MathFunctions Math related functions + + @{ + */ + +/** + Safely compare two floating point numbers. + Returns true if they match. */ template static inline @@ -237,9 +260,9 @@ bool d_isEqual(const T& v1, const T& v2) return std::abs(v1-v2) < std::numeric_limits::epsilon(); } -/* - * Safely compare two floating point numbers. - * Returns true if they don't match. +/** + Safely compare two floating point numbers. + Returns true if they don't match. */ template static inline @@ -258,8 +281,8 @@ bool d_isZero(const T& value) return std::abs(value) < std::numeric_limits::epsilon(); } -/* - * Safely check if a floating point number is not zero. +/** + Safely check if a floating point number is not zero. */ template static inline @@ -268,8 +291,8 @@ bool d_isNotZero(const T& value) return std::abs(value) >= std::numeric_limits::epsilon(); } -/* - * Get next power of 2. +/** + Get next power of 2. */ static inline uint32_t d_nextPowerOf2(uint32_t size) noexcept @@ -286,6 +309,8 @@ uint32_t d_nextPowerOf2(uint32_t size) noexcept return ++size; } +/** @} */ + // ----------------------------------------------------------------------- #ifndef DONT_SET_USING_DISTRHO_NAMESPACE diff --git a/dpf.doxygen b/dpf.doxygen index e4083934..eb2be59b 100644 --- a/dpf.doxygen +++ b/dpf.doxygen @@ -242,6 +242,7 @@ SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = DOXYGEN \ + DEBUG \ HAVE_CAIRO=1 \ HAVE_OPENGL=1 \ DISTRHO_PLUGIN_NAME="Plugin Name" \ From b800b5275916e9e99fa4ebae0c8044f2e84df170 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 25 Oct 2021 10:34:35 +0100 Subject: [PATCH 218/504] Forgot one.. Signed-off-by: falkTX --- distrho/DistrhoUtils.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distrho/DistrhoUtils.hpp b/distrho/DistrhoUtils.hpp index ad3499e2..a7694866 100644 --- a/distrho/DistrhoUtils.hpp +++ b/distrho/DistrhoUtils.hpp @@ -271,8 +271,8 @@ bool d_isNotEqual(const T& v1, const T& v2) return std::abs(v1-v2) >= std::numeric_limits::epsilon(); } -/* - * Safely check if a floating point number is zero. +/** + Safely check if a floating point number is zero. */ template static inline From 62996d5578702fde52e309528d6a4a894a7b3997 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 26 Oct 2021 18:13:25 +0100 Subject: [PATCH 219/504] Make macOS open file dialog truly async --- dgl/src/pugl.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index edf83a08..c4e31699 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -539,8 +539,8 @@ bool puglMacOSFilePanelOpen(PuglView* const view, NSOpenPanel* const panel = [NSOpenPanel openPanel]; [panel setAllowsMultipleSelection:NO]; - [panel setCanChooseFiles:YES]; [panel setCanChooseDirectories:NO]; + [panel setCanChooseFiles:YES]; [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:startDir]]]; // TODO file filter using allowedContentTypes: [UTType] @@ -556,19 +556,22 @@ bool puglMacOSFilePanelOpen(PuglView* const view, encoding:NSUTF8StringEncoding]; [panel setTitle:titleString]; - [panel beginSheetModalForWindow:(impl->window ? impl->window : [view->impl->wrapperView window]) - completionHandler:^(NSInteger result) + dispatch_async(dispatch_get_main_queue(), ^ { - if (result == NSModalResponseOK && [[panel URL] isFileURL]) - { - NSString* const path = [[panel URL] path]; - callback(view, [path UTF8String]); - } - else + [panel beginSheetModalForWindow:(impl->window ? impl->window : [view->impl->wrapperView window]) + completionHandler:^(NSModalResponse result) { - callback(view, nullptr); - } - }]; + if (result == NSModalResponseOK && [[panel URL] isFileURL]) + { + NSString* const path = [[panel URL] path]; + callback(view, [path UTF8String]); + } + else + { + callback(view, nullptr); + } + }]; + }); return true; } From 497a831bd9ac0a9f28b581ea58fe8bfc0f95fff1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 26 Oct 2021 21:33:53 +0100 Subject: [PATCH 220/504] Early work for self-test mode in standalone Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 141 +++++++++++++++++++++++++++++- distrho/src/DistrhoUIInternal.hpp | 10 ++- 2 files changed, 146 insertions(+), 5 deletions(-) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index db82f3d0..067cc60f 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -23,6 +23,8 @@ # include "../extra/Sleep.hpp" #endif +#include "../extra/Thread.hpp" + #include "jackbridge/JackBridge.cpp" #include "lv2/lv2.h" @@ -747,14 +749,149 @@ private: #undef thisPtr }; +// ----------------------------------------------------------------------- + +class PluginProcessTestingThread : public Thread +{ + PluginExporter& plugin; + +public: + PluginProcessTestingThread(PluginExporter& p) : plugin(p) {} + +protected: + void run() override + { + plugin.setBufferSize(256); + plugin.activate(); + + float buffer[256]; + const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS > 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1]; + float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS > 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1]; + for (int i=0; i 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1]; + float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS > 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1]; + for (int i=0; iuiIdle(); } -#else + + void showAndFocus() + { + uiData->window->show(); + uiData->window->focus(); + } +#endif + bool plugin_idle() { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false); @@ -239,7 +246,6 @@ public: ui->uiIdle(); return ! uiData->app.isQuitting(); } -#endif void focus() { From def5f2b47b4d0acec118b4ac87c074f0820e34d1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Oct 2021 05:00:15 +0100 Subject: [PATCH 221/504] Run GetOpenFileNameW on secondary thread, tested on Windows 10 Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 154 +++++++++++++++++++++++----------- dgl/src/WindowPrivateData.hpp | 25 ++++++ 2 files changed, 131 insertions(+), 48 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 0b1b549b..3d43315f 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -82,6 +82,100 @@ static double getDesktopScaleFactor(const PuglView* const view) // ----------------------------------------------------------------------- +#ifdef DISTRHO_OS_WINDOWS +struct FileBrowserThread::PrivateData { + OPENFILENAMEW ofn; + std::vector fileNameW; + std::vector startDirW; + std::vector titleW; + + PrivateData() + : fileNameW(32768) + { + std::memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.lpstrFile = fileNameW.data(); + ofn.nMaxFile = (DWORD)fileNameW.size(); + } + + void setup(const char* const startDir, + const char* const title, + const uintptr_t winId, + const Window::FileBrowserOptions options) + { + ofn.hwndOwner = (HWND)winId; + + ofn.Flags = OFN_PATHMUSTEXIST; + if (options.buttons.showHidden == Window::FileBrowserOptions::kButtonVisibleChecked) + ofn.Flags |= OFN_FORCESHOWHIDDEN; + + ofn.FlagsEx = 0x0; + if (options.buttons.showPlaces == Window::FileBrowserOptions::kButtonInvisible) + ofn.FlagsEx |= OFN_EX_NOPLACESBAR; + + startDirW.resize(std::strlen(startDir) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, startDir, -1, startDirW.data(), static_cast(startDirW.size()))) + ofn.lpstrInitialDir = startDirW.data(); + + titleW.resize(std::strlen(title) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, title, -1, titleW.data(), static_cast(titleW.size()))) + ofn.lpstrTitle = titleW.data(); + } + + const char* run() + { + if (GetOpenFileNameW(&ofn)) + { + // back to UTF-8 + std::vector fileNameA(4 * 32768); + if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, + fileNameA.data(), (int)fileNameA.size(), + nullptr, nullptr)) + { + return strdup(fileNameA.data()); + } + } + + return nullptr; + } +}; + +FileBrowserThread::FileBrowserThread(const char*& file) + : pData(new PrivateData()), + win32SelectedFile(file) {} + +FileBrowserThread::~FileBrowserThread() +{ + stopThread(5000); + delete pData; +} + +void FileBrowserThread::start(const char* const startDir, + const char* const title, + const uintptr_t winId, + const Window::FileBrowserOptions options) +{ + pData->setup(startDir, title, winId, options); + startThread(); +} + +void FileBrowserThread::run() +{ + const char* nextFile = pData->run(); + + if (shouldThreadExit()) + return; + + if (nextFile == nullptr) + nextFile = kWin32SelectedFileCancelled; + + d_stdout("WThread finished, final file '%s'", nextFile); + win32SelectedFile = nextFile; +} +#endif // DISTRHO_OS_WINDOWS + +// ----------------------------------------------------------------------- + Window::PrivateData::PrivateData(Application& a, Window* const s) : app(a), appData(a.pData), @@ -102,6 +196,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal() { @@ -128,6 +223,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal(ppData) { @@ -158,6 +254,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal() { @@ -190,6 +287,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), + win32FileThread(win32SelectedFile), #endif modal() { @@ -220,6 +318,9 @@ Window::PrivateData::~PrivateData() } #ifdef DISTRHO_OS_WINDOWS + if (win32FileThread.isThreadRunning()) + win32FileThread.stopThread(2000); + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) std::free(const_cast(win32SelectedFile)); #endif @@ -537,58 +638,15 @@ bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& opti # endif # ifdef DISTRHO_OS_WINDOWS - // the old and compatible dialog API - OPENFILENAMEW ofn; - memset(&ofn, 0, sizeof(ofn)); + // TODO signal to close + if (win32FileThread.isThreadRunning()) + win32FileThread.stopThread(1000); + if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) std::free(const_cast(win32SelectedFile)); win32SelectedFile = nullptr; - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = (HWND)puglGetNativeWindow(view); - - // set start directory in UTF-16 encoding - std::vector startDirW; - startDirW.resize(startDir.length() + 1); - if (MultiByteToWideChar(CP_UTF8, 0, startDir.buffer(), -1, startDirW.data(), static_cast(startDirW.size()))) - ofn.lpstrInitialDir = startDirW.data(); - - // set title in UTF-16 encoding - std::vector titleW; - titleW.resize(title.length() + 1); - if (MultiByteToWideChar(CP_UTF8, 0, title.buffer(), -1, titleW.data(), static_cast(titleW.size()))) - ofn.lpstrTitle = titleW.data(); - - // prepare a buffer to receive the result - std::vector fileNameW(32768); // the Unicode maximum - ofn.lpstrFile = fileNameW.data(); - ofn.nMaxFile = (DWORD)fileNameW.size(); - - // flags - ofn.Flags = OFN_PATHMUSTEXIST; - if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) - ofn.Flags |= OFN_FORCESHOWHIDDEN; - if (options.buttons.showPlaces == FileBrowserOptions::kButtonInvisible) - ofn.FlagsEx |= OFN_EX_NOPLACESBAR; - - // TODO synchronous only, can't do better with WinAPI native dialogs. - // threading might work, if someone is motivated to risk it. - if (GetOpenFileNameW(&ofn)) - { - // back to UTF-8 - std::vector fileNameA(4 * 32768); - if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, - fileNameA.data(), (int)fileNameA.size(), - nullptr, nullptr)) - { - // handle it during the next idle cycle (fake async) - win32SelectedFile = strdup(fileNameA.data()); - } - } - - if (win32SelectedFile == nullptr) - win32SelectedFile = kWin32SelectedFileCancelled; - + win32FileThread.start(startDir, title, puglGetNativeWindow(view), options); return true; # endif diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index d8e0f8cb..eff0d091 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -25,12 +25,35 @@ #include +#ifdef DISTRHO_OS_WINDOWS +# include "../distrho/extra/Thread.hpp" +#endif + START_NAMESPACE_DGL class TopLevelWidget; // ----------------------------------------------------------------------- +#ifdef DISTRHO_OS_WINDOWS +class FileBrowserThread : public Thread +{ + struct PrivateData; + PrivateData* const pData; + const char*& win32SelectedFile; + +public: + FileBrowserThread(const char*& win32SelectedFile); + ~FileBrowserThread() override; + void start(const char* startDir, const char* title, uintptr_t winId, Window::FileBrowserOptions options); + +protected: + void run() override; +}; +#endif + +// ----------------------------------------------------------------------- + struct Window::PrivateData : IdleCallback { /** Reference to the DGL Application class this (private data) window associates with. */ Application& app; @@ -83,6 +106,8 @@ struct Window::PrivateData : IdleCallback { #ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ const char* win32SelectedFile; + /** Thread where the openFileBrowser runs. */ + FileBrowserThread win32FileThread; #endif /** Modal window setup. */ From c4334df6735f92c045e822fb5b179d835a99878b Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Oct 2021 05:05:53 +0100 Subject: [PATCH 222/504] Place the new tests under ifdef DPF_RUNTIME_TESTING Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 067cc60f..8ee3527a 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -751,6 +751,7 @@ private: // ----------------------------------------------------------------------- +#ifdef DPF_RUNTIME_TESTING class PluginProcessTestingThread : public Thread { PluginExporter& plugin; @@ -880,6 +881,7 @@ bool runSelfTests() return true; } +#endif // DPF_RUNTIME_TESTING END_NAMESPACE_DISTRHO @@ -889,8 +891,10 @@ int main(int argc, char* argv[]) { USE_NAMESPACE_DISTRHO; +#ifdef DPF_RUNTIME_TESTING if (argc == 2 && std::strcmp(argv[1], "selftest") == 0) return runSelfTests() ? 0 : 1; +#endif jack_status_t status = jack_status_t(0x0); jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); From 8fd9d95c74ff603a96f9a3de3f6e49ebb34ae865 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Oct 2021 05:10:22 +0100 Subject: [PATCH 223/504] Only include Thread.hpp as needed Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 8ee3527a..ac96f6a6 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -23,7 +23,9 @@ # include "../extra/Sleep.hpp" #endif -#include "../extra/Thread.hpp" +#ifdef DPF_RUNTIME_TESTING +# include "../extra/Thread.hpp" +#endif #include "jackbridge/JackBridge.cpp" #include "lv2/lv2.h" From 2fa587358b9224646a69c0ac8bb5a31790acac3b Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Oct 2021 05:20:42 +0100 Subject: [PATCH 224/504] File dialog setup can't be done on MSVC just yet (pthread usage) Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 16 ++++++++-------- dgl/src/WindowPrivateData.hpp | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 3d43315f..c7801349 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -82,7 +82,7 @@ static double getDesktopScaleFactor(const PuglView* const view) // ----------------------------------------------------------------------- -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) struct FileBrowserThread::PrivateData { OPENFILENAMEW ofn; std::vector fileNameW; @@ -172,7 +172,7 @@ void FileBrowserThread::run() d_stdout("WThread finished, final file '%s'", nextFile); win32SelectedFile = nextFile; } -#endif // DISTRHO_OS_WINDOWS +#endif // DISTRHO_OS_WINDOWS && !_MSC_VER // ----------------------------------------------------------------------- @@ -194,7 +194,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) win32SelectedFile(nullptr), win32FileThread(win32SelectedFile), #endif @@ -221,7 +221,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) win32SelectedFile(nullptr), win32FileThread(win32SelectedFile), #endif @@ -252,7 +252,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) win32SelectedFile(nullptr), win32FileThread(win32SelectedFile), #endif @@ -285,7 +285,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) win32SelectedFile(nullptr), win32FileThread(win32SelectedFile), #endif @@ -317,7 +317,7 @@ Window::PrivateData::~PrivateData() isVisible = false; } -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) if (win32FileThread.isThreadRunning()) win32FileThread.stopThread(2000); @@ -637,7 +637,7 @@ bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& opti return puglMacOSFilePanelOpen(view, startDir, title, flags, openPanelCallback); # endif -# ifdef DISTRHO_OS_WINDOWS +# if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) // TODO signal to close if (win32FileThread.isThreadRunning()) win32FileThread.stopThread(1000); diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index eff0d091..1ffd611f 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -25,7 +25,7 @@ #include -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) # include "../distrho/extra/Thread.hpp" #endif @@ -35,7 +35,7 @@ class TopLevelWidget; // ----------------------------------------------------------------------- -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) class FileBrowserThread : public Thread { struct PrivateData; @@ -103,7 +103,7 @@ struct Window::PrivateData : IdleCallback { /** Render to a picture file when non-null, automatically free+unset after saving. */ char* filenameToRenderInto; -#ifdef DISTRHO_OS_WINDOWS +#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) /** Selected file for openFileBrowser on windows, stored for fake async operation. */ const char* win32SelectedFile; /** Thread where the openFileBrowser runs. */ From f3d38188c95b482d3311e84e9140837734f28af6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Oct 2021 05:28:24 +0100 Subject: [PATCH 225/504] Fix some leftover warnings Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index ac96f6a6..8329d327 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -952,6 +952,11 @@ int main(int argc, char* argv[]) const PluginJack p(client); return 0; + +#ifndef DPF_RUNTIME_TESTING + // unused + (void)argc; (void)argv; +#endif } // ----------------------------------------------------------------------- From 1d756d6ac203d5c133769e1f5930f3dac7edb9bc Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Oct 2021 12:36:52 +0100 Subject: [PATCH 226/504] Add a missing include Signed-off-by: falkTX --- dgl/src/nanovg/nanovg_gl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index dc541ff2..a7ea9539 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/dgl/src/nanovg/nanovg_gl.h @@ -18,6 +18,8 @@ #ifndef NANOVG_GL_H #define NANOVG_GL_H +#include + #ifdef __cplusplus extern "C" { #endif From a5952348cdb9baefe1564e9e0c096ce4237dbc5e Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Oct 2021 14:17:13 +0100 Subject: [PATCH 227/504] Fix MSVC build Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index c7801349..f03d87df 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -512,7 +512,7 @@ void Window::PrivateData::setResizable(const bool resizable) void Window::PrivateData::idleCallback() { #ifndef DGL_FILE_BROWSER_DISABLED -# ifdef DISTRHO_OS_WINDOWS +# if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) if (const char* path = win32SelectedFile) { win32SelectedFile = nullptr; From d7abd3e1d5287f0dc023340d424270fd5069c8ab Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Oct 2021 18:12:28 +0100 Subject: [PATCH 228/504] Use win32 thread API for file browser, not pthreads; Cleanup Signed-off-by: falkTX --- Makefile.base.mk | 1 + dgl/src/WindowPrivateData.cpp | 127 +++++++++++++++++++++--------- dgl/src/WindowPrivateData.hpp | 140 ++-------------------------------- 3 files changed, 97 insertions(+), 171 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 5c30c328..d5c9ce03 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -280,6 +280,7 @@ endif ifeq ($(WINDOWS),true) DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 +# -lole32 endif ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index f03d87df..4fa226a3 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -23,6 +23,7 @@ #ifdef DISTRHO_OS_WINDOWS # include +# include # include # include # include @@ -30,7 +31,7 @@ # include #endif -#define DGL_DEBUG_EVENTS +// #define DGL_DEBUG_EVENTS #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) # ifdef DISTRHO_PROPER_CPP11_SUPPORT @@ -82,15 +83,23 @@ static double getDesktopScaleFactor(const PuglView* const view) // ----------------------------------------------------------------------- -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) +#ifdef DISTRHO_OS_WINDOWS struct FileBrowserThread::PrivateData { OPENFILENAMEW ofn; + volatile bool threadCancelled; + uintptr_t threadHandle; std::vector fileNameW; std::vector startDirW; std::vector titleW; - - PrivateData() - : fileNameW(32768) + const bool isEmbed; + const char*& win32SelectedFile; + + PrivateData(const bool embed, const char*& file) + : threadCancelled(false), + threadHandle(0), + fileNameW(32768), + isEmbed(embed), + win32SelectedFile(file) { std::memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); @@ -105,7 +114,7 @@ struct FileBrowserThread::PrivateData { { ofn.hwndOwner = (HWND)winId; - ofn.Flags = OFN_PATHMUSTEXIST; + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; if (options.buttons.showHidden == Window::FileBrowserOptions::kButtonVisibleChecked) ofn.Flags |= OFN_FORCESHOWHIDDEN; @@ -122,56 +131,102 @@ struct FileBrowserThread::PrivateData { ofn.lpstrTitle = titleW.data(); } - const char* run() + void run() { + const char* nextFile = nullptr; + if (GetOpenFileNameW(&ofn)) { + if (threadCancelled) + { + threadHandle = 0; + return; + } + // back to UTF-8 std::vector fileNameA(4 * 32768); if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr)) { - return strdup(fileNameA.data()); + nextFile = strdup(fileNameA.data()); } } - return nullptr; + if (threadCancelled) + { + threadHandle = 0; + return; + } + + if (nextFile == nullptr) + nextFile = kWin32SelectedFileCancelled; + + win32SelectedFile = nextFile; + threadHandle = 0; } }; -FileBrowserThread::FileBrowserThread(const char*& file) - : pData(new PrivateData()), - win32SelectedFile(file) {} +FileBrowserThread::FileBrowserThread(const bool isEmbed, const char*& file) + : pData(new PrivateData(isEmbed, file)) {} FileBrowserThread::~FileBrowserThread() { - stopThread(5000); + stop(); delete pData; } +unsigned __stdcall FileBrowserThread__run(void* const arg) +{ + // CoInitializeEx(nullptr, COINIT_MULTITHREADED); + static_cast(arg)->pData->run(); + // CoUninitialize(); + _endthreadex(0); + return 0; +} + void FileBrowserThread::start(const char* const startDir, const char* const title, const uintptr_t winId, const Window::FileBrowserOptions options) { pData->setup(startDir, title, winId, options); - startThread(); + + uint threadId; + pData->threadCancelled = false; + pData->threadHandle = _beginthreadex(nullptr, 0, FileBrowserThread__run, this, 0, &threadId); } -void FileBrowserThread::run() +void FileBrowserThread::stop() { - const char* nextFile = pData->run(); + pData->threadCancelled = true; - if (shouldThreadExit()) + if (pData->threadHandle == 0) return; - if (nextFile == nullptr) - nextFile = kWin32SelectedFileCancelled; + // if previous dialog running, carefully close its window + const HWND owner = pData->isEmbed ? GetParent(pData->ofn.hwndOwner) : pData->ofn.hwndOwner; - d_stdout("WThread finished, final file '%s'", nextFile); - win32SelectedFile = nextFile; + if (owner != nullptr && owner != INVALID_HANDLE_VALUE) + { + const HWND window = GetWindow(owner, GW_HWNDFIRST); + + if (window != nullptr && window != INVALID_HANDLE_VALUE) + { + SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0); + SendMessage(window, WM_CLOSE, 0, 0); + WaitForSingleObject((HANDLE)pData->threadHandle, 5000); + } + } + + // not good if thread still running, but let's close the handle anyway + if (pData->threadHandle != 0) + { + CloseHandle((HANDLE)pData->threadHandle); + pData->threadHandle = 0; + } } + #endif // DISTRHO_OS_WINDOWS && !_MSC_VER // ----------------------------------------------------------------------- @@ -194,9 +249,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) +#ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), - win32FileThread(win32SelectedFile), + win32FileThread(false, win32SelectedFile), #endif modal() { @@ -221,9 +276,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) +#ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), - win32FileThread(win32SelectedFile), + win32FileThread(false, win32SelectedFile), #endif modal(ppData) { @@ -252,9 +307,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) +#ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), - win32FileThread(win32SelectedFile), + win32FileThread(isEmbed, win32SelectedFile), #endif modal() { @@ -285,9 +340,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) +#ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), - win32FileThread(win32SelectedFile), + win32FileThread(isEmbed, win32SelectedFile), #endif modal() { @@ -317,9 +372,8 @@ Window::PrivateData::~PrivateData() isVisible = false; } -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) - if (win32FileThread.isThreadRunning()) - win32FileThread.stopThread(2000); +#ifdef DISTRHO_OS_WINDOWS + win32FileThread.stop(); if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) std::free(const_cast(win32SelectedFile)); @@ -512,7 +566,7 @@ void Window::PrivateData::setResizable(const bool resizable) void Window::PrivateData::idleCallback() { #ifndef DGL_FILE_BROWSER_DISABLED -# if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) +# ifdef DISTRHO_OS_WINDOWS if (const char* path = win32SelectedFile) { win32SelectedFile = nullptr; @@ -637,10 +691,9 @@ bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& opti return puglMacOSFilePanelOpen(view, startDir, title, flags, openPanelCallback); # endif -# if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) - // TODO signal to close - if (win32FileThread.isThreadRunning()) - win32FileThread.stopThread(1000); +# ifdef DISTRHO_OS_WINDOWS + // only one possible at a time + DISTRHO_SAFE_ASSERT_RETURN(win32FileThread.pData->threadHandle == 0, false); if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) std::free(const_cast(win32SelectedFile)); diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 1ffd611f..6db52747 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -25,30 +25,22 @@ #include -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) -# include "../distrho/extra/Thread.hpp" -#endif - START_NAMESPACE_DGL class TopLevelWidget; // ----------------------------------------------------------------------- -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) -class FileBrowserThread : public Thread +#ifdef DISTRHO_OS_WINDOWS +struct FileBrowserThread { struct PrivateData; PrivateData* const pData; - const char*& win32SelectedFile; -public: - FileBrowserThread(const char*& win32SelectedFile); - ~FileBrowserThread() override; + FileBrowserThread(bool isEmbed, const char*& win32SelectedFile); + ~FileBrowserThread(); void start(const char* startDir, const char* title, uintptr_t winId, Window::FileBrowserOptions options); - -protected: - void run() override; + void stop(); }; #endif @@ -103,7 +95,7 @@ struct Window::PrivateData : IdleCallback { /** Render to a picture file when non-null, automatically free+unset after saving. */ char* filenameToRenderInto; -#if defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) +#ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ const char* win32SelectedFile; /** Thread where the openFileBrowser runs. */ @@ -218,124 +210,4 @@ struct Window::PrivateData : IdleCallback { END_NAMESPACE_DGL -#if 0 -// #if defined(DISTRHO_OS_HAIKU) -// BApplication* bApplication; -// BView* bView; -// BWindow* bWindow; -#if defined(DISTRHO_OS_MAC) -// NSView* mView; -// id mWindow; -// id mParentWindow; -# ifndef DGL_FILE_BROWSER_DISABLED - NSOpenPanel* fOpenFilePanel; - id fFilePanelDelegate; -# endif -#elif defined(DISTRHO_OS_WINDOWS) -// HWND hwnd; -// HWND hwndParent; -# ifndef DGL_FILE_BROWSER_DISABLED - String fSelectedFile; -# endif -#endif -#endif - -#if 0 -// ----------------------------------------------------------------------- -// Window Private - -struct Window::PrivateData { - // ------------------------------------------------------------------- - - bool handlePluginSpecial(const bool press, const Key key) - { - DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key); - - if (fModal.childFocus != nullptr) - { - fModal.childFocus->focus(); - return true; - } - - int mods = 0x0; - - switch (key) - { - case kKeyShift: - mods |= kModifierShift; - break; - case kKeyControl: - mods |= kModifierControl; - break; - case kKeyAlt: - mods |= kModifierAlt; - break; - default: - break; - } - - if (mods != 0x0) - { - if (press) - fView->mods |= mods; - else - fView->mods &= ~(mods); - } - - Widget::SpecialEvent ev; - ev.press = press; - ev.key = key; - ev.mod = static_cast(fView->mods); - ev.time = 0; - - FOR_EACH_WIDGET_INV(rit) - { - Widget* const widget(*rit); - - if (widget->isVisible() && widget->onSpecial(ev)) - return true; - } - - return false; - } - -#if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) - static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData) - { - PrivateData* pData = (PrivateData*)userData; - - if (returnCode == NSOKButton) - { - NSArray* urls = [panel URLs]; - NSURL* fileUrl = nullptr; - - for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i) - { - NSURL* url = (NSURL*)[urls objectAtIndex:i]; - if ([url isFileURL]) - fileUrl = url; - } - - if (fileUrl) - { - PuglView* view = pData->fView; - if (view->fileSelectedFunc) - { - const char* fileName = [fileUrl.path UTF8String]; - view->fileSelectedFunc(view, fileName); - } - } - } - - [pData->fOpenFilePanel release]; - pData->fOpenFilePanel = nullptr; - } -#endif - - // ------------------------------------------------------------------- - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) -}; -#endif - #endif // DGL_WINDOW_PRIVATE_DATA_HPP_INCLUDED From 6bf4d68dec211900a3f3e184678a904919e4aabc Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Oct 2021 18:45:02 +0100 Subject: [PATCH 229/504] Revert "Add a missing include" This reverts commit 1d756d6ac203d5c133769e1f5930f3dac7edb9bc. --- dgl/src/nanovg/nanovg_gl.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index a7ea9539..dc541ff2 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/dgl/src/nanovg/nanovg_gl.h @@ -18,8 +18,6 @@ #ifndef NANOVG_GL_H #define NANOVG_GL_H -#include - #ifdef __cplusplus extern "C" { #endif From d64551f5b4691b324b4321ff3e105f9ffe043f72 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Oct 2021 22:14:33 +0100 Subject: [PATCH 230/504] Delete unused file Signed-off-by: falkTX --- distrho/src/jackbridge/Makefile | 257 -------------------------------- 1 file changed, 257 deletions(-) delete mode 100644 distrho/src/jackbridge/Makefile diff --git a/distrho/src/jackbridge/Makefile b/distrho/src/jackbridge/Makefile deleted file mode 100644 index 9f3e3671..00000000 --- a/distrho/src/jackbridge/Makefile +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/make -f -# Makefile for jackbridge # -# ----------------------- # -# Created by falkTX -# - -CWD=.. -MODULENAME=jackbridge -include ../modules/Makefile.mk - -# --------------------------------------------------------------------------------------------------------------------- - -BUILD_CXX_FLAGS += $(JACKBRIDGE_FLAGS) -LINK_FLAGS += $(JACKBRIDGE_LIBS) - -WINE_32BIT_FLAGS = $(32BIT_FLAGS) -fpermissive -WINE_64BIT_FLAGS = $(64BIT_FLAGS) -fpermissive -WINE_LINK_FLAGS = $(LINK_FLAGS) $(LIBDL_LIBS) -lpthread -lstdc++ - -ifeq ($(JACKBRIDGE_DIRECT),true) -BUILD_CXX_FLAGS += $(JACK_FLAGS) -DJACKBRIDGE_DIRECT -LINK_FLAGS += $(JACK_LIBS) -endif - -ifneq ($(MACOS),true) -WINE_32BIT_FLAGS += -I/usr/include/wine/wine/windows -WINE_32BIT_FLAGS += -I/usr/include/wine-development/windows -WINE_32BIT_FLAGS += -I/opt/wine-devel/include/wine/windows -WINE_32BIT_FLAGS += -L/usr/lib32/wine -WINE_32BIT_FLAGS += -L/usr/lib/wine -WINE_32BIT_FLAGS += -L/usr/lib/i386-linux-gnu/wine -WINE_32BIT_FLAGS += -L/usr/lib/i386-linux-gnu/wine-development -WINE_32BIT_FLAGS += -L/opt/wine-stable/lib -WINE_32BIT_FLAGS += -L/opt/wine-stable/lib/wine -WINE_32BIT_FLAGS += -L/opt/wine-staging/lib -WINE_32BIT_FLAGS += -L/opt/wine-staging/lib/wine - -WINE_64BIT_FLAGS += -I/usr/include/wine/wine/windows -WINE_64BIT_FLAGS += -I/usr/include/wine-development/windows -WINE_64BIT_FLAGS += -I/opt/wine-devel/include/wine/windows -WINE_64BIT_FLAGS += -L/usr/lib64/wine -WINE_64BIT_FLAGS += -L/usr/lib/x86_64-linux-gnu/wine -WINE_64BIT_FLAGS += -L/usr/lib/x86_64-linux-gnu/wine-development -WINE_64BIT_FLAGS += -L/opt/wine-stable/lib64 -WINE_64BIT_FLAGS += -L/opt/wine-stable/lib64/wine -WINE_64BIT_FLAGS += -L/opt/wine-staging/lib64 -WINE_64BIT_FLAGS += -L/opt/wine-staging/lib64/wine - -WINE_LINK_FLAGS += -lrt -endif - -# --------------------------------------------------------------------------------------------------------------------- - -OBJS = $(OBJDIR)/JackBridge1.cpp.o $(OBJDIR)/JackBridge2.cpp.o -OBJS_arm32 = $(OBJDIR)/JackBridge1.cpp.arm32.o $(OBJDIR)/JackBridge2.cpp.arm32.o -OBJS_posix32 = $(OBJDIR)/JackBridge1.cpp.posix32.o $(OBJDIR)/JackBridge2.cpp.posix32.o -OBJS_posix64 = $(OBJDIR)/JackBridge1.cpp.posix64.o $(OBJDIR)/JackBridge2.cpp.posix64.o -OBJS_win32 = $(OBJDIR)/JackBridge1.cpp.win32.o $(OBJDIR)/JackBridge2.cpp.win32.o -OBJS_win64 = $(OBJDIR)/JackBridge1.cpp.win64.o $(OBJDIR)/JackBridge2.cpp.win64.o -OBJS_wine32 = $(OBJDIR)/JackBridge1.cpp.wine32.o $(OBJDIR)/JackBridge2.cpp.wine32.o $(OBJDIR)/JackBridge3.cpp.wine32.o -OBJS_wine64 = $(OBJDIR)/JackBridge1.cpp.wine64.o $(OBJDIR)/JackBridge2.cpp.wine64.o $(OBJDIR)/JackBridge3.cpp.wine64.o - -OBJS_posix32e = $(OBJDIR)/JackBridgeExport.cpp.posix32e.o -OBJS_posix64e = $(OBJDIR)/JackBridgeExport.cpp.posix64e.o -OBJS_win64e = $(OBJDIR)/JackBridgeExport.cpp.win64e.o -OBJS_win32e = $(OBJDIR)/JackBridgeExport.cpp.win32e.o - -# --------------------------------------------------------------------------------------------------------------------- - -all: $(MODULEDIR)/$(MODULENAME).a - -ifeq ($(WIN32),true) -posix32: -posix64: -posix32e: -posix64e: -win32: $(MODULEDIR)/$(MODULENAME).win32.a -win64: $(MODULEDIR)/$(MODULENAME).win64.a -win32e: $(MODULEDIR)/$(MODULENAME).win32e.a -win64e: $(MODULEDIR)/$(MODULENAME).win64e.a -wine32: -wine64: -else -arm32: $(MODULEDIR)/$(MODULENAME).arm32.a -posix32: $(MODULEDIR)/$(MODULENAME).posix32.a -posix64: $(MODULEDIR)/$(MODULENAME).posix64.a -posix32e: $(MODULEDIR)/$(MODULENAME).posix32e.a -posix64e: $(MODULEDIR)/$(MODULENAME).posix64e.a -win32: -win64: -win32e: -win64e: -wine32: $(MODULEDIR)/$(MODULENAME)-wine32.dll$(LIB_EXT) -wine64: $(MODULEDIR)/$(MODULENAME)-wine64.dll$(LIB_EXT) -endif - -# --------------------------------------------------------------------------------------------------------------------- - -clean: - rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.* - -debug: - $(MAKE) DEBUG=true - -# --------------------------------------------------------------------------------------------------------------------- - -$(MODULEDIR)/$(MODULENAME).a: $(OBJS) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).arm32.a: $(OBJS_arm32) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).arm32.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).posix32.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).posix64.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).win32.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).win64.a" - @rm -f $@ - @$(AR) crs $@ $^ - -# --------------------------------------------------------------------------------------------------------------------- - -$(MODULEDIR)/$(MODULENAME).posix32e.a: $(OBJS_posix32e) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).posix32e.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).posix64e.a: $(OBJS_posix64e) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).posix64e.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).win32e.a: $(OBJS_win32e) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).win32e.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).win64e.a: $(OBJS_win64e) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).win64e.a" - @rm -f $@ - @$(AR) crs $@ $^ - -# --------------------------------------------------------------------------------------------------------------------- - -$(MODULEDIR)/$(MODULENAME)-wine32.dll$(LIB_EXT): $(OBJS_wine32) JackBridgeExport.def - -@mkdir -p $(MODULEDIR) - @echo "Linking $(MODULENAME)-wine32.dll$(LIB_EXT)" - @$(WINECC) $^ $(WINE_32BIT_FLAGS) $(WINE_LINK_FLAGS) $(SHARED) -o $@ - -$(MODULEDIR)/$(MODULENAME)-wine64.dll$(LIB_EXT): $(OBJS_wine64) JackBridgeExport.def - -@mkdir -p $(MODULEDIR) - @echo "Linking $(MODULENAME)-wine64.dll$(LIB_EXT)" - @$(WINECC) $^ $(WINE_64BIT_FLAGS) $(WINE_LINK_FLAGS) $(SHARED) -o $@ - -# --------------------------------------------------------------------------------------------------------------------- - -$(OBJDIR)/JackBridge1.cpp.o: JackBridge1.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling JackBridge1.cpp" - @$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ - -$(OBJDIR)/JackBridge2.cpp.o: JackBridge2.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling JackBridge2.cpp" - @$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ - -# --------------------------------------------------------------------------------------------------------------------- - -$(OBJDIR)/JackBridgeExport.cpp.%32e.o: JackBridgeExport.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $<" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -fpermissive -c -o $@ - -$(OBJDIR)/JackBridgeExport.cpp.%64e.o: JackBridgeExport.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $<" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -fpermissive -c -o $@ - -# --------------------------------------------------------------------------------------------------------------------- - -$(OBJDIR)/%.cpp.arm32.o: %.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (arm32)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(ARM32_FLAGS) -c -o $@ - -$(OBJDIR)/%.cpp.posix32.o: %.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (posix32)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ - -$(OBJDIR)/%.cpp.posix64.o: %.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (posix64)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ - -$(OBJDIR)/%.cpp.win32.o: %.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (win32)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ - -$(OBJDIR)/%.cpp.win64.o: %.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (win64)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ - -$(OBJDIR)/%.cpp.wine32.o: %.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (wine32)" - @$(WINECC) $< $(BUILD_CXX_FLAGS) $(WINE_32BIT_FLAGS) -c -o $@ - -$(OBJDIR)/%.cpp.wine64.o: %.cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (wine64)" - @$(WINECC) $< $(BUILD_CXX_FLAGS) $(WINE_64BIT_FLAGS) -c -o $@ - -# --------------------------------------------------------------------------------------------------------------------- - --include $(OBJS:%.o=%.d) --include $(OBJS_arm32:%.o=%.d) --include $(OBJS_posix32:%.o=%.d) --include $(OBJS_posix32e:%.o=%.d) --include $(OBJS_posix64:%.o=%.d) --include $(OBJS_posix64e:%.o=%.d) --include $(OBJS_win32:%.o=%.d) --include $(OBJS_win32e:%.o=%.d) --include $(OBJS_win64:%.o=%.d) --include $(OBJS_win64e:%.o=%.d) --include $(OBJS_wine32:%.o=%.d) --include $(OBJS_wine64:%.o=%.d) - -# --------------------------------------------------------------------------------------------------------------------- From e03017963d4e98cb898a6a1b435ad2ca5bccb537 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Oct 2021 22:14:50 +0100 Subject: [PATCH 231/504] Use the more compat DirectSound as JACK fallback on Windows Signed-off-by: falkTX --- Makefile.plugins.mk | 6 +++++- distrho/src/jackbridge/RtAudioBridge.hpp | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index d75b2739..3729841a 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -49,7 +49,11 @@ endif ifeq ($(MACOS),true) JACK_LIBS += -framework CoreAudio -framework CoreFoundation else ifeq ($(WINDOWS),true) -JACK_LIBS += -lksuser -lmfplat -lmfuuid -lole32 -lwinmm -lwmcodecdspuuid +JACK_LIBS += -lole32 -lwinmm +# DirectSound +JACK_LIBS += -ldsound +# WASAPI +# JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid else ifneq ($(HAIKU),true) ifeq ($(HAVE_ALSA),true) JACK_FLAGS += $(ALSA_FLAGS) diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index bf42738f..46b82c91 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -23,8 +23,8 @@ # define __MACOSX_CORE__ # define RTAUDIO_API_TYPE MACOSX_CORE #elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) -# define __WINDOWS_WASAPI__ -# define RTAUDIO_API_TYPE WINDOWS_WASAPI +# define __WINDOWS_DS__ +# define RTAUDIO_API_TYPE WINDOWS_DS #elif defined(HAVE_PULSEAUDIO) # define __LINUX_PULSE__ # define RTAUDIO_API_TYPE LINUX_PULSE From 56a65f56a369e5b031aac5bbfb5999e974e32fad Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 4 Nov 2021 00:47:35 +0000 Subject: [PATCH 232/504] Update pugl to ensure macOS windows start hidden by default --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 9c2c093b..7418e9d1 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 9c2c093b9157a8581de878b9ac3dba3f6b213b16 +Subproject commit 7418e9d1f0045d192c86e7dea25272264ca92f22 From 0f31c24917043d6841fcc16efcb0e4e85bc51a89 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 4 Nov 2021 12:06:36 +0000 Subject: [PATCH 233/504] Expose the custom nvgCreateGL for others to use Signed-off-by: falkTX --- dgl/NanoVG.hpp | 9 +++++++++ dgl/src/NanoVG.cpp | 22 +++++++++++----------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index b3483e97..e18074f5 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -42,6 +42,15 @@ START_NAMESPACE_DGL class NanoVG; +// ----------------------------------------------------------------------- +// Helper methods + +/** + Create a NanoVG context using the DPF-provided NanoVG library. + On Windows this will load a few extra OpenGL functions required for NanoVG to work. + */ +NVGcontext* nvgCreateGL(int flags); + // ----------------------------------------------------------------------- // NanoImage diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 8f6fa854..772391b0 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -102,28 +102,32 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) #endif #if defined(NANOVG_GL2) -# define nvgCreateGL nvgCreateGL2 +# define nvgCreateGLfn nvgCreateGL2 # define nvgDeleteGL nvgDeleteGL2 # define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2 # define nvglImageHandle nvglImageHandleGL2 #elif defined(NANOVG_GL3) -# define nvgCreateGL nvgCreateGL3 +# define nvgCreateGLfn nvgCreateGL3 # define nvgDeleteGL nvgDeleteGL3 # define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3 # define nvglImageHandle nvglImageHandleGL3 #elif defined(NANOVG_GLES2) -# define nvgCreateGL nvgCreateGLES2 +# define nvgCreateGLfn nvgCreateGLES2 # define nvgDeleteGL nvgDeleteGLES2 # define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2 # define nvglImageHandle nvglImageHandleGLES2 #elif defined(NANOVG_GLES3) -# define nvgCreateGL nvgCreateGLES3 +# define nvgCreateGLfn nvgCreateGLES3 # define nvgDeleteGL nvgDeleteGLES3 # define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3 # define nvglImageHandle nvglImageHandleGLES3 #endif -static NVGcontext* nvgCreateGL_helper(int flags) +// ----------------------------------------------------------------------- + +START_NAMESPACE_DGL + +NVGcontext* nvgCreateGL(int flags) { #if defined(DISTRHO_OS_WINDOWS) # if defined(__GNUC__) && (__GNUC__ >= 9) @@ -189,13 +193,9 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) # pragma GCC diagnostic pop # endif #endif - return nvgCreateGL(flags); + return nvgCreateGLfn(flags); } -// ----------------------------------------------------------------------- - -START_NAMESPACE_DGL - // ----------------------------------------------------------------------- // DGL Color class conversion @@ -312,7 +312,7 @@ NanoVG::Paint::operator NVGpaint() const noexcept // NanoVG NanoVG::NanoVG(int flags) - : fContext(nvgCreateGL_helper(flags)), + : fContext(nvgCreateGL(flags)), fInFrame(false), fIsSubWidget(false) {} From ad055720fc348478717037017edb003072278d6d Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 5 Nov 2021 12:41:58 +0000 Subject: [PATCH 234/504] Add Plugin::isDummyInstance() method, useful for some plugins Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 8 ++++++++ distrho/src/DistrhoPlugin.cpp | 6 ++++++ distrho/src/DistrhoPluginInternal.hpp | 11 +++++++---- distrho/src/DistrhoPluginLADSPA+DSSI.cpp | 2 ++ distrho/src/DistrhoPluginLV2export.cpp | 2 ++ distrho/src/DistrhoPluginVST2.cpp | 2 ++ distrho/src/DistrhoPluginVST3.cpp | 2 ++ 7 files changed, 29 insertions(+), 4 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index c59a9e34..50dad44f 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -845,6 +845,14 @@ public: */ const char* getBundlePath() const noexcept; + /** + Check if this plugin instance is a "dummy" one used for plugin meta-data/information export.@n + When true no processing will be done, the plugin is created only to extract information.@n + In DPF, LADSPA/DSSI, VST2 and VST3 formats create one global instance per plugin binary + while LV2 creates one when generating turtle meta-data. + */ + bool isDummyInstance() const noexcept; + #if DISTRHO_PLUGIN_WANT_TIMEPOS /** Get the current host transport time position.@n diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 2daa9ed2..2242b16c 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -24,6 +24,7 @@ START_NAMESPACE_DISTRHO uint32_t d_nextBufferSize = 0; double d_nextSampleRate = 0.0; const char* d_nextBundlePath = nullptr; +bool d_nextPluginIsDummy = false; bool d_nextCanRequestParameterValueChanges = false; /* ------------------------------------------------------------------------------------------------------------ @@ -106,6 +107,11 @@ const char* Plugin::getBundlePath() const noexcept return pData->bundlePath; } +bool Plugin::isDummyInstance() const noexcept +{ + return pData->isDummy; +} + #if DISTRHO_PLUGIN_WANT_TIMEPOS const TimePosition& Plugin::getTimePosition() const noexcept { diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 208dc34f..969e6685 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -34,6 +34,7 @@ static const uint32_t kMaxMidiEvents = 512; extern uint32_t d_nextBufferSize; extern double d_nextSampleRate; extern const char* d_nextBundlePath; +extern bool d_nextPluginIsDummy; extern bool d_nextCanRequestParameterValueChanges; // ----------------------------------------------------------------------- @@ -84,6 +85,8 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por // Plugin private data struct Plugin::PrivateData { + const bool canRequestParameterValueChanges; + const bool isDummy; bool isProcessing; #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 @@ -124,10 +127,11 @@ struct Plugin::PrivateData { uint32_t bufferSize; double sampleRate; char* bundlePath; - bool canRequestParameterValueChanges; PrivateData() noexcept - : isProcessing(false), + : canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges), + isDummy(d_nextPluginIsDummy), + isProcessing(false), #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 audioPorts(nullptr), #endif @@ -153,8 +157,7 @@ struct Plugin::PrivateData { requestParameterValueChangeCallbackFunc(nullptr), bufferSize(d_nextBufferSize), sampleRate(d_nextSampleRate), - bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr), - canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges) + bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr) { DISTRHO_SAFE_ASSERT(bufferSize != 0); DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); diff --git a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp index f3492ccb..48b68eb0 100644 --- a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp +++ b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp @@ -553,9 +553,11 @@ static const struct DescriptorInitializer // Create dummy plugin to get data from d_nextBufferSize = 512; d_nextSampleRate = 44100.0; + d_nextPluginIsDummy = true; const PluginExporter plugin(nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; + d_nextPluginIsDummy = false; // Get port count, init ulong port = 0; diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index dd4aa285..712d722a 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -231,9 +231,11 @@ void lv2_generate_ttl(const char* const basename) // Dummy plugin to get data from d_nextBufferSize = 512; d_nextSampleRate = 44100.0; + d_nextPluginIsDummy = true; PluginExporter plugin(nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; + d_nextPluginIsDummy = false; const String pluginDLL(basename); const String pluginTTL(pluginDLL + ".ttl"); diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 147bf0c1..d285dacf 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1406,6 +1406,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t // set valid but dummy values d_nextBufferSize = 512; d_nextSampleRate = 44100.0; + d_nextPluginIsDummy = true; d_nextCanRequestParameterValueChanges = true; } @@ -1417,6 +1418,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t // unset d_nextBufferSize = 0; d_nextSampleRate = 0.0; + d_nextPluginIsDummy = false; d_nextCanRequestParameterValueChanges = false; *(PluginExporter**)ptr = &plugin; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 48dcb64b..db62be54 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -3544,10 +3544,12 @@ static const PluginExporter& _getPluginInfo() { d_nextBufferSize = 512; d_nextSampleRate = 44100.0; + d_nextPluginIsDummy = true; d_nextCanRequestParameterValueChanges = true; static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; + d_nextPluginIsDummy = false; d_nextCanRequestParameterValueChanges = false; return gPluginInfo; From fbbfe11a5bb1a9d02f9fa7a341caae7e7d532e7e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 6 Nov 2021 20:33:15 +0000 Subject: [PATCH 235/504] Add missing LV2_STATE_ERR_NO_SPACE to pre-included LV2 headers Signed-off-by: falkTX --- distrho/src/lv2/state.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distrho/src/lv2/state.h b/distrho/src/lv2/state.h index 09787d93..7e162cb4 100644 --- a/distrho/src/lv2/state.h +++ b/distrho/src/lv2/state.h @@ -99,7 +99,8 @@ typedef enum { LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ LV2_STATE_ERR_BAD_FLAGS = 3, /**< Failed due to unsupported flags. */ LV2_STATE_ERR_NO_FEATURE = 4, /**< Failed due to missing features. */ - LV2_STATE_ERR_NO_PROPERTY = 5 /**< Failed due to missing property. */ + LV2_STATE_ERR_NO_PROPERTY = 5, /**< Failed due to missing property. */ + LV2_STATE_ERR_NO_SPACE = 6 /**< Failed due to insufficient space. */ } LV2_State_Status; /** From 29709cbe4e1f5d97b7630fd1664e2eb1210bb82f Mon Sep 17 00:00:00 2001 From: Filipe Coelho Date: Fri, 12 Nov 2021 15:11:48 +0000 Subject: [PATCH 236/504] UI filebrowser saving mode, separate from pugl/DGL/Window (#349) * Add UI::openFileBrowser that matches Window::openFileBrowser * Add empty implementation so it builds * Move file browser dialog implementation into its own file Signed-off-by: falkTX * Fix warnings Signed-off-by: falkTX * Fix tests; Add non-implemented saving flag Signed-off-by: falkTX * Initial DBus/freedesktop file browser implementation Signed-off-by: falkTX * Build fixes Signed-off-by: falkTX * Fix window id Signed-off-by: falkTX * More build fixes Signed-off-by: falkTX * More file dialog tweaks Signed-off-by: falkTX * Attempted fixes Signed-off-by: falkTX * Fix C++98 build Signed-off-by: falkTX * Fix windows build Signed-off-by: falkTX * Really fix windows builds Signed-off-by: falkTX * Fix for MSVC Signed-off-by: falkTX * Yet another fix attempt Signed-off-by: falkTX * Also fix macOS side Signed-off-by: falkTX * More attempted fixes, this is getting annoying... Signed-off-by: falkTX * FileBrowserDialog: Implement saving in Windows Signed-off-by: falkTX * FileBrowserDialog: Implement saving on macOS Signed-off-by: falkTX * Rework last commit Signed-off-by: falkTX * One more macOS fix needed Signed-off-by: falkTX * unref dbus connection on close Signed-off-by: falkTX * More build fixes Signed-off-by: falkTX * Hopefully final macOS fix Signed-off-by: falkTX * Add libdbus-1-dev to CI Signed-off-by: falkTX * Check that org.freedesktop.portal.Desktop exists before connecting Signed-off-by: falkTX * Less indentation Signed-off-by: falkTX * Fix macOS build --- .github/workflows/cmake.yml | 1 + .github/workflows/example-plugins.yml | 10 +- .github/workflows/makefile.yml | 2 +- Makefile.base.mk | 6 + dgl/Window.hpp | 57 +-- dgl/src/WindowPrivateData.cpp | 341 ++----------- dgl/src/WindowPrivateData.hpp | 28 +- dgl/src/pugl.cpp | 150 +----- dgl/src/pugl.hpp | 20 - distrho/DistrhoUI.hpp | 26 +- distrho/DistrhoUI_macOS.mm | 35 +- distrho/extra/FileBrowserDialog.cpp | 569 ++++++++++++++++++++++ distrho/extra/FileBrowserDialog.hpp | 134 +++++ {dgl/src => distrho/extra}/sofd/libsofd.c | 0 {dgl/src => distrho/extra}/sofd/libsofd.h | 0 distrho/src/DistrhoDefines.h | 1 + distrho/src/DistrhoUI.cpp | 42 +- distrho/src/DistrhoUIPrivateData.hpp | 2 +- tests/FileBrowserDialog.cpp | 1 + 19 files changed, 856 insertions(+), 569 deletions(-) create mode 100644 distrho/extra/FileBrowserDialog.cpp create mode 100644 distrho/extra/FileBrowserDialog.hpp rename {dgl/src => distrho/extra}/sofd/libsofd.c (100%) rename {dgl/src => distrho/extra}/sofd/libsofd.h (100%) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ef9de80c..5932f468 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -30,6 +30,7 @@ jobs: liblo-dev \ libgl-dev \ libcairo2-dev \ + libdbus-1-dev \ libx11-dev - name: Create Build Environment shell: bash diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index 48975cc2..c8bf4505 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -26,7 +26,7 @@ jobs: echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-arm64.list sudo apt-get update -qq - sudo apt-get install -yq g++-aarch64-linux-gnu libasound2-dev:arm64 libcairo2-dev:arm64 libgl1-mesa-dev:arm64 liblo-dev:arm64 libpulse-dev:arm64 libx11-dev:arm64 libxcursor-dev:arm64 libxext-dev:arm64 libxrandr-dev:arm64 qemu-user-static + sudo apt-get install -yq g++-aarch64-linux-gnu libasound2-dev:arm64 libcairo2-dev:arm64 libdbus-1-dev:arm64 libgl1-mesa-dev:arm64 liblo-dev:arm64 libpulse-dev:arm64 libx11-dev:arm64 libxcursor-dev:arm64 libxext-dev:arm64 libxrandr-dev:arm64 qemu-user-static # fix broken Ubuntu packages missing pkg-config file in multi-arch package sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev libxcursor-dev libxrandr-dev sudo ln -s /usr/lib/aarch64-linux-gnu/liblo.so.7 /usr/lib/aarch64-linux-gnu/liblo.so @@ -63,7 +63,7 @@ jobs: echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ports-armhf.list sudo apt-get update -qq - sudo apt-get install -yq g++-arm-linux-gnueabihf libasound2-dev:armhf libcairo2-dev:armhf libgl1-mesa-dev:armhf liblo-dev:armhf libpulse-dev:armhf libx11-dev:armhf libxcursor-dev:armhf libxext-dev:armhf libxrandr-dev:armhf qemu-user-static + sudo apt-get install -yq g++-arm-linux-gnueabihf libasound2-dev:armhf libcairo2-dev:armhf libdbus-1-dev:armhf libgl1-mesa-dev:armhf liblo-dev:armhf libpulse-dev:armhf libx11-dev:armhf libxcursor-dev:armhf libxext-dev:armhf libxrandr-dev:armhf qemu-user-static # fix broken Ubuntu packages missing pkg-config file in multi-arch package sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev libxcursor-dev libxrandr-dev sudo ln -s /usr/lib/arm-linux-gnueabihf/liblo.so.7 /usr/lib/arm-linux-gnueabihf/liblo.so @@ -96,7 +96,7 @@ jobs: run: | sudo dpkg --add-architecture i386 sudo apt-get update -qq - sudo apt-get install -yq g++-multilib libasound2-dev:i386 libcairo2-dev:i386 libgl1-mesa-dev:i386 liblo-dev:i386 libpulse-dev:i386 libx11-dev:i386 libxcursor-dev:i386 libxext-dev:i386 libxrandr-dev:i386 + sudo apt-get install -yq g++-multilib libasound2-dev:i386 libcairo2-dev:i386 libdbus-1-dev:i386 libgl1-mesa-dev:i386 liblo-dev:i386 libpulse-dev:i386 libx11-dev:i386 libxcursor-dev:i386 libxext-dev:i386 libxrandr-dev:i386 - name: Build linux x86 env: CFLAGS: -m32 @@ -124,7 +124,7 @@ jobs: - name: Set up dependencies run: | sudo apt-get update -qq - sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev + sudo apt-get install -yq libasound2-dev libcairo2-dev libdbus-1-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev - name: Build linux x86_64 env: LDFLAGS: -static-libgcc -static-libstdc++ @@ -250,7 +250,7 @@ jobs: sudo dpkg -i kxstudio-repos_10.0.3_all.deb sudo apt-get update -qq # build-deps - sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev + sudo apt-get install -yq libasound2-dev libcairo2-dev libdbus-1-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev # runtime testing sudo apt-get install -yq carla-git lilv-utils lv2-dev lv2lint valgrind - name: Build plugins diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index f453d10d..ea456232 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -20,7 +20,7 @@ jobs: - name: Set up dependencies run: | sudo apt-get update -qq - sudo apt-get install -yq libasound2-dev libcairo2-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev xvfb + sudo apt-get install -yq libasound2-dev libcairo2-dev libdbus-1-dev libgl1-mesa-dev liblo-dev libpulse-dev libx11-dev libxcursor-dev libxext-dev libxrandr-dev xvfb - name: Without any warnings env: CFLAGS: -Werror diff --git a/Makefile.base.mk b/Makefile.base.mk index d5c9ce03..b5e95ead 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -238,6 +238,7 @@ HAVE_OPENGL = true else HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) ifneq ($(HAIKU),true) +HAVE_DBUS = $(shell $(PKG_CONFIG) --exists dbus-1 && echo true) HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true) HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) @@ -284,6 +285,10 @@ DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 endif ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifeq ($(HAVE_DBUS),true) +DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS +DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) +endif ifeq ($(HAVE_X11),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) @@ -476,6 +481,7 @@ features: $(call print_available,UNIX) @echo === Detected features $(call print_available,HAVE_ALSA) + $(call print_available,HAVE_DBUS) $(call print_available,HAVE_CAIRO) $(call print_available,HAVE_DGL) $(call print_available,HAVE_LIBLO) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 181f0122..7aaa17df 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -19,9 +19,14 @@ #include "Geometry.hpp" +#ifndef DGL_FILE_BROWSER_DISABLED +# include "../distrho/extra/FileBrowserDialog.hpp" +#endif + START_NAMESPACE_DGL class Application; +class PluginWindow; class TopLevelWidget; // ----------------------------------------------------------------------- @@ -53,53 +58,9 @@ class Window public: #ifndef DGL_FILE_BROWSER_DISABLED - /** - File browser options. - @see Window::openFileBrowser - */ - struct FileBrowserOptions { - /** - File browser button state. - This allows to customize the behaviour of the file browse dialog buttons. - Note these are merely hints, not all systems support them. - */ - enum ButtonState { - kButtonInvisible, - kButtonVisibleUnchecked, - kButtonVisibleChecked, - }; - - /** Start directory, uses current working directory if null */ - const char* startDir; - /** File browser dialog window title, uses "FileBrowser" if null */ - const char* title; - // TODO file filter - - /** - File browser buttons. - */ - struct Buttons { - /** Whether to list all files vs only those with matching file extension */ - ButtonState listAllFiles; - /** Whether to show hidden files */ - ButtonState showHidden; - /** Whether to show list of places (bookmarks) */ - ButtonState showPlaces; - - /** Constructor for default values */ - Buttons() - : listAllFiles(kButtonVisibleChecked), - showHidden(kButtonVisibleUnchecked), - showPlaces(kButtonVisibleChecked) {} - } buttons; - - /** Constructor for default values */ - FileBrowserOptions() - : startDir(nullptr), - title(nullptr), - buttons() {} - }; -#endif // DGL_FILE_BROWSER_DISABLED + typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle; + typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions; +#endif /** Window graphics context as a scoped struct. @@ -361,7 +322,7 @@ public: #ifndef DGL_FILE_BROWSER_DISABLED /** - Open a file browser dialog with this window as parent. + Open a file browser dialog with this window as transient parent. A few options can be specified to setup the dialog. If a path is selected, onFileSelected() will be called with the user chosen path. diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 4fa226a3..097299b2 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -19,18 +19,6 @@ #include "pugl.hpp" -#include "../../distrho/extra/String.hpp" - -#ifdef DISTRHO_OS_WINDOWS -# include -# include -# include -# include -# include -#else -# include -#endif - // #define DGL_DEBUG_EVENTS #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) @@ -64,11 +52,6 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -#ifdef DISTRHO_OS_WINDOWS -// static pointer used for direct comparisons -static const char* const kWin32SelectedFileCancelled = "__dpf_cancelled__"; -#endif - static double getDesktopScaleFactor(const PuglView* const view) { // allow custom scale for testing @@ -83,154 +66,6 @@ static double getDesktopScaleFactor(const PuglView* const view) // ----------------------------------------------------------------------- -#ifdef DISTRHO_OS_WINDOWS -struct FileBrowserThread::PrivateData { - OPENFILENAMEW ofn; - volatile bool threadCancelled; - uintptr_t threadHandle; - std::vector fileNameW; - std::vector startDirW; - std::vector titleW; - const bool isEmbed; - const char*& win32SelectedFile; - - PrivateData(const bool embed, const char*& file) - : threadCancelled(false), - threadHandle(0), - fileNameW(32768), - isEmbed(embed), - win32SelectedFile(file) - { - std::memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.lpstrFile = fileNameW.data(); - ofn.nMaxFile = (DWORD)fileNameW.size(); - } - - void setup(const char* const startDir, - const char* const title, - const uintptr_t winId, - const Window::FileBrowserOptions options) - { - ofn.hwndOwner = (HWND)winId; - - ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; - if (options.buttons.showHidden == Window::FileBrowserOptions::kButtonVisibleChecked) - ofn.Flags |= OFN_FORCESHOWHIDDEN; - - ofn.FlagsEx = 0x0; - if (options.buttons.showPlaces == Window::FileBrowserOptions::kButtonInvisible) - ofn.FlagsEx |= OFN_EX_NOPLACESBAR; - - startDirW.resize(std::strlen(startDir) + 1); - if (MultiByteToWideChar(CP_UTF8, 0, startDir, -1, startDirW.data(), static_cast(startDirW.size()))) - ofn.lpstrInitialDir = startDirW.data(); - - titleW.resize(std::strlen(title) + 1); - if (MultiByteToWideChar(CP_UTF8, 0, title, -1, titleW.data(), static_cast(titleW.size()))) - ofn.lpstrTitle = titleW.data(); - } - - void run() - { - const char* nextFile = nullptr; - - if (GetOpenFileNameW(&ofn)) - { - if (threadCancelled) - { - threadHandle = 0; - return; - } - - // back to UTF-8 - std::vector fileNameA(4 * 32768); - if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, - fileNameA.data(), (int)fileNameA.size(), - nullptr, nullptr)) - { - nextFile = strdup(fileNameA.data()); - } - } - - if (threadCancelled) - { - threadHandle = 0; - return; - } - - if (nextFile == nullptr) - nextFile = kWin32SelectedFileCancelled; - - win32SelectedFile = nextFile; - threadHandle = 0; - } -}; - -FileBrowserThread::FileBrowserThread(const bool isEmbed, const char*& file) - : pData(new PrivateData(isEmbed, file)) {} - -FileBrowserThread::~FileBrowserThread() -{ - stop(); - delete pData; -} - -unsigned __stdcall FileBrowserThread__run(void* const arg) -{ - // CoInitializeEx(nullptr, COINIT_MULTITHREADED); - static_cast(arg)->pData->run(); - // CoUninitialize(); - _endthreadex(0); - return 0; -} - -void FileBrowserThread::start(const char* const startDir, - const char* const title, - const uintptr_t winId, - const Window::FileBrowserOptions options) -{ - pData->setup(startDir, title, winId, options); - - uint threadId; - pData->threadCancelled = false; - pData->threadHandle = _beginthreadex(nullptr, 0, FileBrowserThread__run, this, 0, &threadId); -} - -void FileBrowserThread::stop() -{ - pData->threadCancelled = true; - - if (pData->threadHandle == 0) - return; - - // if previous dialog running, carefully close its window - const HWND owner = pData->isEmbed ? GetParent(pData->ofn.hwndOwner) : pData->ofn.hwndOwner; - - if (owner != nullptr && owner != INVALID_HANDLE_VALUE) - { - const HWND window = GetWindow(owner, GW_HWNDFIRST); - - if (window != nullptr && window != INVALID_HANDLE_VALUE) - { - SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0); - SendMessage(window, WM_CLOSE, 0, 0); - WaitForSingleObject((HANDLE)pData->threadHandle, 5000); - } - } - - // not good if thread still running, but let's close the handle anyway - if (pData->threadHandle != 0) - { - CloseHandle((HANDLE)pData->threadHandle); - pData->threadHandle = 0; - } -} - -#endif // DISTRHO_OS_WINDOWS && !_MSC_VER - -// ----------------------------------------------------------------------- - Window::PrivateData::PrivateData(Application& a, Window* const s) : app(a), appData(a.pData), @@ -249,9 +84,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS - win32SelectedFile(nullptr), - win32FileThread(false, win32SelectedFile), +#ifndef DGL_FILE_BROWSER_DISABLED + fileBrowserHandle(nullptr), #endif modal() { @@ -276,9 +110,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS - win32SelectedFile(nullptr), - win32FileThread(false, win32SelectedFile), +#ifndef DGL_FILE_BROWSER_DISABLED + fileBrowserHandle(nullptr), #endif modal(ppData) { @@ -307,9 +140,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS - win32SelectedFile(nullptr), - win32FileThread(isEmbed, win32SelectedFile), +#ifndef DGL_FILE_BROWSER_DISABLED + fileBrowserHandle(nullptr), #endif modal() { @@ -340,9 +172,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, keepAspectRatio(false), ignoreIdleCallbacks(false), filenameToRenderInto(nullptr), -#ifdef DISTRHO_OS_WINDOWS - win32SelectedFile(nullptr), - win32FileThread(isEmbed, win32SelectedFile), +#ifndef DGL_FILE_BROWSER_DISABLED + fileBrowserHandle(nullptr), #endif modal() { @@ -363,8 +194,9 @@ Window::PrivateData::~PrivateData() if (isEmbed) { -#ifdef HAVE_X11 - sofdFileDialogClose(); +#ifndef DGL_FILE_BROWSER_DISABLED + if (fileBrowserHandle != nullptr) + fileBrowserClose(fileBrowserHandle); #endif puglHide(view); appData->oneWindowClosed(); @@ -372,13 +204,6 @@ Window::PrivateData::~PrivateData() isVisible = false; } -#ifdef DISTRHO_OS_WINDOWS - win32FileThread.stop(); - - if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) - std::free(const_cast(win32SelectedFile)); -#endif - puglFreeView(view); } @@ -522,9 +347,14 @@ void Window::PrivateData::hide() if (modal.enabled) stopModal(); -#ifdef HAVE_X11 - sofdFileDialogClose(); +#ifndef DGL_FILE_BROWSER_DISABLED + if (fileBrowserHandle != nullptr) + { + fileBrowserClose(fileBrowserHandle); + fileBrowserHandle = nullptr; + } #endif + puglHide(view); isVisible = false; @@ -566,20 +396,12 @@ void Window::PrivateData::setResizable(const bool resizable) void Window::PrivateData::idleCallback() { #ifndef DGL_FILE_BROWSER_DISABLED -# ifdef DISTRHO_OS_WINDOWS - if (const char* path = win32SelectedFile) + if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle)) { - win32SelectedFile = nullptr; - if (path == kWin32SelectedFileCancelled) - path = nullptr; - self->onFileSelected(path); - std::free(const_cast(path)); + self->onFileSelected(fileBrowserGetPath(fileBrowserHandle)); + fileBrowserClose(fileBrowserHandle); + fileBrowserHandle = nullptr; } -# endif -# ifdef HAVE_X11 - if (sofdFileDialogIdle(view)) - self->onFileSelected(sofdFileDialogGetPath()); -# endif #endif } @@ -619,120 +441,23 @@ bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback) // ----------------------------------------------------------------------- // file handling -bool Window::PrivateData::openFileBrowser(const Window::FileBrowserOptions& options) +bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options) { - using DISTRHO_NAMESPACE::String; - - // -------------------------------------------------------------------------- - // configure start dir - - // TODO: get abspath if needed - // TODO: cross-platform - - String startDir(options.startDir); - - if (startDir.isEmpty()) - { - // TESTING verify this whole thing... -# ifdef DISTRHO_OS_WINDOWS - if (char* const cwd = _getcwd(nullptr, 0)) - { - startDir = cwd; - std::free(cwd); - } -# else - if (char* const cwd = getcwd(nullptr, 0)) - { - startDir = cwd; - std::free(cwd); - } -# endif - } - - DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false); - - if (! startDir.endsWith(DISTRHO_OS_SEP)) - startDir += DISTRHO_OS_SEP_STR; - - // -------------------------------------------------------------------------- - // configure title - - String title(options.title); - - if (title.isEmpty()) - { - title = puglGetWindowTitle(view); - - if (title.isEmpty()) - title = "FileBrowser"; - } - - // -------------------------------------------------------------------------- - // show - -# ifdef DISTRHO_OS_MAC - uint flags = 0x0; - - if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked) - flags |= 0x001; - else if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked) - flags |= 0x002; - - if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) - flags |= 0x010; - else if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked) - flags |= 0x020; + if (fileBrowserHandle != nullptr) + fileBrowserClose(fileBrowserHandle); - if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked) - flags |= 0x100; - else if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked) - flags |= 0x200; + FileBrowserOptions options2 = options; - return puglMacOSFilePanelOpen(view, startDir, title, flags, openPanelCallback); -# endif - -# ifdef DISTRHO_OS_WINDOWS - // only one possible at a time - DISTRHO_SAFE_ASSERT_RETURN(win32FileThread.pData->threadHandle == 0, false); - - if (win32SelectedFile != nullptr && win32SelectedFile != kWin32SelectedFileCancelled) - std::free(const_cast(win32SelectedFile)); - win32SelectedFile = nullptr; - - win32FileThread.start(startDir, title, puglGetNativeWindow(view), options); - return true; -# endif - -# ifdef HAVE_X11 - uint flags = 0x0; - - if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked) - flags |= 0x001; - else if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked) - flags |= 0x002; + if (options2.title == nullptr) + options2.title = puglGetWindowTitle(view); - if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) - flags |= 0x010; - else if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked) - flags |= 0x020; + fileBrowserHandle = fileBrowserCreate(isEmbed, + puglGetNativeWindow(view), + autoScaling ? autoScaleFactor : scaleFactor, + options2); - if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked) - flags |= 0x100; - else if (options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked) - flags |= 0x200; - - return sofdFileDialogShow(view, startDir, title, flags, autoScaling ? autoScaleFactor : scaleFactor); -# endif - - return false; + return fileBrowserHandle != nullptr; } - -# ifdef DISTRHO_OS_MAC -void Window::PrivateData::openPanelCallback(PuglView* const view, const char* const path) -{ - ((Window::PrivateData*)puglGetHandle(view))->self->onFileSelected(path); -} -# endif #endif // ! DGL_FILE_BROWSER_DISABLED // ----------------------------------------------------------------------- diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 6db52747..68b2e375 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -31,21 +31,6 @@ class TopLevelWidget; // ----------------------------------------------------------------------- -#ifdef DISTRHO_OS_WINDOWS -struct FileBrowserThread -{ - struct PrivateData; - PrivateData* const pData; - - FileBrowserThread(bool isEmbed, const char*& win32SelectedFile); - ~FileBrowserThread(); - void start(const char* startDir, const char* title, uintptr_t winId, Window::FileBrowserOptions options); - void stop(); -}; -#endif - -// ----------------------------------------------------------------------- - struct Window::PrivateData : IdleCallback { /** Reference to the DGL Application class this (private data) window associates with. */ Application& app; @@ -95,11 +80,9 @@ struct Window::PrivateData : IdleCallback { /** 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; - /** Thread where the openFileBrowser runs. */ - FileBrowserThread win32FileThread; +#ifndef DGL_FILE_BROWSER_DISABLED + /** Handle for file browser dialog operations. */ + FileBrowserHandle fileBrowserHandle; #endif /** Modal window setup. */ @@ -176,10 +159,7 @@ struct Window::PrivateData : IdleCallback { #ifndef DGL_FILE_BROWSER_DISABLED // file handling - bool openFileBrowser(const Window::FileBrowserOptions& options); -# ifdef DISTRHO_OS_MAC - static void openPanelCallback(PuglView* view, const char* path); -# endif + bool openFileBrowser(const FileBrowserOptions& options); #endif static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index c4e31699..4d28af08 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -42,6 +42,7 @@ # endif #elif defined(DISTRHO_OS_WINDOWS) # include +# include # include # include # ifdef DGL_CAIRO @@ -57,6 +58,7 @@ # endif #else # include +# include # include # include # include @@ -90,10 +92,12 @@ # endif #endif -#ifdef HAVE_X11 -# define DBLCLKTME 400 -# include "sofd/libsofd.h" -# include "sofd/libsofd.c" +#ifndef DGL_FILE_BROWSER_DISABLED +# ifdef DISTRHO_OS_MAC +# import "../../distrho/extra/FileBrowserDialog.cpp" +# else +# include "../../distrho/extra/FileBrowserDialog.cpp" +# endif #endif #ifndef DISTRHO_OS_MAC @@ -528,53 +532,6 @@ void puglMacOSShowCentered(PuglView* const view) } // -------------------------------------------------------------------------------------------------------------------- -// macOS specific, setup file browser dialog - -bool puglMacOSFilePanelOpen(PuglView* const view, - const char* const startDir, const char* const title, const uint flags, - openPanelCallback callback) -{ - PuglInternals* impl = view->impl; - - NSOpenPanel* const panel = [NSOpenPanel openPanel]; - - [panel setAllowsMultipleSelection:NO]; - [panel setCanChooseDirectories:NO]; - [panel setCanChooseFiles:YES]; - [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:startDir]]]; - - // TODO file filter using allowedContentTypes: [UTType] - - if (flags & 0x001) - [panel setAllowsOtherFileTypes:YES]; - if (flags & 0x010) - [panel setShowsHiddenFiles:YES]; - - NSString* titleString = [[NSString alloc] - initWithBytes:title - length:strlen(title) - encoding:NSUTF8StringEncoding]; - [panel setTitle:titleString]; - - dispatch_async(dispatch_get_main_queue(), ^ - { - [panel beginSheetModalForWindow:(impl->window ? impl->window : [view->impl->wrapperView window]) - completionHandler:^(NSModalResponse result) - { - if (result == NSModalResponseOK && [[panel URL] isFileURL]) - { - NSString* const path = [[panel URL] path]; - callback(view, [path UTF8String]); - } - else - { - callback(view, nullptr); - } - }]; - }); - - return true; -} #endif #ifdef DISTRHO_OS_WINDOWS @@ -680,96 +637,7 @@ void puglX11SetWindowTypeAndPID(const PuglView* const view) } // -------------------------------------------------------------------------------------------------------------------- -// X11 specific stuff for sofd - -static Display* sofd_display; -static char* sofd_filename; - -// -------------------------------------------------------------------------------------------------------------------- -// X11 specific, show file dialog via sofd - -bool sofdFileDialogShow(PuglView* const view, - const char* const startDir, const char* const title, - const uint flags, const double scaleFactor) -{ - // only one possible at a time - DISTRHO_SAFE_ASSERT_RETURN(sofd_display == nullptr, false); - - sofd_display = XOpenDisplay(nullptr); - DISTRHO_SAFE_ASSERT_RETURN(sofd_display != nullptr, false); - - DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); - DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); - - x_fib_cfg_buttons(3, flags & 0x001 ? 1 : flags & 0x002 ? 0 : -1); - x_fib_cfg_buttons(1, flags & 0x010 ? 1 : flags & 0x020 ? 0 : -1); - x_fib_cfg_buttons(2, flags & 0x100 ? 1 : flags & 0x200 ? 0 : -1); - - return (x_fib_show(sofd_display, view->impl->win, 0, 0, scaleFactor + 0.5) == 0); -} - -// -------------------------------------------------------------------------------------------------------------------- -// X11 specific, idle sofd file dialog, returns true if dialog was closed (with or without a file selection) - -bool sofdFileDialogIdle(PuglView* const view) -{ - if (sofd_display == nullptr) - return false; - - XEvent event; - while (XPending(sofd_display) > 0) - { - XNextEvent(sofd_display, &event); - - if (x_fib_handle_events(sofd_display, &event) == 0) - continue; - - if (sofd_filename != nullptr) - std::free(sofd_filename); - - if (x_fib_status() > 0) - sofd_filename = x_fib_filename(); - else - sofd_filename = nullptr; - - x_fib_close(sofd_display); - XCloseDisplay(sofd_display); - sofd_display = nullptr; - return true; - } - - return false; -} - -// -------------------------------------------------------------------------------------------------------------------- -// X11 specific, close sofd file dialog - -void sofdFileDialogClose() -{ - if (sofd_display != nullptr) - { - x_fib_close(sofd_display); - XCloseDisplay(sofd_display); - sofd_display = nullptr; - } - - if (sofd_filename != nullptr) - { - std::free(sofd_filename); - sofd_filename = nullptr; - } -} - -// -------------------------------------------------------------------------------------------------------------------- -// X11 specific, get path chosen via sofd file dialog - -const char* sofdFileDialogGetPath() -{ - return sofd_filename; -} -#endif - -// -------------------------------------------------------------------------------------------------------------------- +#endif // HAVE_X11 #ifndef DISTRHO_OS_MAC END_NAMESPACE_DGL diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 63b034a5..c19c82eb 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -111,10 +111,6 @@ puglMacOSRemoveChildWindow(PuglView* view, PuglView* child); // macOS specific, center view based on parent coordinates (if there is one) PUGL_API void puglMacOSShowCentered(PuglView* view); - -// macOS specific, setup file browser dialog -typedef void (*openPanelCallback)(PuglView* view, const char* path); -bool puglMacOSFilePanelOpen(PuglView* view, const char* startDir, const char* title, uint flags, openPanelCallback callback); #endif #ifdef DISTRHO_OS_WINDOWS @@ -139,22 +135,6 @@ puglX11GrabFocus(const PuglView* view); // X11 specific, set dialog window type and pid hints PUGL_API void puglX11SetWindowTypeAndPID(const PuglView* view); - -// X11 specific, show file dialog via sofd -PUGL_API bool -sofdFileDialogShow(PuglView* view, const char* startDir, const char* title, uint flags, double scaleFactor); - -// X11 specific, idle sofd file dialog, returns true if dialog was closed (with or without a file selection) -PUGL_API bool -sofdFileDialogIdle(PuglView* const view); - -// X11 specific, close sofd file dialog -PUGL_API void -sofdFileDialogClose(); - -// X11 specific, get path chosen via sofd file dialog -PUGL_API const char* -sofdFileDialogGetPath(); #endif PUGL_END_DECLS diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 2c121788..e7140828 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -48,6 +48,10 @@ typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; typedef DGL_NAMESPACE::TopLevelWidget UIWidget; #endif +#ifndef DGL_FILE_BROWSER_DISABLED +# include "extra/FileBrowserDialog.hpp" +#endif + START_NAMESPACE_DGL class PluginWindow; END_NAMESPACE_DGL @@ -183,6 +187,22 @@ public: void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); #endif +#ifndef DGL_FILE_BROWSER_DISABLED + /** + Open a file browser dialog with this window as transient parent.@n + A few options can be specified to setup the dialog. + + If a path is selected, onFileSelected() will be called with the user chosen path. + If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename. + + This function does not block the event loop. + + @note This is exactly the same API as provided by the Window class, + but redeclared here so that non-embed/DGL based UIs can still use file browser related functions. + */ + bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); +#endif + #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS /* -------------------------------------------------------------------------------------------------------- * Direct DSP access - DO NOT USE THIS UNLESS STRICTLY NECESSARY!! */ @@ -297,8 +317,9 @@ protected: The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code. */ virtual void uiReshape(uint width, uint height); +#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# ifndef DGL_FILE_BROWSER_DISABLED +#ifndef DGL_FILE_BROWSER_DISABLED /** Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser(). This function is for plugin UIs to be able to override Window::onFileSelected(const char*). @@ -309,8 +330,7 @@ protected: If you need to use files as plugin state, please setup and use DISTRHO_PLUGIN_WANT_STATEFILES instead. */ virtual void uiFileBrowserSelected(const char* filename); -# endif -#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +#endif /* -------------------------------------------------------------------------------------------------------- * UI Resize Handling, internal */ diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index 5127762d..24f24f37 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -22,10 +22,15 @@ #include "src/DistrhoDefines.h" #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#import -#include -#include +# import +# include +# include +# ifndef DGL_FILE_BROWSER_DISABLED +# import "extra/FileBrowserDialog.cpp" +# endif + +// Declared in DistrhoUI.cpp but defined here because it uses Obj-C START_NAMESPACE_DISTRHO double getDesktopScaleFactor(const uintptr_t parentWindowHandle) { @@ -40,19 +45,19 @@ double getDesktopScaleFactor(const uintptr_t parentWindowHandle) return [NSScreen mainScreen].backingScaleFactor; } END_NAMESPACE_DISTRHO -#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#include "../dgl/Base.hpp" -#define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE -#define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE) +#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#define PuglCairoView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, CairoView) -#define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, OpenGLView) -#define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, StubView) -#define PuglVulkanView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, VulkanView) -#define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, Window) -#define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate) -#define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView) +# include "../dgl/Base.hpp" +# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE +# define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE) +# define PuglCairoView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, CairoView) +# define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, OpenGLView) +# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, StubView) +# define PuglVulkanView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, VulkanView) +# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, Window) +# define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate) +# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView) +# import "src/pugl.mm" -#import "src/pugl.mm" #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp new file mode 100644 index 00000000..01e57b9c --- /dev/null +++ b/distrho/extra/FileBrowserDialog.cpp @@ -0,0 +1,569 @@ +/* + * 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 "FileBrowserDialog.hpp" +#include "ScopedPointer.hpp" +#include "String.hpp" + +#ifdef DISTRHO_OS_MAC +# import +#endif +#ifdef DISTRHO_OS_WINDOWS +# include +# include +# include +# include +# include +#else +# include +#endif +#ifdef HAVE_DBUS +# include +#endif +#ifdef HAVE_X11 +# define DBLCLKTME 400 +# include "sofd/libsofd.h" +# include "sofd/libsofd.c" +#endif + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +// static pointer used for signal null/none action taken +static const char* const kSelectedFileCancelled = "__dpf_cancelled__"; + +struct FileBrowserData { + const char* selectedFile; + +#ifdef DISTRHO_OS_MAC + NSSavePanel* nsBasePanel; + NSOpenPanel* nsOpenPanel; +#endif +#ifdef HAVE_DBUS + DBusConnection* dbuscon; +#endif +#ifdef HAVE_X11 + Display* x11display; +#endif + +#ifdef DISTRHO_OS_WINDOWS + OPENFILENAMEW ofn; + volatile bool threadCancelled; + uintptr_t threadHandle; + std::vector fileNameW; + std::vector startDirW; + std::vector titleW; + const bool saving; + bool isEmbed; + + FileBrowserData(const bool save) + : selectedFile(nullptr), + threadCancelled(false), + threadHandle(0), + fileNameW(32768), + saving(save), + isEmbed(false) + { + std::memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.lpstrFile = fileNameW.data(); + ofn.nMaxFile = (DWORD)fileNameW.size(); + } + + ~FileBrowserData() + { + if (cancelAndStop() && selectedFile != nullptr && selectedFile != kSelectedFileCancelled) + std::free(const_cast(selectedFile)); + } + + void setupAndStart(const bool embed, + const char* const startDir, + const char* const windowTitle, + const uintptr_t winId, + const FileBrowserOptions options) + { + isEmbed = embed; + + ofn.hwndOwner = (HWND)winId; + + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; + if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) + ofn.Flags |= OFN_FORCESHOWHIDDEN; + + ofn.FlagsEx = 0x0; + if (options.buttons.showPlaces == FileBrowserOptions::kButtonInvisible) + ofn.FlagsEx |= OFN_EX_NOPLACESBAR; + + startDirW.resize(std::strlen(startDir) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, startDir, -1, startDirW.data(), static_cast(startDirW.size()))) + ofn.lpstrInitialDir = startDirW.data(); + + titleW.resize(std::strlen(windowTitle) + 1); + if (MultiByteToWideChar(CP_UTF8, 0, windowTitle, -1, titleW.data(), static_cast(titleW.size()))) + ofn.lpstrTitle = titleW.data(); + + uint threadId; + threadCancelled = false; + threadHandle = _beginthreadex(nullptr, 0, _run, this, 0, &threadId); + } + + bool cancelAndStop() + { + threadCancelled = true; + + if (threadHandle == 0) + return true; + + // if previous dialog running, carefully close its window + const HWND owner = isEmbed ? GetParent(ofn.hwndOwner) : ofn.hwndOwner; + + if (owner != nullptr && owner != INVALID_HANDLE_VALUE) + { + const HWND window = GetWindow(owner, GW_HWNDFIRST); + + if (window != nullptr && window != INVALID_HANDLE_VALUE) + { + SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0); + SendMessage(window, WM_CLOSE, 0, 0); + WaitForSingleObject((HANDLE)threadHandle, 5000); + } + } + + if (threadHandle == 0) + return true; + + // not good if thread still running, but let's close the handle anyway + CloseHandle((HANDLE)threadHandle); + threadHandle = 0; + return false; + } + + void run() + { + const char* nextFile = nullptr; + + if (saving ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) + { + if (threadCancelled) + { + threadHandle = 0; + return; + } + + // back to UTF-8 + std::vector fileNameA(4 * 32768); + if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, + fileNameA.data(), (int)fileNameA.size(), + nullptr, nullptr)) + { + nextFile = strdup(fileNameA.data()); + } + } + + if (threadCancelled) + { + threadHandle = 0; + return; + } + + if (nextFile == nullptr) + nextFile = kSelectedFileCancelled; + + selectedFile = nextFile; + threadHandle = 0; + } + + static unsigned __stdcall _run(void* const arg) + { + // CoInitializeEx(nullptr, COINIT_MULTITHREADED); + static_cast(arg)->run(); + // CoUninitialize(); + _endthreadex(0); + return 0; + } +#else // DISTRHO_OS_WINDOWS + FileBrowserData(const bool saving) + : selectedFile(nullptr) + { +#ifdef DISTRHO_OS_MAC + if (saving) + { + nsOpenPanel = nullptr; + nsBasePanel = [[NSSavePanel savePanel]retain]; + } + else + { + nsOpenPanel = [[NSOpenPanel openPanel]retain]; + nsBasePanel = nsOpenPanel; + } +#endif +#ifdef HAVE_DBUS + if ((dbuscon = dbus_bus_get(DBUS_BUS_SESSION, nullptr)) != nullptr) + dbus_connection_set_exit_on_disconnect(dbuscon, false); +#endif +#ifdef HAVE_X11 + x11display = XOpenDisplay(nullptr); +#endif + + // maybe unused + return; (void)saving; + } + + ~FileBrowserData() + { +#ifdef DISTRHO_OS_MAC + [nsBasePanel release]; +#endif +#ifdef HAVE_DBUS + if (dbuscon != nullptr) + dbus_connection_unref(dbuscon); +#endif +#ifdef HAVE_X11 + if (x11display != nullptr) + XCloseDisplay(x11display); +#endif + + if (selectedFile != nullptr && selectedFile != kSelectedFileCancelled) + std::free(const_cast(selectedFile)); + } +#endif +}; + +// -------------------------------------------------------------------------------------------------------------------- + +#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE +namespace DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE { +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +FileBrowserHandle fileBrowserCreate(const bool isEmbed, + const uintptr_t windowId, + const double scaleFactor, + const FileBrowserOptions& options) +{ + String startDir(options.startDir); + + if (startDir.isEmpty()) + { +#ifdef DISTRHO_OS_WINDOWS + if (char* const cwd = _getcwd(nullptr, 0)) + { + startDir = cwd; + std::free(cwd); + } +#else + if (char* const cwd = getcwd(nullptr, 0)) + { + startDir = cwd; + std::free(cwd); + } +#endif + } + + DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), nullptr); + + if (! startDir.endsWith(DISTRHO_OS_SEP)) + startDir += DISTRHO_OS_SEP_STR; + + String windowTitle(options.title); + + if (windowTitle.isEmpty()) + windowTitle = "FileBrowser"; + + ScopedPointer handle(new FileBrowserData(options.saving)); + +#ifdef DISTRHO_OS_MAC + NSSavePanel* const nsBasePanel = handle->nsBasePanel; + DISTRHO_SAFE_ASSERT_RETURN(nsBasePanel != nullptr, nullptr); + + if (! options.saving) + { + NSOpenPanel* const nsOpenPanel = handle->nsOpenPanel; + DISTRHO_SAFE_ASSERT_RETURN(nsOpenPanel != nullptr, nullptr); + + [nsOpenPanel setAllowsMultipleSelection:NO]; + [nsOpenPanel setCanChooseDirectories:NO]; + [nsOpenPanel setCanChooseFiles:YES]; + } + + [nsBasePanel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:startDir]]]; + + // TODO file filter using allowedContentTypes: [UTType] + + if (options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked) + [nsBasePanel setAllowsOtherFileTypes:YES]; + if (options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked) + [nsBasePanel setShowsHiddenFiles:YES]; + + NSString* const titleString = [[NSString alloc] + initWithBytes:windowTitle + length:strlen(windowTitle) + encoding:NSUTF8StringEncoding]; + [nsBasePanel setTitle:titleString]; + + FileBrowserData* const handleptr = handle.get(); + + dispatch_async(dispatch_get_main_queue(), ^ + { + [nsBasePanel beginSheetModalForWindow:[(NSView*)windowId window] + completionHandler:^(NSModalResponse result) + { + if (result == NSModalResponseOK && [[nsBasePanel URL] isFileURL]) + { + NSString* const path = [[nsBasePanel URL] path]; + handleptr->selectedFile = strdup([path UTF8String]); + } + else + { + handleptr->selectedFile = kSelectedFileCancelled; + } + }]; + }); +#endif + +#ifdef DISTRHO_OS_WINDOWS + handle->setupAndStart(isEmbed, startDir, windowTitle, windowId, options); +#endif + +#ifdef HAVE_DBUS + // optional, can be null + DBusConnection* const dbuscon = handle->dbuscon; + + if (dbuscon != nullptr && dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr)) + { + // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.FileChooser + if (DBusMessage* const message = dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.FileChooser", + options.saving ? "SaveFile" : "OpenFile")) + { + char windowIdStr[32]; + memset(windowIdStr, 0, sizeof(windowIdStr)); +# ifdef HAVE_X11 + snprintf(windowIdStr, sizeof(windowIdStr)-1, "x11:%llx", (ulonglong)windowId); +# endif + const char* windowIdStrPtr = windowIdStr; + + dbus_message_append_args(message, + DBUS_TYPE_STRING, &windowIdStrPtr, + DBUS_TYPE_STRING, &windowTitle, + DBUS_TYPE_INVALID); + + DBusMessageIter iter, array; + dbus_message_iter_init_append(message, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); + + /* here merely as example, in case we need to configure it + { + DBusMessageIter dict, variant; + const char* const property = "property"; + const char* const value = "value"; + + dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict); + dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property); + dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &value); + dbus_message_iter_close_container(&dict, &variant); + dbus_message_iter_close_container(&array, &dict); + } + */ + + dbus_message_iter_close_container(&iter, &array); + + dbus_connection_send(dbuscon, message, nullptr); + + dbus_message_unref(message); + return handle.release(); + } + } +#endif + +#ifdef HAVE_X11 + Display* const x11display = handle->x11display; + DISTRHO_SAFE_ASSERT_RETURN(x11display != nullptr, nullptr); + + // unsupported at the moment + if (options.saving) + return nullptr; + + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, windowTitle) == 0, nullptr); + + const int button1 = options.buttons.showHidden == FileBrowserOptions::kButtonVisibleChecked ? 1 + : options.buttons.showHidden == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; + const int button2 = options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleChecked ? 1 + : options.buttons.showPlaces == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; + const int button3 = options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleChecked ? 1 + : options.buttons.listAllFiles == FileBrowserOptions::kButtonVisibleUnchecked ? 0 : -1; + + x_fib_cfg_buttons(1, button1); + x_fib_cfg_buttons(2, button2); + x_fib_cfg_buttons(3, button3); + + if (x_fib_show(x11display, windowId, 0, 0, scaleFactor + 0.5) != 0) + return nullptr; +#endif + + return handle.release(); + + // might be unused + (void)isEmbed; + (void)windowId; + (void)scaleFactor; +} + +// -------------------------------------------------------------------------------------------------------------------- +// returns true if dialog was closed (with or without a file selection) + +bool fileBrowserIdle(const FileBrowserHandle handle) +{ +#ifdef HAVE_DBUS + if (DBusConnection* dbuscon = handle->dbuscon) + { + while (dbus_connection_dispatch(dbuscon) == DBUS_DISPATCH_DATA_REMAINS) {} + dbus_connection_read_write_dispatch(dbuscon, 0); + + if (DBusMessage* const message = dbus_connection_pop_message(dbuscon)) + { + const char* const interface = dbus_message_get_interface(message); + const char* const member = dbus_message_get_member(message); + + if (interface != nullptr && std::strcmp(interface, "org.freedesktop.portal.Request") == 0 + && member != nullptr && std::strcmp(member, "Response") == 0) + { + do { + DBusMessageIter iter; + dbus_message_iter_init(message, &iter); + + // starts with uint32 for return/exit code + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32); + + uint32_t ret = 1; + dbus_message_iter_get_basic(&iter, &ret); + + if (ret != 0) + break; + + // next must be array + dbus_message_iter_next(&iter); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY); + + // open dict array + DBusMessageIter dictArray; + dbus_message_iter_recurse(&iter, &dictArray); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); + + // open containing dict + DBusMessageIter dict; + dbus_message_iter_recurse(&dictArray, &dict); + + // start with the string "uris" + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); + + const char* key = nullptr; + dbus_message_iter_get_basic(&dict, &key); + DISTRHO_SAFE_ASSERT_BREAK(key != nullptr && std::strcmp(key, "uris") == 0); + + // then comes variant + dbus_message_iter_next(&dict); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_VARIANT); + + DBusMessageIter variant; + dbus_message_iter_recurse(&dict, &variant); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_ARRAY); + + // open variant array (variant type is string) + DBusMessageIter variantArray; + dbus_message_iter_recurse(&variant, &variantArray); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&variantArray) == DBUS_TYPE_STRING); + + const char* value = nullptr; + dbus_message_iter_get_basic(&variantArray, &value); + + // and finally we have our dear value, just make sure it is local + DISTRHO_SAFE_ASSERT_BREAK(value != nullptr); + + if (const char* const localvalue = std::strstr(value, "file:///")) + handle->selectedFile = strdup(localvalue + 7); + + } while(false); + + if (handle->selectedFile == nullptr) + handle->selectedFile = kSelectedFileCancelled; + } + } + } +#endif + +#ifdef HAVE_X11 + Display* const x11display = handle->x11display; + + if (x11display == nullptr) + return false; + + XEvent event; + while (XPending(x11display) > 0) + { + XNextEvent(x11display, &event); + + if (x_fib_handle_events(x11display, &event) == 0) + continue; + + if (x_fib_status() > 0) + handle->selectedFile = x_fib_filename(); + else + handle->selectedFile = kSelectedFileCancelled; + + x_fib_close(x11display); + XCloseDisplay(x11display); + handle->x11display = nullptr; + break; + } +#endif + + return handle->selectedFile != nullptr; +} + +// -------------------------------------------------------------------------------------------------------------------- +// close sofd file dialog + +void fileBrowserClose(const FileBrowserHandle handle) +{ +#ifdef HAVE_X11 + if (Display* const x11display = handle->x11display) + x_fib_close(x11display); +#endif + + delete handle; +} + +// -------------------------------------------------------------------------------------------------------------------- +// get path chosen via sofd file dialog + +const char* fileBrowserGetPath(const FileBrowserHandle handle) +{ + return handle->selectedFile != kSelectedFileCancelled ? handle->selectedFile : nullptr; +} + +// -------------------------------------------------------------------------------------------------------------------- + +#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE +} +#endif + +END_NAMESPACE_DISTRHO diff --git a/distrho/extra/FileBrowserDialog.hpp b/distrho/extra/FileBrowserDialog.hpp new file mode 100644 index 00000000..2074e4cb --- /dev/null +++ b/distrho/extra/FileBrowserDialog.hpp @@ -0,0 +1,134 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED +#define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- +// File Browser Dialog stuff + +struct FileBrowserData; +typedef FileBrowserData* FileBrowserHandle; + +// -------------------------------------------------------------------------------------------------------------------- + +/** + File browser options, for customizing the file browser dialog.@n + By default the file browser dialog will be work as "open file" in the current working directory. +*/ +struct FileBrowserOptions { + /** Whether we are saving, opening files otherwise (default) */ + bool saving; + + /** Start directory, uses current working directory if null */ + const char* startDir; + + /** File browser dialog window title, uses "FileBrowser" if null */ + const char* title; + + // TODO file filter + + /** + File browser button state. + This allows to customize the behaviour of the file browse dialog buttons. + Note these are merely hints, not all systems support them. + */ + enum ButtonState { + kButtonInvisible, + kButtonVisibleUnchecked, + kButtonVisibleChecked, + }; + + /** + File browser buttons. + */ + struct Buttons { + /** Whether to list all files vs only those with matching file extension */ + ButtonState listAllFiles; + /** Whether to show hidden files */ + ButtonState showHidden; + /** Whether to show list of places (bookmarks) */ + ButtonState showPlaces; + + /** Constructor for default values */ + Buttons() + : listAllFiles(kButtonVisibleChecked), + showHidden(kButtonVisibleUnchecked), + showPlaces(kButtonVisibleChecked) {} + } buttons; + + /** Constructor for default values */ + FileBrowserOptions() + : saving(false), + startDir(nullptr), + title(nullptr), + buttons() {} +}; + +// -------------------------------------------------------------------------------------------------------------------- + +#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE +namespace DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE { +#endif + +/** + Create a new file browser dialog. + + @p isEmbed: Whether the window this dialog belongs to is an embed/child window (needed to close dialog on Windows) + @p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*) + @p scaleFactor: Scale factor to use (only used on X11) + @p options: Extra options, optional + By default the file browser dialog will be work as "open file" in the current working directory. +*/ +FileBrowserHandle fileBrowserCreate(bool isEmbed, + uintptr_t windowId, + double scaleFactor, + const FileBrowserOptions& options = FileBrowserOptions()); + +/** + Idle the file browser dialog handle.@n + Returns true if dialog was closed (with or without a file selection), + in which case the handle must not be used afterwards. + You can then call fileBrowserGetPath to know the selected file (or null if cancelled). +*/ +bool fileBrowserIdle(const FileBrowserHandle handle); + +/** + Close the file browser dialog, handle must not be used afterwards. +*/ +void fileBrowserClose(const FileBrowserHandle handle); + +/** + Get the path chosen by the user or null.@n + Should only be called after fileBrowserIdle returns true. +*/ +const char* fileBrowserGetPath(const FileBrowserHandle handle); + +// -------------------------------------------------------------------------------------------------------------------- + +#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED diff --git a/dgl/src/sofd/libsofd.c b/distrho/extra/sofd/libsofd.c similarity index 100% rename from dgl/src/sofd/libsofd.c rename to distrho/extra/sofd/libsofd.c diff --git a/dgl/src/sofd/libsofd.h b/distrho/extra/sofd/libsofd.h similarity index 100% rename from dgl/src/sofd/libsofd.h rename to distrho/extra/sofd/libsofd.h diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index 281031c2..b3d2de6b 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -207,6 +207,7 @@ typedef unsigned char uchar; typedef unsigned short int ushort; typedef unsigned int uint; typedef unsigned long int ulong; +typedef unsigned long long int ulonglong; /* Deprecated macros */ #define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) DISTRHO_DECLARE_NON_COPYABLE(ClassName) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index c4797e53..99e322e4 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -15,6 +15,29 @@ */ #include "src/DistrhoPluginChecks.h" +#include "src/DistrhoDefines.h" + +#if !defined(DGL_FILE_BROWSER_DISABLED) && !defined(DISTRHO_OS_MAC) +# define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION +# define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) +# define DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE Plugin +# define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_add_recent) +# define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_buttons) +# define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_filter_callback) +# define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_close) +# define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_configure) +# define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_filename) +# define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_free_recent) +# define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_handle_events) +# define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_load_recent) +# define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_at) +# define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_count) +# define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_file) +# define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_save_recent) +# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_show) +# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_status) +# include "../extra/FileBrowserDialog.cpp" +#endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # if defined(DISTRHO_OS_WINDOWS) @@ -255,6 +278,19 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) } #endif +#ifndef DGL_FILE_BROWSER_DISABLED +bool UI::openFileBrowser(const FileBrowserOptions& options) +{ +# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + // TODO + return false; + (void)options; +# else + return getWindow().openFileBrowser(options); +# endif +} +#endif + #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS /* ------------------------------------------------------------------------------------------------------------ * Direct DSP access */ @@ -311,13 +347,13 @@ void UI::uiReshape(uint, uint) // NOTE this must be the same as Window::onReshape pData->fallbackOnResize(); } +#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# ifndef DGL_FILE_BROWSER_DISABLED +#ifndef DGL_FILE_BROWSER_DISABLED void UI::uiFileBrowserSelected(const char*) { } -# endif -#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +#endif /* ------------------------------------------------------------------------------------------------------------ * UI Resize Handling, internal */ diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 0b03160a..6d837cb1 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -433,7 +433,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key); title[sizeof(title)-1u] = '\0'; - DGL_NAMESPACE::Window::FileBrowserOptions opts; + FileBrowserOptions opts; opts.title = title; return window->openFileBrowser(opts); #endif diff --git a/tests/FileBrowserDialog.cpp b/tests/FileBrowserDialog.cpp index 3733c385..bc4ece4c 100644 --- a/tests/FileBrowserDialog.cpp +++ b/tests/FileBrowserDialog.cpp @@ -125,6 +125,7 @@ protected: repaint(); FileBrowserOptions opts; + // opts.saving = true; opts.title = "Look at me"; if (! openFileBrowser(opts)) { From c6d942049090d7623bb2eaa6e406136d52e6d0df Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 12 Nov 2021 16:18:10 +0000 Subject: [PATCH 237/504] Include commdlg.h, needed if others define WIN32_LEAN_AND_MEAN --- distrho/extra/FileBrowserDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index 01e57b9c..a9bfd1d3 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -22,6 +22,7 @@ # import #endif #ifdef DISTRHO_OS_WINDOWS +# include # include # include # include From d290ac2fe07117e74cea1c50a95c21dc1e039b47 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 12 Nov 2021 16:34:34 +0000 Subject: [PATCH 238/504] Fix DBus file dialog for Gtk/GNOME Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index a9bfd1d3..b04578ab 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -473,12 +473,29 @@ bool fileBrowserIdle(const FileBrowserHandle handle) DBusMessageIter dict; dbus_message_iter_recurse(&dictArray, &dict); - // start with the string "uris" + // look for dict with string "uris" DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); const char* key = nullptr; dbus_message_iter_get_basic(&dict, &key); - DISTRHO_SAFE_ASSERT_BREAK(key != nullptr && std::strcmp(key, "uris") == 0); + DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); + + // keep going until we find it + while (std::strcmp(key, "uris") != 0) + { + key = nullptr; + dbus_message_iter_next(&dictArray); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dictArray) == DBUS_TYPE_DICT_ENTRY); + + dbus_message_iter_recurse(&dictArray, &dict); + DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING); + + dbus_message_iter_get_basic(&dict, &key); + DISTRHO_SAFE_ASSERT_BREAK(key != nullptr); + } + + if (key == nullptr) + break; // then comes variant dbus_message_iter_next(&dict); From 4e0b1005324ab383b7084a295ec4c87014ac9d37 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 12 Nov 2021 16:37:55 +0000 Subject: [PATCH 239/504] Header includes are a tricky business.. Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index b04578ab..c3a46ed1 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -22,11 +22,11 @@ # import #endif #ifdef DISTRHO_OS_WINDOWS -# include # include # include # include # include +# include # include #else # include From 315c10e1bc80fd43ee32131fe7180bba811604dc Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 12 Nov 2021 17:00:58 +0000 Subject: [PATCH 240/504] Add support for start-dir on dbus file browser saving Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index c3a46ed1..a512f72a 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -369,20 +369,21 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, dbus_message_iter_init_append(message, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); - /* here merely as example, in case we need to configure it { - DBusMessageIter dict, variant; - const char* const property = "property"; - const char* const value = "value"; + DBusMessageIter dict, variant, variantArray; + const char* const current_folder_key = "current_folder"; + const char* const current_folder_val = startDir.buffer(); dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict); - dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant); - dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &value); + dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, ¤t_folder_key); + dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "ay", &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &variantArray); + dbus_message_iter_append_fixed_array(&variantArray, DBUS_TYPE_BYTE, + ¤t_folder_val, startDir.length()+1); + dbus_message_iter_close_container(&variant, &variantArray); dbus_message_iter_close_container(&dict, &variant); dbus_message_iter_close_container(&array, &dict); } - */ dbus_message_iter_close_container(&iter, &array); From 6b836375c937c96078daf67ca26417bd3c96e26f Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 12 Nov 2021 17:03:23 +0000 Subject: [PATCH 241/504] Try another way for finicky windows headers Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 1 - distrho/src/DistrhoUI.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index a512f72a..4c8d1c29 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -26,7 +26,6 @@ # include # include # include -# include # include #else # include diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 99e322e4..ee62ee68 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -41,7 +41,7 @@ #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # if defined(DISTRHO_OS_WINDOWS) -# define WIN32_LEAN_AND_MEAN +# include # include # elif defined(HAVE_X11) # include From 4c62f76915478d7cf6ac89cbc1c9714d1fbeb044 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 12 Nov 2021 18:01:53 +0000 Subject: [PATCH 242/504] Re-add commdlg header, this seems to work now.. Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index 4c8d1c29..a512f72a 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -26,6 +26,7 @@ # include # include # include +# include # include #else # include From 14423c5eec85b7ef1b5f997a9e27bfc48487c24f Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 22 Nov 2021 12:06:46 +0000 Subject: [PATCH 243/504] Automable is not a word, sorry! Signed-off-by: falkTX --- distrho/DistrhoInfo.hpp | 4 ++-- distrho/DistrhoPlugin.hpp | 12 ++++++++---- distrho/src/DistrhoPluginCarla.cpp | 2 +- distrho/src/DistrhoPluginLV2export.cpp | 4 ++-- distrho/src/DistrhoPluginVST2.cpp | 4 ++-- distrho/src/DistrhoPluginVST3.cpp | 2 +- distrho/src/lv2/lv2_kxstudio_properties.h | 4 ++-- examples/CVPort/ExamplePluginCVPort.cpp | 2 +- .../EmbedExternalUI/EmbedExternalExamplePlugin.cpp | 6 +++--- examples/ExternalUI/ExternalExamplePlugin.cpp | 4 ++-- examples/ImGuiSimpleGain/PluginSimpleGain.cpp | 2 +- examples/Info/InfoExamplePlugin.cpp | 4 ++-- examples/Latency/LatencyExamplePlugin.cpp | 4 ++-- examples/Meters/ExamplePluginMeters.cpp | 6 +++--- examples/Metronome/ExamplePluginMetronome.cpp | 2 +- examples/Parameters/ExamplePluginParameters.cpp | 4 ++-- 16 files changed, 35 insertions(+), 31 deletions(-) diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index e58f25cb..5eaf6879 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -221,7 +221,7 @@ START_NAMESPACE_DISTRHO { // we only have one parameter so we can skip checking the index - parameter.hints = kParameterIsAutomable; + parameter.hints = kParameterIsAutomatable; parameter.name = "Gain"; parameter.symbol = "gain"; parameter.ranges.min = 0.0f; @@ -331,7 +331,7 @@ START_NAMESPACE_DISTRHO */ void initParameter(uint32_t index, Parameter& parameter) override { - parameter.hints = kParameterIsAutomable; + parameter.hints = kParameterIsAutomatable; parameter.ranges.min = 0.0f; parameter.ranges.max = 2.0f; parameter.ranges.def = 1.0f; diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 50dad44f..80de8c8c 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -86,10 +86,14 @@ static const uint32_t kCVPortHasScaledRange = 0x80; */ /** - Parameter is automable (real-time safe). + Parameter is automatable (real-time safe). @see Plugin::setParameterValue(uint32_t, float) */ -static const uint32_t kParameterIsAutomable = 0x01; +static const uint32_t kParameterIsAutomatable = 0x01; + +/** It was a typo, sorry.. */ +DISTRHO_DEPRECATED_BY("kParameterIsAutomatable") +static const uint32_t kParameterIsAutomable = kParameterIsAutomatable; /** Parameter value is boolean.@n @@ -563,7 +567,7 @@ struct Parameter { case kParameterDesignationNull: break; case kParameterDesignationBypass: - hints = kParameterIsAutomable|kParameterIsBoolean|kParameterIsInteger; + hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger; name = "Bypass"; shortName = "Bypass"; symbol = "dpf_bypass"; @@ -1010,7 +1014,7 @@ protected: /** Change a parameter value.@n The host may call this function from any context, including realtime processing.@n - When a parameter is marked as automable, you must ensure no non-realtime operations are performed. + When a parameter is marked as automatable, you must ensure no non-realtime operations are performed. @note This function will only be called for parameter inputs. */ virtual void setParameterValue(uint32_t index, float value); diff --git a/distrho/src/DistrhoPluginCarla.cpp b/distrho/src/DistrhoPluginCarla.cpp index bc1bd00c..5f375f03 100644 --- a/distrho/src/DistrhoPluginCarla.cpp +++ b/distrho/src/DistrhoPluginCarla.cpp @@ -238,7 +238,7 @@ protected: int nativeParamHints = ::NATIVE_PARAMETER_IS_ENABLED; const uint32_t paramHints = fPlugin.getParameterHints(index); - if (paramHints & kParameterIsAutomable) + if (paramHints & kParameterIsAutomatable) nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMABLE; if (paramHints & kParameterIsBoolean) nativeParamHints |= ::NATIVE_PARAMETER_IS_BOOLEAN; diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 712d722a..a857140c 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -851,10 +851,10 @@ void lv2_generate_ttl(const char* const basename) pluginString += " lv2:portProperty lv2:integer ;\n"; if (hints & kParameterIsLogarithmic) pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; - if ((hints & kParameterIsAutomable) == 0 && plugin.isParameterInput(i)) + if ((hints & kParameterIsAutomatable) == 0 && plugin.isParameterInput(i)) { pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; - pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; + pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomatable "> ;\n"; } // group diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index d285dacf..6d55f4ff 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -992,8 +992,8 @@ public: { const uint32_t hints(fPlugin.getParameterHints(index)); - // must be automable, and not output - if ((hints & kParameterIsAutomable) != 0 && (hints & kParameterIsOutput) == 0) + // must be automatable, and not output + if ((hints & kParameterIsAutomatable) != 0 && (hints & kParameterIsOutput) == 0) return 1; } break; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index db62be54..f6c7958e 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1458,7 +1458,7 @@ public: break; } - if (hints & kParameterIsAutomable) + if (hints & kParameterIsAutomatable) flags |= V3_PARAM_CAN_AUTOMATE; if (hints & kParameterIsOutput) flags |= V3_PARAM_READ_ONLY; diff --git a/distrho/src/lv2/lv2_kxstudio_properties.h b/distrho/src/lv2/lv2_kxstudio_properties.h index e8b42a6c..22922650 100644 --- a/distrho/src/lv2/lv2_kxstudio_properties.h +++ b/distrho/src/lv2/lv2_kxstudio_properties.h @@ -1,6 +1,6 @@ /* LV2 KXStudio Properties Extension - Copyright 2014 Filipe Coelho + Copyright 2014-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 @@ -26,7 +26,7 @@ #define LV2_KXSTUDIO_PROPERTIES_URI "http://kxstudio.sf.net/ns/lv2ext/props" #define LV2_KXSTUDIO_PROPERTIES_PREFIX LV2_KXSTUDIO_PROPERTIES_URI "#" -#define LV2_KXSTUDIO_PROPERTIES__NonAutomable LV2_KXSTUDIO_PROPERTIES_PREFIX "NonAutomable" +#define LV2_KXSTUDIO_PROPERTIES__NonAutomatable LV2_KXSTUDIO_PROPERTIES_PREFIX "NonAutomatable" #define LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat LV2_KXSTUDIO_PROPERTIES_PREFIX "TimePositionTicksPerBeat" #define LV2_KXSTUDIO_PROPERTIES__TransientWindowId LV2_KXSTUDIO_PROPERTIES_PREFIX "TransientWindowId" diff --git a/examples/CVPort/ExamplePluginCVPort.cpp b/examples/CVPort/ExamplePluginCVPort.cpp index 4fb78c88..5b77c125 100644 --- a/examples/CVPort/ExamplePluginCVPort.cpp +++ b/examples/CVPort/ExamplePluginCVPort.cpp @@ -159,7 +159,7 @@ protected: parameter.name = "Hold Time"; parameter.symbol = "hold_time"; - parameter.hints = kParameterIsAutomable|kParameterIsLogarithmic; + parameter.hints = kParameterIsAutomatable|kParameterIsLogarithmic; parameter.ranges.min = 0.0f; parameter.ranges.max = kMaxHoldTime; parameter.ranges.def = 0.1f; diff --git a/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp b/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp index a88b3d9e..0eb65941 100644 --- a/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp +++ b/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp @@ -108,7 +108,7 @@ protected: switch (index) { case kParameterWidth: - parameter.hints = kParameterIsAutomable|kParameterIsInteger; + parameter.hints = kParameterIsAutomatable|kParameterIsInteger; parameter.ranges.def = 512.0f; parameter.ranges.min = 256.0f; parameter.ranges.max = 4096.0f; @@ -117,7 +117,7 @@ protected: parameter.unit = "px"; break; case kParameterHeight: - parameter.hints = kParameterIsAutomable|kParameterIsInteger; + parameter.hints = kParameterIsAutomatable|kParameterIsInteger; parameter.ranges.def = 256.0f; parameter.ranges.min = 256.0f; parameter.ranges.max = 4096.0f; @@ -152,7 +152,7 @@ protected: /** Change a parameter value. The host may call this function from any context, including realtime processing. - When a parameter is marked as automable, you must ensure no non-realtime operations are performed. + When a parameter is marked as automatable, you must ensure no non-realtime operations are performed. @note This function will only be called for parameter inputs. */ void setParameterValue(uint32_t index, float value) override diff --git a/examples/ExternalUI/ExternalExamplePlugin.cpp b/examples/ExternalUI/ExternalExamplePlugin.cpp index 92b1b12e..08d40e27 100644 --- a/examples/ExternalUI/ExternalExamplePlugin.cpp +++ b/examples/ExternalUI/ExternalExamplePlugin.cpp @@ -107,7 +107,7 @@ protected: if (index != 0) return; - parameter.hints = kParameterIsAutomable|kParameterIsInteger; + parameter.hints = kParameterIsAutomatable|kParameterIsInteger; parameter.ranges.def = 0.0f; parameter.ranges.min = 0.0f; parameter.ranges.max = 100.0f; @@ -134,7 +134,7 @@ protected: /** Change a parameter value. The host may call this function from any context, including realtime processing. - When a parameter is marked as automable, you must ensure no non-realtime operations are performed. + When a parameter is marked as automatable, you must ensure no non-realtime operations are performed. @note This function will only be called for parameter inputs. */ void setParameterValue(uint32_t index, float value) override diff --git a/examples/ImGuiSimpleGain/PluginSimpleGain.cpp b/examples/ImGuiSimpleGain/PluginSimpleGain.cpp index 5cb19c3c..b3e9fe95 100644 --- a/examples/ImGuiSimpleGain/PluginSimpleGain.cpp +++ b/examples/ImGuiSimpleGain/PluginSimpleGain.cpp @@ -49,7 +49,7 @@ void PluginSimpleGain::initParameter(uint32_t index, Parameter& parameter) parameter.ranges.min = -90.0f; parameter.ranges.max = 30.0f; parameter.ranges.def = -0.0f; - parameter.hints = kParameterIsAutomable; + parameter.hints = kParameterIsAutomatable; parameter.name = "Gain"; parameter.shortName = "Gain"; parameter.symbol = "gain"; diff --git a/examples/Info/InfoExamplePlugin.cpp b/examples/Info/InfoExamplePlugin.cpp index 68d42045..6e339c25 100644 --- a/examples/Info/InfoExamplePlugin.cpp +++ b/examples/Info/InfoExamplePlugin.cpp @@ -109,7 +109,7 @@ protected: */ void initParameter(uint32_t index, Parameter& parameter) override { - parameter.hints = kParameterIsAutomable|kParameterIsOutput; + parameter.hints = kParameterIsAutomatable|kParameterIsOutput; parameter.ranges.def = 0.0f; parameter.ranges.min = 0.0f; parameter.ranges.max = 16777216.0f; @@ -193,7 +193,7 @@ protected: /** Change a parameter value. The host may call this function from any context, including realtime processing. - When a parameter is marked as automable, you must ensure no non-realtime operations are performed. + When a parameter is marked as automatable, you must ensure no non-realtime operations are performed. @note This function will only be called for parameter inputs. */ void setParameterValue(uint32_t, float) override diff --git a/examples/Latency/LatencyExamplePlugin.cpp b/examples/Latency/LatencyExamplePlugin.cpp index bab5df5b..2d264a34 100644 --- a/examples/Latency/LatencyExamplePlugin.cpp +++ b/examples/Latency/LatencyExamplePlugin.cpp @@ -117,7 +117,7 @@ protected: if (index != 0) return; - parameter.hints = kParameterIsAutomable; + parameter.hints = kParameterIsAutomatable; parameter.name = "Latency"; parameter.symbol = "latency"; parameter.unit = "s"; @@ -144,7 +144,7 @@ protected: /** Change a parameter value. The host may call this function from any context, including realtime processing. - When a parameter is marked as automable, you must ensure no non-realtime operations are performed. + When a parameter is marked as automatable, you must ensure no non-realtime operations are performed. @note This function will only be called for parameter inputs. */ void setParameterValue(uint32_t index, float value) override diff --git a/examples/Meters/ExamplePluginMeters.cpp b/examples/Meters/ExamplePluginMeters.cpp index 47ddd3c0..33483195 100644 --- a/examples/Meters/ExamplePluginMeters.cpp +++ b/examples/Meters/ExamplePluginMeters.cpp @@ -120,7 +120,7 @@ protected: switch (index) { case 0: - parameter.hints = kParameterIsAutomable|kParameterIsInteger; + parameter.hints = kParameterIsAutomatable|kParameterIsInteger; parameter.name = "color"; parameter.symbol = "color"; parameter.enumValues.count = 2; @@ -136,12 +136,12 @@ protected: } break; case 1: - parameter.hints = kParameterIsAutomable|kParameterIsOutput; + parameter.hints = kParameterIsAutomatable|kParameterIsOutput; parameter.name = "out-left"; parameter.symbol = "out_left"; break; case 2: - parameter.hints = kParameterIsAutomable|kParameterIsOutput; + parameter.hints = kParameterIsAutomatable|kParameterIsOutput; parameter.name = "out-right"; parameter.symbol = "out_right"; break; diff --git a/examples/Metronome/ExamplePluginMetronome.cpp b/examples/Metronome/ExamplePluginMetronome.cpp index 586e4b35..537e994b 100644 --- a/examples/Metronome/ExamplePluginMetronome.cpp +++ b/examples/Metronome/ExamplePluginMetronome.cpp @@ -156,7 +156,7 @@ protected: */ void initParameter(uint32_t index, Parameter& parameter) override { - parameter.hints = kParameterIsAutomable; + parameter.hints = kParameterIsAutomatable; switch (index) { diff --git a/examples/Parameters/ExamplePluginParameters.cpp b/examples/Parameters/ExamplePluginParameters.cpp index b142048a..eb466b49 100644 --- a/examples/Parameters/ExamplePluginParameters.cpp +++ b/examples/Parameters/ExamplePluginParameters.cpp @@ -122,10 +122,10 @@ The plugin will be treated as an effect, but it will not change the host audio." */ /** - Changing parameters does not cause any realtime-unsafe operations, so we can mark them as automable. + Changing parameters does not cause any realtime-unsafe operations, so we can mark them as automatable. Also set as boolean because they work as on/off switches. */ - parameter.hints = kParameterIsAutomable|kParameterIsBoolean; + parameter.hints = kParameterIsAutomatable|kParameterIsBoolean; /** Minimum 0 (off), maximum 1 (on). From d1892e151f4fdeeb93f23beaddd69df6b53edeca Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 24 Nov 2021 22:46:37 +0000 Subject: [PATCH 244/504] Expose puglGetTime as Application::getTime --- dgl/Application.hpp | 9 +++++++++ dgl/src/Application.cpp | 5 +++++ dgl/src/ApplicationPrivateData.cpp | 7 +++++++ dgl/src/ApplicationPrivateData.hpp | 3 +++ 4 files changed, 24 insertions(+) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 87937cc7..1024e226 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -80,6 +80,15 @@ public: */ bool isStandalone() const noexcept; + /** + Return the time in seconds. + + This is a monotonically increasing clock with high resolution.@n + The returned time is only useful to compare against other times returned by this function, + its absolute value has no meaning. + */ + double getTime() const; + /** Add a callback function to be triggered on every idle cycle. You can add more than one, and remove them at anytime with removeIdleCallback(). diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index 12191f7a..5d6529a9 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -56,6 +56,11 @@ bool Application::isStandalone() const noexcept return pData->isStandalone; } +double Application::getTime() const +{ + return pData->getTime(); +} + void Application::addIdleCallback(IdleCallback* const callback) { DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index f573c2f7..f54535fc 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -152,6 +152,13 @@ void Application::PrivateData::quit() #endif } +double Application::PrivateData::getTime() const +{ + DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, 0.0); + + return puglGetTime(world); +} + void Application::PrivateData::setClassName(const char* const name) { DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 0d2993c4..c3030b06 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -91,6 +91,9 @@ struct Application::PrivateData { For standalone mode only. */ void quit(); + /** Get time via pugl */ + double getTime() const; + /** Set pugl world class name. */ void setClassName(const char* name); From 6053ec2a3e036865be7e25bf70920640d416dbe3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 5 Dec 2021 14:39:15 +0000 Subject: [PATCH 245/504] Assume mingw wants posix Signed-off-by: falkTX --- Makefile.base.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index b5e95ead..dc18b896 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -169,8 +169,10 @@ BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections endif ifeq ($(WINDOWS),true) +# Assume we want posix +BASE_FLAGS += -posix # Needed for windows, see https://github.com/falkTX/Carla/issues/855 -BASE_OPTS += -mstackrealign +BASE_FLAGS += -mstackrealign else # Not needed for Windows BASE_FLAGS += -fPIC -DPIC From 30d890cdb75b5805afeb5768d77b2af033a4849a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 5 Dec 2021 15:49:27 +0000 Subject: [PATCH 246/504] Use ubuntu-18.04 for win32 CI builds Signed-off-by: falkTX --- .github/workflows/example-plugins.yml | 2 +- Makefile.base.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index c8bf4505..ac270764 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -175,7 +175,7 @@ jobs: !bin/vst3 win32: - runs-on: ubuntu-20.04 + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 with: diff --git a/Makefile.base.mk b/Makefile.base.mk index dc18b896..1594a12a 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -170,7 +170,7 @@ endif ifeq ($(WINDOWS),true) # Assume we want posix -BASE_FLAGS += -posix +BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS # Needed for windows, see https://github.com/falkTX/Carla/issues/855 BASE_FLAGS += -mstackrealign else From 56c33dd1cdd66b315b9783f1f9a4e0f83d580579 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 5 Dec 2021 19:00:43 +0000 Subject: [PATCH 247/504] Fix fileBrowserGetPath under some systems Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index a512f72a..ccaa4f1b 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -576,7 +576,11 @@ void fileBrowserClose(const FileBrowserHandle handle) const char* fileBrowserGetPath(const FileBrowserHandle handle) { - return handle->selectedFile != kSelectedFileCancelled ? handle->selectedFile : nullptr; + if (const char* const selectedFile = handle->selectedFile) + if (selectedFile != kSelectedFileCancelled && std::strcmp(selectedFile, kSelectedFileCancelled) != 0) + return selectedFile; + + return nullptr; } // -------------------------------------------------------------------------------------------------------------------- From 2208680d20cca0360d38043d65f050a51d2c5a02 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 5 Dec 2021 21:37:46 +0000 Subject: [PATCH 248/504] Allow and fix LTO build Signed-off-by: falkTX --- Makefile.base.mk | 5 +++++ dgl/src/ApplicationPrivateData.hpp | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Makefile.base.mk b/Makefile.base.mk index 1594a12a..fdbeb374 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -186,6 +186,11 @@ BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden CXXFLAGS += -fvisibility-inlines-hidden endif +ifeq ($(WITH_LTO),true) +BASE_FLAGS += -fno-strict-aliasing -flto +LINK_FLAGS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch +endif + BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=gnu++11 $(CXXFLAGS) LINK_FLAGS = $(LINK_OPTS) $(LDFLAGS) diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index c3030b06..40fbc1a5 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -30,12 +30,18 @@ typedef HANDLE d_ThreadHandle; typedef pthread_t d_ThreadHandle; #endif +#ifdef DISTRHO_OS_MAC typedef struct PuglWorldImpl PuglWorld; +#endif START_NAMESPACE_DGL class Window; +#ifndef DISTRHO_OS_MAC +typedef struct PuglWorldImpl PuglWorld; +#endif + // -------------------------------------------------------------------------------------------------------------------- struct Application::PrivateData { From c6c6900b8743550da7eb4f0f61a28ee85ac80966 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 8 Dec 2021 22:26:44 +0000 Subject: [PATCH 249/504] Add steps to create a plugin as shared library The exported createSharedPlugin function will return a DPF Plugin Signed-off-by: falkTX --- Makefile.plugins.mk | 20 ++++++++++++++++++++ distrho/DistrhoPluginMain.cpp | 3 +++ distrho/DistrhoUIMain.cpp | 2 ++ utils/symbols/shared.def | 2 ++ utils/symbols/shared.exp | 1 + utils/symbols/shared.version | 4 ++++ 6 files changed, 32 insertions(+) create mode 100644 utils/symbols/shared.def create mode 100644 utils/symbols/shared.exp create mode 100644 utils/symbols/shared.version diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 3729841a..ffa3d015 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -126,6 +126,7 @@ vst2 = $(TARGET_DIR)/$(VST2_FILENAME) ifneq ($(VST3_FILENAME),) vst3 = $(TARGET_DIR)/$(VST3_FILENAME) endif +shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) ifeq ($(MACOS),true) vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist @@ -147,6 +148,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_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp else ifeq ($(WINDOWS),true) SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def @@ -155,6 +157,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_SHARED = $(DPF_PATH)/utils/symbols/shared.def else ifneq ($(DEBUG),true) SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version SYMBOLS_DSSI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/dssi.version @@ -163,6 +166,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_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version endif # --------------------------------------------------------------------------------------------------------------------- @@ -439,6 +443,20 @@ endif @echo "Creating VST3 plugin for $(NAME)" $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ +# --------------------------------------------------------------------------------------------------------------------- +# Shared + +shared: $(shared) + +ifeq ($(HAVE_DGL),true) +$(shared): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.o $(DGL_LIB) +else +$(shared): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o +endif + -@mkdir -p $(shell dirname $@) + @echo "Creating shared library for $(NAME)" + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@ + # --------------------------------------------------------------------------------------------------------------------- # macOS files @@ -467,11 +485,13 @@ 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_SHARED.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d -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_SHARED.cpp.d # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp index 0335f2c7..6affef3c 100644 --- a/distrho/DistrhoPluginMain.cpp +++ b/distrho/DistrhoPluginMain.cpp @@ -29,6 +29,9 @@ # include "src/DistrhoPluginVST2.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_VST3) # include "src/DistrhoPluginVST3.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) +DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin(); +DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); } #else # error unsupported format #endif diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index f186e6a5..aeeade58 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -28,6 +28,8 @@ // nothing #elif defined(DISTRHO_PLUGIN_TARGET_VST3) # include "src/DistrhoUIVST3.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) +// nothing #else # error unsupported format #endif diff --git a/utils/symbols/shared.def b/utils/symbols/shared.def new file mode 100644 index 00000000..1413caea --- /dev/null +++ b/utils/symbols/shared.def @@ -0,0 +1,2 @@ +EXPORTS +createSharedPlugin diff --git a/utils/symbols/shared.exp b/utils/symbols/shared.exp new file mode 100644 index 00000000..80f62510 --- /dev/null +++ b/utils/symbols/shared.exp @@ -0,0 +1 @@ +_createSharedPlugin diff --git a/utils/symbols/shared.version b/utils/symbols/shared.version new file mode 100644 index 00000000..e60a663a --- /dev/null +++ b/utils/symbols/shared.version @@ -0,0 +1,4 @@ +{ + global: createSharedPlugin; + local: *; +}; From 83e8afdb77695686a6b61dda2a5013d991232ddb Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 9 Dec 2021 21:08:59 +0000 Subject: [PATCH 250/504] Expose clipboard functions from pugl, tested to work in Cardinal --- dgl/TopLevelWidget.hpp | 2 ++ dgl/Window.hpp | 21 +++++++++++++++++++++ dgl/src/TopLevelWidget.cpp | 10 ++++++++++ dgl/src/Window.cpp | 10 ++++++++++ 4 files changed, 43 insertions(+) diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 5441dbcd..dfdcf750 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -101,6 +101,8 @@ public: void repaint(const Rectangle& rect) noexcept; // TODO group stuff after here, convenience functions present in Window class + bool setClipboard(const char* mimeType, const void* data, size_t dataSize); + const void* getClipboard(const char*& mimeType, size_t& dataSize); bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); bool removeIdleCallback(IdleCallback* callback); double getScaleFactor() const noexcept; diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 7aaa17df..d884f54f 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -263,6 +263,27 @@ public: */ void setIgnoringKeyRepeat(bool ignore) noexcept; + /** + Set the clipboard contents. + + This sets the system clipboard contents, + which can be retrieved with getClipboard() or pasted into other applications. + + If using a string, the use of a null terminator is required (and must be part of dataSize).@n + The MIME type of the data "text/plain" is assumed if null is used. + */ + bool setClipboard(const char* mimeType, const void* data, size_t dataSize); + + /** + Get the clipboard contents. + + This gets the system clipboard contents, + which may have been set with setClipboard() or copied from another application. + + returns the clipboard contents, or null. + */ + const void* getClipboard(const char*& mimeType, size_t& dataSize); + /** Add a callback function to be triggered on every idle cycle or on a specific timer frequency. You can add more than one, and remove them at anytime with removeIdleCallback(). diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 556d9bd7..4c0257b6 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -60,6 +60,16 @@ void TopLevelWidget::setSize(const Size& size) pData->window.setSize(size); } +bool TopLevelWidget::setClipboard(const char* mimeType, const void* data, size_t dataSize) +{ + return pData->window.setClipboard(mimeType, data, dataSize); +} + +const void* TopLevelWidget::getClipboard(const char*& mimeType, size_t& dataSize) +{ + return pData->window.getClipboard(mimeType, dataSize); +} + bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) { return pData->window.addIdleCallback(callback, timerFrequencyInMs); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index c09106fa..6459163f 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -257,6 +257,16 @@ void Window::setIgnoringKeyRepeat(const bool ignore) noexcept puglSetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT, ignore); } +bool Window::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) +{ + return puglSetClipboard(pData->view, mimeType, data, dataSize) == PUGL_SUCCESS; +} + +const void* Window::getClipboard(const char*& mimeType, size_t& dataSize) +{ + return puglGetClipboard(pData->view, &mimeType, &dataSize); +} + bool Window::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) { DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr, false) From 84745e90c93dfd9af5e152cba739c6f8895756a5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 9 Dec 2021 22:57:28 +0000 Subject: [PATCH 251/504] Workaround puglGetClipboard behaviour on X11 --- dgl/src/Window.cpp | 6 +++++- dgl/src/WindowPrivateData.cpp | 20 ++++++++++++++++++++ dgl/src/WindowPrivateData.hpp | 3 +++ dgl/src/pugl-upstream | 2 +- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 6459163f..6fa5f3a6 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -264,7 +264,11 @@ bool Window::setClipboard(const char* const mimeType, const void* const data, co const void* Window::getClipboard(const char*& mimeType, size_t& dataSize) { - return puglGetClipboard(pData->view, &mimeType, &dataSize); + DISTRHO_SAFE_ASSERT_RETURN(!pData->ignoreEvents, nullptr); + pData->ignoreEvents = true; + const void* const clipboard = puglGetClipboard(pData->view, &mimeType, &dataSize); + pData->ignoreEvents = false; + return clipboard; } bool Window::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 097299b2..6c4ee299 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -83,6 +83,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -109,6 +110,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -139,6 +141,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -171,6 +174,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -785,6 +789,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< View must be drawn, a #PuglEventExpose case PUGL_EXPOSE: + if (pData->ignoreEvents) + break; // unused x, y, width, height (double) pData->onPuglExpose(); break; @@ -798,6 +804,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_FOCUS_IN: ///< Keyboard focus left view, a #PuglEventFocus case PUGL_FOCUS_OUT: + if (pData->ignoreEvents) + break; pData->onPuglFocus(event->type == PUGL_FOCUS_IN, static_cast(event->focus.mode)); break; @@ -807,6 +815,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Key released, a #PuglEventKey case PUGL_KEY_RELEASE: { + if (pData->ignoreEvents) + break; // unused x, y, xRoot, yRoot (double) Widget::KeyboardEvent ev; ev.mod = event->key.state; @@ -830,6 +840,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Character entered, a #PuglEventText case PUGL_TEXT: { + if (pData->ignoreEvents) + break; // unused x, y, xRoot, yRoot (double) Widget::CharacterInputEvent ev; ev.mod = event->text.state; @@ -854,6 +866,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Mouse button released, a #PuglEventButton case PUGL_BUTTON_RELEASE: { + if (pData->ignoreEvents) + break; Widget::MouseEvent ev; ev.mod = event->button.state; ev.flags = event->button.flags; @@ -869,6 +883,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Pointer moved, a #PuglEventMotion case PUGL_MOTION: { + if (pData->ignoreEvents) + break; Widget::MotionEvent ev; ev.mod = event->motion.state; ev.flags = event->motion.flags; @@ -882,6 +898,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Scrolled, a #PuglEventScroll case PUGL_SCROLL: { + if (pData->ignoreEvents) + break; Widget::ScrollEvent ev; ev.mod = event->scroll.state; ev.flags = event->scroll.flags; @@ -900,6 +918,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Timer triggered, a #PuglEventTimer case PUGL_TIMER: + if (pData->ignoreEvents) + break; if (IdleCallback* const idleCallback = reinterpret_cast(event->timer.id)) idleCallback->idleCallback(); break; diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 68b2e375..ffb2237a 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -77,6 +77,9 @@ struct Window::PrivateData : IdleCallback { /** Whether to ignore idle callback requests, useful for temporary windows. */ bool ignoreIdleCallbacks; + /** Whether to ignore pugl events (except create and destroy), used for puglGetClipboard. */ + bool ignoreEvents; + /** Render to a picture file when non-null, automatically free+unset after saving. */ char* filenameToRenderInto; diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 7418e9d1..f69d953d 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 7418e9d1f0045d192c86e7dea25272264ca92f22 +Subproject commit f69d953d95dae70e32d37e522076fd41cec31a22 From d9e856141a3f34618b03080a9541cc70e8c7210e Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 10 Dec 2021 19:08:56 +0000 Subject: [PATCH 252/504] Allow to move VST2 folder around Signed-off-by: falkTX --- distrho/src/DistrhoUtils.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index 7086ae0a..eb39fdc5 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -99,7 +99,7 @@ const char* getResourcePath(const char* const bundlePath) noexcept if (bundlePathLV2.isEmpty()) { - bundlePathLV2 += bundlePath; + bundlePathLV2 = bundlePath; bundlePathLV2 += DISTRHO_OS_SEP_STR "resources"; } @@ -109,11 +109,10 @@ const char* getResourcePath(const char* const bundlePath) noexcept if (bundlePathVST2.isEmpty()) { - bundlePathVST2 += bundlePath; + bundlePathVST2 = bundlePath; # ifdef DISTRHO_OS_MAC bundlePathVST2 += "/Contents/Resources"; # else - DISTRHO_SAFE_ASSERT_RETURN(bundlePathVST2.endsWith(".vst"), nullptr); bundlePathVST2 += DISTRHO_OS_SEP_STR "resources"; # endif } @@ -124,7 +123,7 @@ const char* getResourcePath(const char* const bundlePath) noexcept if (bundlePathVST3.isEmpty()) { - bundlePathVST3 += bundlePath; + bundlePathVST3 = bundlePath; bundlePathVST3 += "/Contents/Resources"; } From bf74aa24556c39648b7b69eb85ae1f2b99a7b30a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 12 Dec 2021 16:47:30 +0000 Subject: [PATCH 253/504] Use -g in build flags if using SKIP_STRIPPING=true --- Makefile.base.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile.base.mk b/Makefile.base.mk index fdbeb374..016777d7 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -163,6 +163,10 @@ LINK_OPTS += -Wl,--strip-all endif endif +ifeq ($(SKIP_STRIPPING),true) +BASE_FLAGS += -g +endif + ifeq ($(NOOPT),true) # Non-CPU-specific optimization flags BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections From ef69eba593298b90eb433493470a1ba4c0f1c0bf Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 12 Dec 2021 16:48:07 +0000 Subject: [PATCH 254/504] fontstash: ensure new allocated reasons have their memory cleared --- dgl/src/nanovg/fontstash.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dgl/src/nanovg/fontstash.h b/dgl/src/nanovg/fontstash.h index 72569282..cfe1072f 100644 --- a/dgl/src/nanovg/fontstash.h +++ b/dgl/src/nanovg/fontstash.h @@ -907,6 +907,8 @@ static int fons__allocFont(FONScontext* stash) stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts); if (stash->fonts == NULL) return -1; + for (int i=stash->nfonts; icfonts; ++i) + stash->fonts[i] = NULL; } font = (FONSfont*)malloc(sizeof(FONSfont)); if (font == NULL) goto error; @@ -1015,6 +1017,8 @@ static FONSglyph* fons__allocGlyph(FONSfont* font) font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs); if (font->glyphs == NULL) return NULL; + for (int i=font->nglyphs; icglyphs; ++i) + memset(&font->glyphs[i], 0, sizeof(*font->glyphs)); } font->nglyphs++; return &font->glyphs[font->nglyphs-1]; From 86b8a102bb0ec78fbd072a01ceebe904d29283e7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 12 Dec 2021 18:38:05 +0000 Subject: [PATCH 255/504] Add NVG_DISABLE_SKIPPING_WHITESPACE build option --- dgl/Makefile | 3 +++ dgl/src/nanovg/nanovg.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index e570a949..72840583 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -13,6 +13,9 @@ BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-un BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include LINK_FLAGS += $(DGL_LIBS) +ifeq ($(NVG_DISABLE_SKIPPING_WHITESPACE),true) +BUILD_CXX_FLAGS += -DNVG_DISABLE_SKIPPING_WHITESPACE +endif ifeq ($(USE_OPENGL3),true) BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 endif diff --git a/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index 3f9c0b4b..14765dd3 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/dgl/src/nanovg/nanovg.c @@ -30,6 +30,12 @@ #include "stb_image.h" #endif +#ifdef NVG_DISABLE_SKIPPING_WHITESPACE +#define NVG_SKIPPED_CHAR NVG_SPACE +#else +#define NVG_SKIPPED_CHAR NVG_CHAR +#endif + #ifdef _MSC_VER #pragma warning(disable: 4100) // unreferenced formal parameter #pragma warning(disable: 4127) // conditional expression is constant @@ -2775,7 +2781,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa } else { if (rowStart == NULL) { // Skip white space until the beginning of the line - if (type == NVG_CHAR || type == NVG_CJK_CHAR) { + if (type == NVG_CHAR || type == NVG_CJK_CHAR || type == NVG_SKIPPED_CHAR) { // The current char is the row so far rowStartX = iter.x; rowStart = iter.str; @@ -2795,7 +2801,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa float nextWidth = iter.nextx - rowStartX; // track last non-white space character - if (type == NVG_CHAR || type == NVG_CJK_CHAR) { + if (type == NVG_CHAR || type == NVG_CJK_CHAR || type == NVG_SKIPPED_CHAR) { rowEnd = iter.next; rowWidth = iter.nextx - rowStartX; rowMaxX = q.x1 - rowStartX; From cd2c8c223eb31aa0bffd16bf38557e419fd7e1e5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 12 Dec 2021 19:56:47 +0000 Subject: [PATCH 256/504] Fix duplicated character input on VST host key up --- distrho/src/DistrhoUIInternal.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 18562018..669d2427 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -326,6 +326,9 @@ public: const bool ret = ui->onKeyboard(ev); + if (! press) + return ret; + DGL_NAMESPACE::Widget::CharacterInputEvent cev; cev.mod = mods; cev.character = key; @@ -348,6 +351,9 @@ public: const bool ret = ui->onKeyboard(ev); + if (! press) + return ret; + DGL_NAMESPACE::Widget::CharacterInputEvent cev; cev.mod = mods; cev.keycode = keycode; From ed6705a161e13565391e020389b6c1b7bd5ab742 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 15 Dec 2021 00:48:31 +0000 Subject: [PATCH 257/504] Give more useful rtaudio errors --- distrho/src/jackbridge/RtAudioBridge.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index 46b82c91..78f2fdd2 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -108,6 +108,9 @@ struct RtAudioBridge { try { rtAudio->openStream(&outParams, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, RtAudioCallback, this, &opts, nullptr); + } catch (const RtAudioError& err) { + d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); + return false; } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); handle = rtAudio; From 7357d71fa9ce01eab8707e775f862be7e3be0676 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 15 Dec 2021 11:14:33 +0000 Subject: [PATCH 258/504] Expose cursor API from pugl, with added diagonal resize cursors Signed-off-by: falkTX --- Makefile.base.mk | 3 +-- dgl/Base.hpp | 17 +++++++++++++++++ dgl/TopLevelWidget.hpp | 1 + dgl/Window.hpp | 9 +++++++++ dgl/src/TopLevelWidget.cpp | 5 +++++ dgl/src/Window.cpp | 5 +++++ dgl/src/pugl-upstream | 2 +- 7 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 016777d7..c4b0e05c 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -304,8 +304,7 @@ ifeq ($(HAVE_X11),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) ifeq ($(HAVE_XCURSOR),true) -# TODO -DHAVE_XCURSOR -DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xcursor) +DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xcursor) -DHAVE_XCURSOR DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xcursor) endif ifeq ($(HAVE_XEXT),true) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index 0288cb06..bb0538c9 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -130,6 +130,23 @@ enum CrossingMode { kCrossingUngrab ///< Crossing due to a grab release }; +/** + A mouse cursor type. + + This is a portable subset of mouse cursors that exist on X11, MacOS, and Windows. +*/ +enum MouseCursor { + kMouseCursorArrow, ///< Default pointing arrow + kMouseCursorCaret, ///< Caret (I-Beam) for text entry + kMouseCursorCrosshair, ///< Cross-hair + kMouseCursorHand, ///< Hand with a pointing finger + kMouseCursorNotAllowed, ///< Operation not allowed + kMouseCursorLeftRight, ///< Left/right arrow for horizontal resize + kMouseCursorUpDown, ///< Up/down arrow for vertical resize + kMouseCursorDiagonal, ///< Top-left to bottom-right arrow for diagonal resize + kMouseCursorAntiDiagonal ///< Bottom-left to top-right arrow for diagonal resize +}; + /** Scroll direction. diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index dfdcf750..522dfaf8 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -103,6 +103,7 @@ public: // TODO group stuff after here, convenience functions present in Window class bool setClipboard(const char* mimeType, const void* data, size_t dataSize); const void* getClipboard(const char*& mimeType, size_t& dataSize); + bool setCursor(MouseCursor cursor); bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); bool removeIdleCallback(IdleCallback* callback); double getScaleFactor() const noexcept; diff --git a/dgl/Window.hpp b/dgl/Window.hpp index d884f54f..f91bb41e 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -284,6 +284,15 @@ public: */ const void* getClipboard(const char*& mimeType, size_t& dataSize); + /** + Set the mouse cursor. + + This changes the system cursor that is displayed when the pointer is inside the window. + May fail if setting the cursor is not supported on this system, + for example if compiled on X11 without Xcursor support. + */ + bool setCursor(MouseCursor cursor); + /** Add a callback function to be triggered on every idle cycle or on a specific timer frequency. You can add more than one, and remove them at anytime with removeIdleCallback(). diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 4c0257b6..9a6b5c26 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -70,6 +70,11 @@ const void* TopLevelWidget::getClipboard(const char*& mimeType, size_t& dataSize return pData->window.getClipboard(mimeType, dataSize); } +bool TopLevelWidget::setCursor(const MouseCursor cursor) +{ + return pData->window.setCursor(cursor); +} + bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) { return pData->window.addIdleCallback(callback, timerFrequencyInMs); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 6fa5f3a6..1e2f3a12 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -271,6 +271,11 @@ const void* Window::getClipboard(const char*& mimeType, size_t& dataSize) return clipboard; } +bool Window::setCursor(const MouseCursor cursor) +{ + return puglSetCursor(pData->view, static_cast(cursor)) == PUGL_SUCCESS; +} + bool Window::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) { DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr, false) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index f69d953d..d6c17abe 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit f69d953d95dae70e32d37e522076fd41cec31a22 +Subproject commit d6c17abe22399acefc00fd0b78b99ea38ce27f41 From c2341b1cadbca2019719bf9a55418325075b435f Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 15 Dec 2021 17:51:49 +0000 Subject: [PATCH 259/504] More tweaks to pugl cursor handling Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index d6c17abe..15f70d2a 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit d6c17abe22399acefc00fd0b78b99ea38ce27f41 +Subproject commit 15f70d2a7606cf50d48b72408c0ab4ea7a683380 From 62ba84882410fd7ca3fcbfe417a731494b57cf2d Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 15 Dec 2021 23:27:07 +0000 Subject: [PATCH 260/504] Fix setGeometryConstraints on macOS, aspect ratio was always on --- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 15f70d2a..0363bc36 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 15f70d2a7606cf50d48b72408c0ab4ea7a683380 +Subproject commit 0363bc36379a268877af147e759fd6fd5f576c36 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 4d28af08..7181fa3f 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -325,20 +325,12 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co view->maxAspectY = (int)height; } -#if defined(DISTRHO_OS_HAIKU) - // nothing? -#elif defined(DISTRHO_OS_MAC) - /* - if (view->impl->window) - { - [view->impl->window setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)]; +#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) + puglSetMinSize(view, width, height); - if (aspect) - [view->impl->window setContentAspectRatio:sizePoints(view, view->minAspectX, view->minAspectY)]; + if (aspect) { + puglSetAspectRatio(view, width, height, width, height); } - */ - puglSetMinSize(view, width, height); - puglSetAspectRatio(view, width, height, width, height); #elif defined(DISTRHO_OS_WINDOWS) // nothing #else From 400dca29de54e1cceee55f22672e940ae5d5e005 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 16 Dec 2021 13:59:27 +0000 Subject: [PATCH 261/504] Do not set _NET_WM_WINDOW_TYPE_DIALOG hint for standalones Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 2 +- dgl/src/pugl.cpp | 17 +++++++++-------- dgl/src/pugl.hpp | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 6c4ee299..a00320c7 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -761,7 +761,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_CREATE: #ifdef HAVE_X11 if (! pData->isEmbed) - puglX11SetWindowTypeAndPID(view); + puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); #endif break; diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 7181fa3f..f18dcb28 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -608,7 +608,7 @@ PuglStatus puglX11GrabFocus(const PuglView* const view) // -------------------------------------------------------------------------------------------------------------------- // X11 specific, set dialog window type and pid hints -void puglX11SetWindowTypeAndPID(const PuglView* const view) +void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone) { const PuglInternals* const impl = view->impl; @@ -618,14 +618,15 @@ void puglX11SetWindowTypeAndPID(const PuglView* const view) const Atom _wt = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE", False); - // Setting the window to both dialog and normal will produce a decorated floating dialog - // Order is important: DIALOG needs to come before NORMAL - const Atom _wts[2] = { - XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False), - XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False) - }; + Atom _wts[2]; + int numAtoms = 0; - XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); + if (! isStandalone) + _wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False); + + _wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False); + + XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index c19c82eb..2075627a 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -134,7 +134,7 @@ puglX11GrabFocus(const PuglView* view); // X11 specific, set dialog window type and pid hints PUGL_API void -puglX11SetWindowTypeAndPID(const PuglView* view); +puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); #endif PUGL_END_DECLS From 493837049e773e45f842c0ee83b6b69c7c56adf9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 16 Dec 2021 14:21:41 +0000 Subject: [PATCH 262/504] Remove superfulous line Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 6c8e6724..66b69272 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -305,7 +305,7 @@ function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC) add_dependencies("${NAME}-lv2" lv2_ttl_generator) add_custom_command(TARGET "${NAME}-lv2" POST_BUILD - COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} + COMMAND "$" "$" WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2" From b325295d6c77032eeb042aacd242683aa1773f5d Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Dec 2021 01:55:12 +0000 Subject: [PATCH 263/504] Allow to set NVG_FONT_TEXTURE_FLAGS, less blurry and precise fonts Signed-off-by: falkTX --- dgl/Makefile | 3 +++ dgl/src/nanovg/nanovg.c | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 72840583..04494eba 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -16,6 +16,9 @@ LINK_FLAGS += $(DGL_LIBS) ifeq ($(NVG_DISABLE_SKIPPING_WHITESPACE),true) BUILD_CXX_FLAGS += -DNVG_DISABLE_SKIPPING_WHITESPACE endif +ifneq ($(NVG_FONT_TEXTURE_FLAGS),) +BUILD_CXX_FLAGS += -DNVG_FONT_TEXTURE_FLAGS=$(NVG_FONT_TEXTURE_FLAGS) +endif ifeq ($(USE_OPENGL3),true) BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 endif diff --git a/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index 14765dd3..0c75b17f 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/dgl/src/nanovg/nanovg.c @@ -36,6 +36,10 @@ #define NVG_SKIPPED_CHAR NVG_CHAR #endif +#ifndef NVG_FONT_TEXTURE_FLAGS +#define NVG_FONT_TEXTURE_FLAGS 0 +#endif + #ifdef _MSC_VER #pragma warning(disable: 4100) // unreferenced formal parameter #pragma warning(disable: 4127) // conditional expression is constant @@ -348,7 +352,12 @@ NVGcontext* nvgCreateInternal(NVGparams* params, NVGcontext* other) // Share th if (ctx->fontContext->fs == NULL) goto error; // Create font texture - ctx->fontContext->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, fontParams.width, fontParams.height, 0, NULL); + ctx->fontContext->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr, + NVG_TEXTURE_ALPHA, + fontParams.width, + fontParams.height, + NVG_FONT_TEXTURE_FLAGS, + NULL); if (ctx->fontContext->fontImages[0] == 0) goto error; ctx->fontContext->fontImageIdx = 0; } @@ -2490,7 +2499,9 @@ static int nvg__allocTextAtlas(NVGcontext* ctx) iw *= 2; if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE) iw = ih = NVG_MAX_FONTIMAGE_SIZE; - ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, iw, ih, 0, NULL); + ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1] + = ctx->params.renderCreateTexture(ctx->params.userPtr, + NVG_TEXTURE_ALPHA, iw, ih, NVG_FONT_TEXTURE_FLAGS, NULL); } ++ctx->fontContext->fontImageIdx; fonsResetAtlas(ctx->fontContext->fs, iw, ih); From 2d916332ff773db629ef9211ac3b3a8a5a428ea0 Mon Sep 17 00:00:00 2001 From: Filipe Coelho Date: Tue, 28 Dec 2021 16:34:05 +0000 Subject: [PATCH 264/504] Rework VST3 implementation (#351) * Start cleanup of vst3 code * Continue vst3 rework * Some renaming and working parameter changes Signed-off-by: falkTX * Continue rework; factory non-static, fixing random things Signed-off-by: falkTX * Attempt at parameter outputs and failing Signed-off-by: falkTX * Still not working Signed-off-by: falkTX * More vst3 * VST3 starts to work once again Signed-off-by: falkTX * Cleanup Signed-off-by: falkTX * A few more vst3 details, now in similar state as before Signed-off-by: falkTX * More VST3 things, almost ready Signed-off-by: falkTX * Little tweaks to make reaper happy * Fix a print Signed-off-by: falkTX * More vst3 tweaks, makes UI behave a bit better Signed-off-by: falkTX * Fixup VST3 applyGeometryConstraints Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 31 +- distrho/src/DistrhoPluginVST3.cpp | 2741 ++++++++++++++---------- distrho/src/DistrhoUIPrivateData.hpp | 29 +- distrho/src/DistrhoUIVST3.cpp | 353 +-- distrho/src/travesty/base.h | 1 + distrho/src/travesty/edit_controller.h | 6 +- distrho/src/travesty/view.h | 2 +- examples/Info/Makefile | 2 +- 8 files changed, 1888 insertions(+), 1277 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 969e6685..a48b20c5 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -81,6 +81,28 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por } } +// #ifdef DISTRHO_PLUGIN_TARGET_VST3 +enum Vst3InternalParameters { + kVst3InternalParameterActive = 0, + kVst3InternalParameterBufferSize, + kVst3InternalParameterSampleRate, +# if DISTRHO_PLUGIN_WANT_LATENCY + kVst3InternalParameterLatency, +# endif +# if DISTRHO_PLUGIN_WANT_PROGRAMS + kVst3InternalParameterProgram, +# endif + kVst3InternalParameterBaseCount, +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, + kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, + kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end +# else + kVst3InternalParameterCount = kVst3InternalParameterBaseCount +# endif +}; +// #endif + // ----------------------------------------------------------------------- // Plugin private data @@ -170,7 +192,7 @@ struct Plugin::PrivateData { #endif #ifdef DISTRHO_PLUGIN_TARGET_LV2 -# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) +# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_STATE || DISTRHO_PLUGIN_WANT_TIMEPOS) parameterOffset += 1; # endif # if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) @@ -179,12 +201,7 @@ struct Plugin::PrivateData { #endif #ifdef DISTRHO_PLUGIN_TARGET_VST3 -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - parameterOffset += 130 * 16; // all MIDI CCs plus aftertouch and pitchbend -# endif -# if DISTRHO_PLUGIN_WANT_PROGRAMS - parameterOffset += 1; -# endif + parameterOffset += kVst3InternalParameterCount; #endif } diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index f6c7958e..7464e1a6 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -28,6 +28,14 @@ # define DISTRHO_PLUGIN_HAS_UI 0 #endif +#if DISTRHO_PLUGIN_HAS_UI == 1 && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS == 0 +# define DPF_VST3_USES_SEPARATE_CONTROLLER +#endif + +#define DPF_VST3_MAX_BUFFER_SIZE 32768 +#define DPF_VST3_MAX_SAMPLE_RATE 384000 +#define DPF_VST3_MAX_LATENCY DPF_VST3_MAX_SAMPLE_RATE * 10 + #if DISTRHO_PLUGIN_HAS_UI # include "../extra/RingBuffer.hpp" #endif @@ -58,21 +66,34 @@ namespace std { #include /* TODO items: - * - parameter enumeration as lists - * - hide parameter outputs? - * - hide program parameter? - * - deal with parameter triggers + * == parameters + * - test parameter triggers + * - have parameter outputs host-provided UI working in at least 1 host + * - parameter groups via unit ids + * - test parameter changes from DSP (aka requestParameterValueChange) + * - how to hide parameter outputs? + * - how to hide program parameter? + * - test receiving midi CC + * - implement getParameterNormalized/setParameterNormalized for MIDI CC params ? + * - fully implemented parameter stuff and verify + * - float to int safe casting + * - verify that latency changes works (with and without DPF_VST3_USES_SEPARATE_CONTROLLER) + * == MIDI * - MIDI CC changes (need to store value to give to the host?) * - MIDI program changes * - MIDI sysex * - append MIDI input events in a sorted way + * == BUSES * - bus arrangements * - optional audio buses, create dummy buffer of max_block_size length for them * - routing info, do we care? * - set sidechain bus name from port group - * - implement getParameterValueForString (use names from enumeration if available, fallback to std::atof) + * == CV + * - cv scaling to -1/+1 + * - test in at least 1 host + * == INFO * - set factory email (needs new DPF API, useful for LV2 as well) - * - do something with get_controller_class_id and set_io_mode? + * - do something with set_io_mode? */ START_NAMESPACE_DISTRHO @@ -130,6 +151,7 @@ const char* tuid2str(const v3_tuid iid) { V3_ID(0x7F4EFE59,0xF3204967,0xAC27A3AE,0xAFB63038), "{v3_edit_controller2|NOT}" }, { V3_ID(0x067D02C1,0x5B4E274D,0xA92D90FD,0x6EAF7240), "{v3_component_handler_bus_activation|NOT}" }, { V3_ID(0xC1271208,0x70594098,0xB9DD34B3,0x6BB0195E), "{v3_edit_controller_host_editing|NOT}" }, + { V3_ID(0xB7F8F859,0x41234872,0x91169581,0x4F3721A3), "{v3_edit_controller_note_expression_controller|NOT}" }, // units { V3_ID(0x8683B01F,0x7B354F70,0xA2651DEC,0x353AF4FF), "{v3_program_list_data|NOT}" }, { V3_ID(0x6C389611,0xD391455D,0xB870B833,0x94A0EFDD), "{v3_unit_data|NOT}" }, @@ -221,6 +243,38 @@ const char* tuid2str(const v3_tuid iid) // -------------------------------------------------------------------------------------------------------------------- +static bool strcmp_utf16(const int16_t* const str16, const char* const str8) +{ + size_t i = 0; + for (; str8[i] != '\0'; ++i) + { + const uint8_t char8 = static_cast(str8[i]); + + // skip non-ascii chars, unsupported + if (char8 >= 0x80) + return false; + + if (str16[i] != char8) + return false; + } + + return str16[i] == str8[i]; +} + +// -------------------------------------------------------------------------------------------------------------------- + +static size_t strlen_utf16(const int16_t* const str) +{ + size_t i = 0; + + while (str[i] != 0) + ++i; + + return i; +} + +// -------------------------------------------------------------------------------------------------------------------- + static void strncpy(char* const dst, const char* const src, const size_t length) { DISTRHO_SAFE_ASSERT_RETURN(length > 0,); @@ -236,6 +290,28 @@ static void strncpy(char* const dst, const char* const src, const size_t length) } } +void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length) +{ + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); + + if (const size_t len = std::min(strlen_utf16(src), length-1U)) + { + for (size_t i=0; i= 0x80) + continue; + + dst[i] = src[i]; + } + dst[len] = 0; + } + else + { + dst[0] = 0; + } +} + void strncpy_utf16(int16_t* const dst, const char* const src, const size_t length) { DISTRHO_SAFE_ASSERT_RETURN(length > 0,); @@ -244,7 +320,7 @@ void strncpy_utf16(int16_t* const dst, const char* const src, const size_t lengt { for (size_t i=0; i= 0x80) continue; @@ -295,14 +371,20 @@ void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size return snprintf_utf16_t(dst, value, "%f", size); } +static inline +void snprintf_i32_utf16(int16_t* const dst, const int value, const size_t size) +{ + return snprintf_utf16_t(dst, value, "%d", size); +} + // -------------------------------------------------------------------------------------------------------------------- -// handy way to create a utf16 string on the current function scope, used for message strings +// handy way to create a utf16 string from a utf8 one on the current function scope, used for message strings struct ScopedUTF16String { int16_t* str; ScopedUTF16String(const char* const s) noexcept; ~ScopedUTF16String() noexcept; - operator int16_t*() const noexcept; + operator const int16_t*() const noexcept; }; // -------------------------------------------------------------------------------------------------------------------- @@ -310,8 +392,8 @@ struct ScopedUTF16String { ScopedUTF16String::ScopedUTF16String(const char* const s) noexcept : str(nullptr) { - const size_t len = strlen(s); - str = (int16_t*)malloc(sizeof(int16_t) * (len + 1)); + const size_t len = std::strlen(s); + str = static_cast(std::malloc(sizeof(int16_t) * (len + 1))); DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,); strncpy_utf16(str, s, len + 1); } @@ -321,11 +403,28 @@ ScopedUTF16String::~ScopedUTF16String() noexcept std::free(str); } -ScopedUTF16String::operator int16_t*() const noexcept +ScopedUTF16String::operator const int16_t*() const noexcept { return str; } +// -------------------------------------------------------------------------------------------------------------------- +// handy way to create a utf8 string from a utf16 one on the current function scope + +struct ScopedUTF8String { + char str[128]; + + ScopedUTF8String(const int16_t* const s) noexcept + { + strncpy_utf8(str, s, 128); + } + + operator const char*() const noexcept + { + return str; + } +}; + // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (implemented on UI side) @@ -366,20 +465,23 @@ class PluginVst3 } inputBuses, outputBuses; public: - PluginVst3(v3_host_application** const context) + PluginVst3(v3_host_application** const host) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), #if DISTRHO_PLUGIN_HAS_UI - fConnection(nullptr), - fHostContext(context), +# ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + fConnectionFromCompToCtrl(nullptr), +# endif + fConnectionFromCtrlToView(nullptr), + fHostApplication(host), #endif - fParameterOffset(fPlugin.getParameterOffset()), - fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()), - fParameterValues(nullptr), - fChangedParameterValues(nullptr) + fParameterCount(fPlugin.getParameterCount()), + fVst3ParameterCount(fParameterCount + kVst3InternalParameterCount), + fCachedParameterValues(nullptr), + fParameterValuesChangedDuringProcessing(nullptr) #if DISTRHO_PLUGIN_HAS_UI + , fParameterValueChangesForUI(nullptr) , fConnectedToUI(false) - , fNextSampleRate(0.0) #endif #if DISTRHO_PLUGIN_WANT_LATENCY , fLastKnownLatency(fPlugin.getLatency()) @@ -419,7 +521,7 @@ public: if (port.hints & kAudioPortIsCV) port.busId = inputBuses.audio + inputBuses.sidechain + cvInputBusId++; else if (port.hints & kAudioPortIsSidechain) - port.busId = 1; + port.busId = inputBuses.audio; else port.busId = 0; } @@ -451,24 +553,36 @@ public: if (port.hints & kAudioPortIsCV) port.busId = outputBuses.audio + outputBuses.sidechain + cvOutputBusId++; else if (port.hints & kAudioPortIsSidechain) - port.busId = 1; + port.busId = outputBuses.audio; else port.busId = 0; } #endif - if (const uint32_t parameterCount = fPlugin.getParameterCount()) - { - fParameterValues = new float[parameterCount]; - - for (uint32_t i=0; i < parameterCount; ++i) - fParameterValues[i] = fPlugin.getParameterDefault(i); - } - - if (fRealParameterCount != 0) + if (const uint32_t extraParameterCount = fParameterCount + kVst3InternalParameterBaseCount) { - fChangedParameterValues = new bool[fRealParameterCount]; - std::memset(fChangedParameterValues, 0, sizeof(bool)*fRealParameterCount); + fCachedParameterValues = new float[extraParameterCount]; + + fCachedParameterValues[kVst3InternalParameterActive] = 0.0f; + fCachedParameterValues[kVst3InternalParameterBufferSize] = fPlugin.getBufferSize(); + fCachedParameterValues[kVst3InternalParameterSampleRate] = fPlugin.getSampleRate(); + #if DISTRHO_PLUGIN_WANT_LATENCY + fCachedParameterValues[kVst3InternalParameterLatency] = fLastKnownLatency; + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + fCachedParameterValues[kVst3InternalParameterProgram] = 0.0f; + #endif + + for (uint32_t i=0; i < fParameterCount; ++i) + fCachedParameterValues[kVst3InternalParameterBaseCount + i] = fPlugin.getParameterDefault(i); + + fParameterValuesChangedDuringProcessing = new bool[extraParameterCount]; + std::memset(fParameterValuesChangedDuringProcessing, 0, sizeof(bool)*extraParameterCount); + + #if DISTRHO_PLUGIN_HAS_UI + fParameterValueChangesForUI = new bool[extraParameterCount]; + std::memset(fParameterValueChangesForUI, 0, sizeof(bool)*extraParameterCount); + #endif } #if DISTRHO_PLUGIN_WANT_STATE @@ -481,23 +595,57 @@ public: #if !DISTRHO_PLUGIN_HAS_UI // unused - return; (void)context; + return; (void)host; #endif } ~PluginVst3() { - if (fParameterValues != nullptr) + if (fCachedParameterValues != nullptr) + { + delete[] fCachedParameterValues; + fCachedParameterValues = nullptr; + } + + if (fParameterValuesChangedDuringProcessing != nullptr) { - delete[] fParameterValues; - fParameterValues = nullptr; + delete[] fParameterValuesChangedDuringProcessing; + fParameterValuesChangedDuringProcessing = nullptr; } - if (fChangedParameterValues != nullptr) + #if DISTRHO_PLUGIN_HAS_UI + if (fParameterValueChangesForUI != nullptr) + { + delete[] fParameterValueChangesForUI; + fParameterValueChangesForUI = nullptr; + } + #endif + } + + // ---------------------------------------------------------------------------------------------------------------- + // utilities and common code + + void setNormalizedPluginParameterValue(const uint32_t index, const float normalized) + { + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + const uint32_t hints = fPlugin.getParameterHints(index); + float value = ranges.getUnnormalizedValue(normalized); + + if (hints & kParameterIsBoolean) + { + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + value = value > midRange ? ranges.max : ranges.min; + } + else if (hints & kParameterIsInteger) { - delete[] fChangedParameterValues; - fChangedParameterValues = nullptr; + value = std::round(value); } + + fCachedParameterValues[kVst3InternalParameterBaseCount + index] = value; +#if DISTRHO_PLUGIN_HAS_UI + fParameterValueChangesForUI[kVst3InternalParameterBaseCount + index] = true; +#endif + fPlugin.setParameterValue(index, value); } // ---------------------------------------------------------------------------------------------------------------- @@ -581,7 +729,7 @@ public: { numChannels = inputBuses.numSidechain; busType = V3_AUX; - flags = v3_bus_flags(0); + flags = static_cast(0); break; } // fall-through @@ -611,6 +759,7 @@ public: } } #else + d_stdout("invalid bus %d", busId); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_INPUTS } @@ -633,7 +782,7 @@ public: { numChannels = outputBuses.numSidechain; busType = V3_AUX; - flags = v3_bus_flags(0); + flags = static_cast(0); break; } // fall-through @@ -663,6 +812,7 @@ public: } } #else + d_stdout("invalid bus %d", busId); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_OUTPUTS } @@ -676,6 +826,7 @@ public: info->flags = flags; return V3_OK; #else + d_stdout("invalid bus, line %d", __LINE__); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS } @@ -686,6 +837,7 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG); #else + d_stdout("invalid bus, line %d", __LINE__); return V3_INVALID_ARG; #endif } @@ -694,6 +846,7 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG); #else + d_stdout("invalid bus, line %d", __LINE__); return V3_INVALID_ARG; #endif } @@ -741,11 +894,9 @@ public: */ v3_result setState(v3_bstream** const stream) { -#if DISTRHO_PLUGIN_HAS_UI - const bool connectedToUI = fConnection != nullptr && fConnectedToUI; -#endif - const uint32_t paramCount = fPlugin.getParameterCount(); - + #if DISTRHO_PLUGIN_HAS_UI + const bool connectedToUI = fConnectionFromCtrlToView != nullptr && fConnectedToUI; + #endif String key, value; bool fillingKey = true; // if filling key or value char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters) @@ -754,6 +905,9 @@ public: buffer[sizeof(buffer)-1] = '\xff'; v3_result res; + // int64_t ignore = 0; + // v3_cpp_obj(stream)->seek(stream, 0, V3_SEEK_SET, &ignore); + for (int32_t pos = 0, term = 0, read; term == 0; pos += read) { res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); @@ -854,8 +1008,8 @@ public: # if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) { - fChangedParameterValues[0] = false; - sendParameterChangeToUI(0, fCurrentProgram); + fParameterValueChangesForUI[kVst3InternalParameterProgram] = false; + sendParameterSetToUI(kVst3InternalParameterProgram, program); } # endif #endif @@ -872,7 +1026,7 @@ public: # if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) - sendStateChangeToUI(key, value); + sendStateSetToUI(key, value); # endif } #endif @@ -880,24 +1034,31 @@ public: else if (queryingType == 'p') { d_stdout("found parameter '%s' '%s'", key.buffer(), value.buffer()); + float fvalue; // find parameter with this symbol, and set its value - for (uint32_t j=0; j < paramCount; ++j) + for (uint32_t j=0; j < fParameterCount; ++j) { if (fPlugin.isParameterOutputOrTrigger(j)) continue; if (fPlugin.getParameterSymbol(j) != key) continue; - const float fvalue = fParameterValues[j] = std::atof(value.buffer()); - fPlugin.setParameterValue(j, fvalue); + if (fPlugin.getParameterHints(j) & kParameterIsInteger) + fvalue = std::atoi(value.buffer()); + else + fvalue = std::atof(value.buffer()); + + // TODO atoi if integer + fCachedParameterValues[kVst3InternalParameterBaseCount + j] = fvalue; #if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) { // UI parameter updates are handled outside the read loop (after host param restart) - fChangedParameterValues[j + fParameterOffset] = true; + fParameterValueChangesForUI[kVst3InternalParameterBaseCount + j] = true; } #endif + fPlugin.setParameterValue(j, fvalue); break; } @@ -909,24 +1070,22 @@ public: } } - if (paramCount != 0) - { - if (fComponentHandler != nullptr) - v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED); + if (fComponentHandler != nullptr) + v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED); #if DISTRHO_PLUGIN_HAS_UI - if (connectedToUI) + if (connectedToUI) + { + for (uint32_t i=0; iseek(stream, 0, V3_SEEK_SET, &ignore); + if (stateCount == 0 && paramCount == 0) { char buffer = '\0'; @@ -1005,6 +1167,7 @@ public: String tmpStr; tmpStr = fPlugin.getParameterSymbol(i); tmpStr += "\xff"; + // TODO cast to integer if needed tmpStr += String(fPlugin.getParameterValue(i)); tmpStr += "\xff"; @@ -1039,16 +1202,24 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_audio_processor interface calls - v3_result setBusArrangements(v3_speaker_arrangement*, int32_t, v3_speaker_arrangement*, int32_t) + v3_result setBusArrangements(v3_speaker_arrangement* /*inputs*/, const int32_t /*numInputs*/, + v3_speaker_arrangement* /*outputs*/, const int32_t /*numOutputs*/) { // TODO return V3_NOT_IMPLEMENTED; } - v3_result getBusArrangement(int32_t, int32_t, v3_speaker_arrangement*) + v3_result getBusArrangement(const int32_t direction, const int32_t /*idx*/, v3_speaker_arrangement*) { - // TODO - return V3_NOT_IMPLEMENTED; + switch (direction) + { + case V3_INPUT: + case V3_OUTPUT: + // TODO + return V3_NOT_IMPLEMENTED; + } + + return V3_INVALID_ARG; } uint32_t getLatencySamples() const noexcept @@ -1069,14 +1240,18 @@ public: // TODO process_mode can be V3_REALTIME, V3_PREFETCH, V3_OFFLINE -#if DISTRHO_PLUGIN_HAS_UI - if (d_isNotEqual(setup->sample_rate, fPlugin.getSampleRate())) - fNextSampleRate = setup->sample_rate; -#endif - fPlugin.setSampleRate(setup->sample_rate, true); fPlugin.setBufferSize(setup->max_block_size, true); + fCachedParameterValues[kVst3InternalParameterBufferSize] = setup->max_block_size; + fParameterValuesChangedDuringProcessing[kVst3InternalParameterBufferSize] = true; + + fCachedParameterValues[kVst3InternalParameterSampleRate] = setup->sample_rate; + fParameterValuesChangedDuringProcessing[kVst3InternalParameterSampleRate] = true; + #if DISTRHO_PLUGIN_HAS_UI + fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = true; + #endif + if (active) fPlugin.activate(); @@ -1094,21 +1269,22 @@ public: } else { - fPlugin.deactivate(); + fPlugin.deactivateIfNeeded(); } + fCachedParameterValues[kVst3InternalParameterActive] = processing ? 1.0f : 0.0f; + fParameterValuesChangedDuringProcessing[kVst3InternalParameterActive] = true; return V3_OK; } v3_result process(v3_process_data* const data) { DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG); + // d_stdout("process %i", data->symbolic_sample_size); + // activate plugin if not done yet if (! fPlugin.isActive()) - { - // host has not activated the plugin yet, nasty! fPlugin.activate(); - } #if DISTRHO_PLUGIN_WANT_TIMEPOS if (v3_process_context* const ctx = data->ctx) @@ -1116,7 +1292,7 @@ public: fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING; fTimePosition.bbt.valid = ctx->state & (V3_PROCESS_CTX_TEMPO_VALID|V3_PROCESS_CTX_TIME_SIG_VALID); - // ticksPerBeat is not possible with VST2 + // ticksPerBeat is not possible with VST3 fTimePosition.bbt.ticksPerBeat = 1920.0; if (ctx->state & V3_PROCESS_CTX_CONT_TIME_VALID) @@ -1168,7 +1344,7 @@ public: if (data->nframes <= 0) { - updateParameterOutputsAndTriggers(); + updateParametersFromProcessing(data->output_params, 0); return V3_OK; } @@ -1289,75 +1465,80 @@ public: } } - // TODO append MIDI events in a sorted way + // TODO append parameter MIDI events in a sorted way + /* +#if DISTRHO_PLUGIN_WANT_PROGRAMS + if (rindex == 0) + continue; + --rindex; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); + midiEvent.frame = offset; + midiEvent.size = 3; + midiEvent.data[0] = (rindex / 130) & 0xf; + + switch (rindex) + { + case 128: // channel pressure + midiEvent.data[0] |= 0xD0; + midiEvent.data[1] = std::max(0, std::min(127, (int)(value * 127))); + midiEvent.data[2] = 0; + midiEvent.data[3] = 0; + break; + case 129: // pitchbend + midiEvent.data[0] |= 0xE0; + midiEvent.data[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f; + midiEvent.data[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7; + midiEvent.data[3] = 0; + break; + default: + midiEvent.data[0] |= 0xB0; + midiEvent.data[1] = rindex % 130; + midiEvent.data[2] = std::max(0, std::min(127, (int)(value * 127))); + midiEvent.data[3] = 0; + break; + } + + if (midiEventCount == kMaxMidiEvents) + break; + } +#endif + */ +#endif + + // if there are any parameter changes at frame 0, set them here if (v3_param_changes** const inparamsptr = data->input_params) { - v3_param_value_queue** queue; int32_t offset; double value; for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i) { - queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i); + v3_param_value_queue** const queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i); DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr); - v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); - DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fRealParameterCount, rindex); + const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); + DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex); - // not supported yet - if (rindex >= fParameterOffset) + if (rindex < kVst3InternalParameterCount) continue; -# if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) + if (v3_cpp_obj(queue)->get_point_count(queue) <= 0) continue; - --rindex; -# endif - - for (int32_t j = 0, pcount = v3_cpp_obj(queue)->get_point_count(queue); j < pcount; ++j) - { - if (v3_cpp_obj(queue)->get_point(queue, j, &offset, &value) != V3_OK) - break; - DISTRHO_SAFE_ASSERT_BREAK(offset < data->nframes); - - MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); - midiEvent.frame = offset; - midiEvent.size = 3; - midiEvent.data[0] = (rindex / 130) & 0xf; - - switch (rindex) - { - case 128: // channel pressure - midiEvent.data[0] |= 0xD0; - midiEvent.data[1] = std::max(0, std::min(127, (int)(value * 127))); - midiEvent.data[2] = 0; - midiEvent.data[3] = 0; - break; - case 129: // pitchbend - midiEvent.data[0] |= 0xE0; - midiEvent.data[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f; - midiEvent.data[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7; - midiEvent.data[3] = 0; - break; - default: - midiEvent.data[0] |= 0xB0; - midiEvent.data[1] = rindex % 130; - midiEvent.data[2] = std::max(0, std::min(127, (int)(value * 127))); - midiEvent.data[3] = 0; - break; - } + if (v3_cpp_obj(queue)->get_point(queue, 0, &offset, &value) != V3_OK) + break; - if (midiEventCount == kMaxMidiEvents) - break; - } + if (offset != 0) + continue; - if (midiEventCount == kMaxMidiEvents) - break; + const uint32_t index = rindex - kVst3InternalParameterCount; + setNormalizedPluginParameterValue(index, value); } } - +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount); #else fPlugin.run(inputs, outputs, data->nframes); @@ -1367,7 +1548,40 @@ public: fHostEventOutputHandle = nullptr; #endif - updateParameterOutputsAndTriggers(); + // if there are any parameter changes after frame 0, set them here + if (v3_param_changes** const inparamsptr = data->input_params) + { + int32_t offset; + double value; + + for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i) + { + v3_param_value_queue** const queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i); + DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr); + + const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); + DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex); + + if (rindex < kVst3InternalParameterCount) + continue; + + const int32_t pcount = v3_cpp_obj(queue)->get_point_count(queue); + + if (pcount <= 0) + continue; + + if (v3_cpp_obj(queue)->get_point(queue, pcount - 1, &offset, &value) != V3_OK) + break; + + if (offset == 0) + continue; + + const uint32_t index = rindex - kVst3InternalParameterCount; + setNormalizedPluginParameterValue(index, value); + } + } + + updateParametersFromProcessing(data->output_params, data->nframes - 1); return V3_OK; } @@ -1379,73 +1593,80 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_edit_controller interface calls -#if 0 - v3_result setComponentState(v3_bstream*, void*) - { - // TODO - return V3_NOT_IMPLEMENTED; - } - - v3_result setState(v3_bstream*, void*) - { - // TODO - return V3_NOT_IMPLEMENTED; - } - - v3_result getState(v3_bstream*, void*) - { - // TODO - return V3_NOT_IMPLEMENTED; - } -#endif - int32_t getParameterCount() const noexcept { - return fRealParameterCount; + return fVst3ParameterCount; } - v3_result getParameterInfo(int32_t rindex, v3_param_info* const info) const noexcept + v3_result getParameterInfo(const int32_t rindex, v3_param_info* const info) const noexcept { - DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INVALID_ARG); std::memset(info, 0, sizeof(v3_param_info)); + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INVALID_ARG); -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) + // TODO hash the parameter symbol + info->param_id = rindex; + + switch (rindex) { - info->param_id = rindex; - info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_LIST | V3_PARAM_PROGRAM_CHANGE; + case kVst3InternalParameterActive: + info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; + info->step_count = 1; + strncpy_utf16(info->title, "Active", 128); + strncpy_utf16(info->short_title, "Active", 128); + return V3_OK; + case kVst3InternalParameterBufferSize: + info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; + info->step_count = DPF_VST3_MAX_BUFFER_SIZE - 1; + strncpy_utf16(info->title, "Buffer Size", 128); + strncpy_utf16(info->short_title, "Buffer Size", 128); + strncpy_utf16(info->units, "frames", 128); + return V3_OK; + case kVst3InternalParameterSampleRate: + info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; + strncpy_utf16(info->title, "Sample Rate", 128); + strncpy_utf16(info->short_title, "Sample Rate", 128); + strncpy_utf16(info->units, "frames", 128); + return V3_OK; + #if DISTRHO_PLUGIN_WANT_LATENCY + case kVst3InternalParameterLatency: + info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; + strncpy_utf16(info->title, "Latency", 128); + strncpy_utf16(info->short_title, "Latency", 128); + strncpy_utf16(info->units, "frames", 128); + return V3_OK; + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: + info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_LIST | V3_PARAM_PROGRAM_CHANGE | V3_PARAM_IS_HIDDEN; info->step_count = fProgramCountMinusOne; strncpy_utf16(info->title, "Current Program", 128); strncpy_utf16(info->short_title, "Program", 128); return V3_OK; + #endif } - --rindex; -#endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex < 130*16) + if (rindex < kVst3InternalParameterCount) { - info->param_id = rindex; -# if DISTRHO_PLUGIN_WANT_PROGRAMS - ++info->param_id; -# endif + const uint32_t index = static_cast(rindex - kVst3InternalParameterMidiCC_start); info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN; info->step_count = 127; char ccstr[24]; - snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", rindex / 130 + 1, rindex % 130); + snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", index / 130 + 1, index % 130); strncpy_utf16(info->title, ccstr, 128); - snprintf(ccstr, sizeof(ccstr)-1, "Ch.%d CC%d", rindex / 130 + 1, rindex % 130); + snprintf(ccstr, sizeof(ccstr)-1, "Ch.%d CC%d", index / 130 + 1, index % 130); strncpy_utf16(info->short_title, ccstr+5, 128); return V3_OK; } - rindex -= 130*16; #endif - const uint32_t index = static_cast(rindex); - DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fPlugin.getParameterCount(), index, V3_INVALID_ARG); + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG); // set up flags int32_t flags = 0; + const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index)); const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); const uint32_t hints = fPlugin.getParameterHints(index); @@ -1462,17 +1683,21 @@ public: flags |= V3_PARAM_CAN_AUTOMATE; if (hints & kParameterIsOutput) flags |= V3_PARAM_READ_ONLY; - // TODO V3_PARAM_IS_LIST // set up step_count int32_t step_count = 0; if (hints & kParameterIsBoolean) step_count = 1; - if ((hints & kParameterIsInteger) && ranges.max - ranges.min > 1) - step_count = ranges.max - ranges.min - 1; + else if (hints & kParameterIsInteger) + step_count = ranges.max - ranges.min; + + if (enumValues.count >= 2 && enumValues.restrictedMode) + { + flags |= V3_PARAM_IS_LIST; + step_count = enumValues.count - 1; + } - info->param_id = index + fParameterOffset; info->flags = flags; info->step_count = step_count; info->default_normalised_value = ranges.getNormalizedValue(ranges.def); @@ -1483,184 +1708,321 @@ public: return V3_OK; } - v3_result getParameterStringForValue(v3_param_id rindex, const double normalised, v3_str_128 output) + v3_result getParameterStringForValue(const v3_param_id rindex, const double normalized, v3_str_128 output) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); - DISTRHO_SAFE_ASSERT_RETURN(normalised >= 0.0 && normalised <= 1.0, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, V3_INVALID_ARG); - -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) + switch (rindex) { - const uint32_t program = std::round(normalised * fProgramCountMinusOne); + case kVst3InternalParameterActive: + strncpy_utf16(output, normalized > 0.5 ? "On" : "Off", 128); + return V3_OK; + case kVst3InternalParameterBufferSize: + snprintf_i32_utf16(output, static_cast(normalized * DPF_VST3_MAX_BUFFER_SIZE + 0.5), 128); + return V3_OK; + case kVst3InternalParameterSampleRate: + snprintf_f32_utf16(output, std::round(normalized * DPF_VST3_MAX_SAMPLE_RATE), 128); + return V3_OK; + #if DISTRHO_PLUGIN_WANT_LATENCY + case kVst3InternalParameterLatency: + snprintf_f32_utf16(output, std::round(normalized * DPF_VST3_MAX_LATENCY), 128); + return V3_OK; + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: + const uint32_t program = std::round(normalized * fProgramCountMinusOne); strncpy_utf16(output, fPlugin.getProgramName(program), 128); return V3_OK; + #endif } - --rindex; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex < 130*16) + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex < kVst3InternalParameterCount) { - snprintf_f32_utf16(output, std::round(normalised * 127), 128); + snprintf_f32_utf16(output, std::round(normalized * 127), 128); return V3_OK; } - rindex -= 130*16; -#endif + #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); - snprintf_f32_utf16(output, ranges.getUnnormalizedValue(normalised), 128); - return V3_OK; - } + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG); - v3_result getParameterValueForString(v3_param_id rindex, int16_t*, double*) - { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); + const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + const uint32_t hints = fPlugin.getParameterHints(index); + float value = ranges.getUnnormalizedValue(normalized); -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) + if (hints & kParameterIsBoolean) { - // TODO find program index based on name - return V3_NOT_IMPLEMENTED; + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + value = value > midRange ? ranges.max : ranges.min; } - --rindex; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex < 130*16) + else if (hints & kParameterIsInteger) { - // TODO - return V3_NOT_IMPLEMENTED; + value = std::round(value); + } + + for (uint32_t i=0; i < enumValues.count; ++i) + { + if (d_isEqual(enumValues.values[i].value, value)) + { + strncpy_utf16(output, enumValues.values[i].label, 128); + return V3_OK; + } } - rindex -= 130*16; -#endif + if (hints & kParameterIsInteger) + snprintf_i32_utf16(output, value, 128); + else + snprintf_f32_utf16(output, value, 128); - // TODO - return V3_NOT_IMPLEMENTED; + return V3_OK; } - double normalisedParameterToPlain(v3_param_id rindex, const double normalised) + v3_result getParameterValueForString(const v3_param_id rindex, int16_t* const input, double* const output) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); + switch (rindex) + { + case kVst3InternalParameterActive: + *output = strcmp_utf16(input, "On") ? 1.0 : 0.0; + return V3_OK; + case kVst3InternalParameterBufferSize: + *output = static_cast(std::atoi(ScopedUTF8String(input))) / DPF_VST3_MAX_BUFFER_SIZE; + return V3_OK; + case kVst3InternalParameterSampleRate: + *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_SAMPLE_RATE; + return V3_OK; + #if DISTRHO_PLUGIN_WANT_LATENCY + case kVst3InternalParameterLatency: + *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_LATENCY; + return V3_OK; + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: + for (uint32_t i=0, count=fPlugin.getProgramCount(); i < count; ++i) + { + if (strcmp_utf16(input, fPlugin.getProgramName(i))) + { + *output = static_cast(i) / static_cast(fProgramCountMinusOne); + return V3_OK; + } + } + return V3_INVALID_ARG; + #endif + } -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) - return std::round(normalised * fProgramCountMinusOne); - --rindex; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex < 130*16) - return std::round(normalised * 127); - rindex -= 130*16; -#endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex < kVst3InternalParameterCount) + { + // TODO find CC/channel based on name + return V3_NOT_IMPLEMENTED; + } + #endif - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); - return ranges.getUnnormalizedValue(normalised); - } + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG); - double plainParameterToNormalised(v3_param_id rindex, const double plain) - { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); + const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) + for (uint32_t i=0; i < enumValues.count; ++i) + { + if (strcmp_utf16(input, enumValues.values[i].label)) + { + *output = ranges.getNormalizedValue(enumValues.values[i].value); + return V3_OK; + } + } + + const ScopedUTF8String input8(input); + + float value; + if (fPlugin.getParameterHints(index) & kParameterIsInteger) + value = std::atoi(input8); + else + value = std::atof(input8); + + *output = ranges.getNormalizedValue(value); + return V3_OK; + } + + double normalizedParameterToPlain(const v3_param_id rindex, const double normalized) + { + DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, 0.0); + + switch (rindex) + { + case kVst3InternalParameterActive: + return normalized > 0.5 ? 1.0 : 0.0; + case kVst3InternalParameterBufferSize: + return std::round(normalized * DPF_VST3_MAX_BUFFER_SIZE); + case kVst3InternalParameterSampleRate: + return normalized * DPF_VST3_MAX_SAMPLE_RATE; + #if DISTRHO_PLUGIN_WANT_LATENCY + case kVst3InternalParameterLatency: + return normalized * DPF_VST3_MAX_LATENCY; + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: + return std::round(normalized * fProgramCountMinusOne); + #endif + } + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex < kVst3InternalParameterCount) + return std::round(normalized * 127); + #endif + + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0); + + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + const uint32_t hints = fPlugin.getParameterHints(index); + float value = ranges.getUnnormalizedValue(normalized); + + if (hints & kParameterIsBoolean) + { + const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + value = value > midRange ? ranges.max : ranges.min; + } + else if (hints & kParameterIsInteger) + { + value = std::round(value); + } + + return value; + } + + double plainParameterToNormalized(const v3_param_id rindex, const double plain) + { + switch (rindex) + { + case kVst3InternalParameterActive: + return plain; + case kVst3InternalParameterBufferSize: + return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_BUFFER_SIZE)); + case kVst3InternalParameterSampleRate: + return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_SAMPLE_RATE)); + #if DISTRHO_PLUGIN_WANT_LATENCY + case kVst3InternalParameterLatency: + return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_LATENCY)); + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: return std::max(0.0, std::min(1.0, plain / fProgramCountMinusOne)); - --rindex; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex < 130*16) + #endif + } + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (rindex < kVst3InternalParameterCount) return std::max(0.0, std::min(1.0, plain / 127)); - rindex -= 130*16; -#endif + #endif + + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0); - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); return ranges.getNormalizedValue(plain); } - double getParameterNormalized(v3_param_id rindex) + double getParameterNormalized(const v3_param_id rindex) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, 0.0); + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + // TODO something to do here? + if (rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end) + return 0.0; + #endif -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) - return std::max(0.0, std::min(1.0, (double)fCurrentProgram / fProgramCountMinusOne)); - --rindex; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex < 130*16) + switch (rindex) { - // TODO - return 0.0; + case kVst3InternalParameterActive: + case kVst3InternalParameterBufferSize: + case kVst3InternalParameterSampleRate: + #if DISTRHO_PLUGIN_WANT_LATENCY + case kVst3InternalParameterLatency: + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: + #endif + return plainParameterToNormalized(rindex, fCachedParameterValues[rindex]); } - rindex -= 130*16; -#endif - const float value = fPlugin.getParameterValue(rindex); - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); - return ranges.getNormalizedValue(value); + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0); + + const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + return ranges.getNormalizedValue(fCachedParameterValues[kVst3InternalParameterBaseCount + index]); } - v3_result setParameterNormalized(v3_param_id rindex, const double value) + v3_result setParameterNormalized(const v3_param_id rindex, const double normalized) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG); - DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, V3_INVALID_ARG); -#if DISTRHO_PLUGIN_HAS_UI - const uint32_t orindex = rindex; -#endif -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + // TODO something to do here? + if (rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end) + return V3_INVALID_ARG; + #endif + + if (rindex < kVst3InternalParameterBaseCount) { - fCurrentProgram = std::round(value * fProgramCountMinusOne); - fPlugin.loadProgram(fCurrentProgram); + fCachedParameterValues[rindex] = normalizedParameterToPlain(rindex, normalized); + int flags = 0; - for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + switch (rindex) { - if (fPlugin.isParameterOutputOrTrigger(i)) - continue; - fParameterValues[i] = fPlugin.getParameterValue(i); + case kVst3InternalParameterActive: + if (fCachedParameterValues[rindex] > 0.5f) + { + if (! fPlugin.isActive()) + fPlugin.activate(); + } + else + { + fPlugin.deactivateIfNeeded(); + } + break; + case kVst3InternalParameterBufferSize: + fPlugin.setBufferSize(fCachedParameterValues[rindex], true); + break; + case kVst3InternalParameterSampleRate: + fPlugin.setSampleRate(fCachedParameterValues[rindex], true); + break; + #if DISTRHO_PLUGIN_WANT_LATENCY + case kVst3InternalParameterLatency: + flags = V3_RESTART_LATENCY_CHANGED; + break; + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: + flags = V3_RESTART_PARAM_VALUES_CHANGED; + fCurrentProgram = fCachedParameterValues[rindex]; + fPlugin.loadProgram(fCurrentProgram); + + for (uint32_t i=0; irestart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED); + if (fComponentHandler != nullptr && flags != 0) + v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, flags); -# if DISTRHO_PLUGIN_HAS_UI - fChangedParameterValues[rindex] = true; -# endif return V3_OK; } - --rindex; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if (rindex < 130*16) - { - // TODO - fChangedParameterValues[rindex] = true; - return V3_NOT_IMPLEMENTED; - } - rindex -= 130*16; -#endif - - const uint32_t hints = fPlugin.getParameterHints(rindex); - const ParameterRanges& ranges(fPlugin.getParameterRanges(rindex)); - float realValue = ranges.getUnnormalizedValue(value); - - if (hints & kParameterIsBoolean) - { - const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; - realValue = realValue > midRange ? ranges.max : ranges.min; - } + const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, V3_INVALID_ARG); - if (hints & kParameterIsInteger) - { - realValue = std::round(realValue); - } - - fParameterValues[rindex] = realValue; - fPlugin.setParameterValue(rindex, realValue); -#if DISTRHO_PLUGIN_HAS_UI - fChangedParameterValues[orindex] = true; -#endif + setNormalizedPluginParameterValue(index, normalized); return V3_OK; } @@ -1674,21 +2036,18 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point interface calls - void connect(v3_connection_point** const other) + #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + void comp2ctrl_connect(v3_connection_point** const other) { - DISTRHO_SAFE_ASSERT(fConnectedToUI == false); - - fConnection = other; - fConnectedToUI = false; + fConnectionFromCompToCtrl = other; } - void disconnect() + void comp2ctrl_disconnect() { - fConnection = nullptr; - fConnectedToUI = false; + fConnectionFromCompToCtrl = nullptr; } - v3_result notify(v3_message** const message) + v3_result comp2ctrl_notify(v3_message** const message) { const char* const msgid = v3_cpp_obj(message)->get_message_id(message); DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG); @@ -1696,97 +2055,119 @@ public: v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (std::strcmp(msgid, "midi") == 0) - { - uint8_t* data; - uint32_t size; - v3_result res; + return notify_midi(attrs); + #endif - res = v3_cpp_obj(attrs)->get_binary(attrs, "data", (const void**)&data, &size); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + #if DISTRHO_PLUGIN_WANT_STATE + if (std::strcmp(msgid, "state-set") == 0) + return notify_state(attrs); + #endif - // known maximum size - DISTRHO_SAFE_ASSERT_UINT_RETURN(size == 3, size, V3_INTERNAL_ERR); + d_stdout("comp2ctrl_notify received unknown msg '%s'", msgid); - return fNotesRingBuffer.writeCustomData(data, size) && fNotesRingBuffer.commitWrite() ? V3_OK : V3_NOMEM; - } -# endif + return V3_NOT_IMPLEMENTED; + } + #endif // DPF_VST3_USES_SEPARATE_CONTROLLER + + // ---------------------------------------------------------------------------------------------------------------- + + void ctrl2view_connect(v3_connection_point** const other) + { + DISTRHO_SAFE_ASSERT(fConnectedToUI == false); + + fConnectionFromCtrlToView = other; + fConnectedToUI = false; + } + + void ctrl2view_disconnect() + { + fConnectedToUI = false; + fConnectionFromCtrlToView = nullptr; + } + + v3_result ctrl2view_notify(v3_message** const message) + { + DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCtrlToView != nullptr, V3_INTERNAL_ERR); + + const char* const msgid = v3_cpp_obj(message)->get_message_id(message); + DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG); if (std::strcmp(msgid, "init") == 0) { - DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR); fConnectedToUI = true; - if (const double sampleRate = fNextSampleRate) - { - fNextSampleRate = 0.0; - sendSampleRateToUI(sampleRate); - } + fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = false; + sendParameterSetToUI(kVst3InternalParameterSampleRate, + fCachedParameterValues[kVst3InternalParameterSampleRate]); -# if DISTRHO_PLUGIN_WANT_PROGRAMS - fChangedParameterValues[0] = false; - sendParameterChangeToUI(0, fCurrentProgram); -# endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + fParameterValueChangesForUI[kVst3InternalParameterProgram] = false; + sendParameterSetToUI(kVst3InternalParameterProgram, fCurrentProgram); + #endif -# if DISTRHO_PLUGIN_WANT_FULL_STATE + #if DISTRHO_PLUGIN_WANT_FULL_STATE // Update current state from plugin side for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; fStateMap[key] = fPlugin.getState(key); } -# endif + #endif -# if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE // Set state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; const String& value = cit->second; - sendStateChangeToUI(key, value); + sendStateSetToUI(key, value); } -# endif + #endif - for (uint32_t i=fParameterOffset; iget_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); + if (std::strcmp(msgid, "idle") == 0) { - DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR); - DISTRHO_SAFE_ASSERT_RETURN(fConnectedToUI, V3_INTERNAL_ERR); - - for (uint32_t i=0, rindex; iget_int(attrs, "rindex", &rindex); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex >= kVst3InternalParameterCount, + rindex, fParameterCount, V3_INTERNAL_ERR); + DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex < kVst3InternalParameterCount + fParameterCount, + rindex, fParameterCount, V3_INTERNAL_ERR); res = v3_cpp_obj(attrs)->get_int(attrs, "started", &started); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - - rindex -= fParameterOffset; - DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + DISTRHO_SAFE_ASSERT_INT_RETURN(started == 0 || started == 1, started, V3_INTERNAL_ERR); return started != 0 ? v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex) : v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex); @@ -1823,89 +2205,142 @@ public: if (std::strcmp(msgid, "parameter-set") == 0) { + DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, V3_INTERNAL_ERR); + int64_t rindex; double value; v3_result res; res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - DISTRHO_SAFE_ASSERT_INT_RETURN(rindex >= fParameterOffset, rindex, V3_INTERNAL_ERR); + DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex >= kVst3InternalParameterCount, + rindex, fParameterCount, V3_INTERNAL_ERR); + DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex < kVst3InternalParameterCount + fParameterCount, + rindex, fParameterCount, V3_INTERNAL_ERR); res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - const uint32_t index = static_cast(rindex - fParameterOffset); - return requestParameterValueChange(index, value) ? V3_OK : V3_INTERNAL_ERR; + const uint32_t index = rindex - kVst3InternalParameterCount; + const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value); + + return v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, rindex, normalized); } -# if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (std::strcmp(msgid, "midi") == 0) + { + #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR); + return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message); + #else + return notify_midi(attrs); + #endif + } + #endif + + #if DISTRHO_PLUGIN_WANT_STATE if (std::strcmp(msgid, "state-set") == 0) { - int64_t keyLength = -1; - int64_t valueLength = -1; - v3_result res; + #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR); + return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message); + #else + return notify_state(attrs); + #endif + } + #endif - res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR); + d_stdout("ctrl2view_notify received unknown msg '%s'", msgid); - res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR); + return V3_NOT_IMPLEMENTED; + } + + #if DISTRHO_PLUGIN_WANT_STATE + v3_result notify_state(v3_attribute_list** const attrs) + { + int64_t keyLength = -1; + int64_t valueLength = -1; + v3_result res; - int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1)); - DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM); + res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR); - int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1)); - DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); + res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR); - res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1)); + DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM); + + int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1)); + DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); + res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength); + DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, keyLength, res); + + if (valueLength != 0) + { res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, valueLength, res); + } - // do cheap inline conversion - char* const key = (char*)key16; - char* const value = (char*)value16; + // do cheap inline conversion + char* const key = (char*)key16; + char* const value = (char*)value16; - for (int64_t i=0; ifirst); + const String& dkey(it->first); - if (dkey == key) - { - it->second = value; - std::free(key16); - std::free(value16); - return V3_OK; - } + if (dkey == key) + { + it->second = value; + std::free(key16); + std::free(value16); + return V3_OK; } - - d_stderr("Failed to find plugin state with key \"%s\"", key); } - std::free(key16); - std::free(value16); - return V3_OK; + d_stderr("Failed to find plugin state with key \"%s\"", key); } -# endif - return V3_NOT_IMPLEMENTED; + std::free(key16); + std::free(value16); + return V3_OK; } + #endif // DISTRHO_PLUGIN_WANT_STATE + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + v3_result notify_midi(v3_attribute_list** const attrs) + { + uint8_t* data; + uint32_t size; + v3_result res; + + res = v3_cpp_obj(attrs)->get_binary(attrs, "data", (const void**)&data, &size); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + // known maximum size + DISTRHO_SAFE_ASSERT_UINT_RETURN(size == 3, size, V3_INTERNAL_ERR); + + return fNotesRingBuffer.writeCustomData(data, size) && fNotesRingBuffer.commitWrite() ? V3_OK : V3_NOMEM; + } + #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT #endif // ---------------------------------------------------------------------------------------------------------------- @@ -1917,18 +2352,21 @@ private: // VST3 stuff v3_component_handler** fComponentHandler; #if DISTRHO_PLUGIN_HAS_UI - v3_connection_point** fConnection; - v3_host_application** const fHostContext; + #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + v3_connection_point** fConnectionFromCompToCtrl; + #endif + v3_connection_point** fConnectionFromCtrlToView; + v3_host_application** const fHostApplication; #endif // Temporary data - const uint32_t fParameterOffset; - const uint32_t fRealParameterCount; // regular parameters + current program - float* fParameterValues; - bool* fChangedParameterValues; + const uint32_t fParameterCount; + const uint32_t fVst3ParameterCount; // full offset + real + float* fCachedParameterValues; // basic offset + real + bool* fParameterValuesChangedDuringProcessing; // basic offset + real #if DISTRHO_PLUGIN_HAS_UI + bool* fParameterValueChangesForUI; // basic offset + real bool fConnectedToUI; - double fNextSampleRate; // if not zero, report to UI #endif #if DISTRHO_PLUGIN_WANT_LATENCY uint32_t fLastKnownLatency; @@ -1956,24 +2394,32 @@ private: // ---------------------------------------------------------------------------------------------------------------- // helper functions called during process, cannot block - void updateParameterOutputsAndTriggers() + void updateParametersFromProcessing(v3_param_changes** const outparamsptr, const int32_t offset) { + DISTRHO_SAFE_ASSERT_RETURN(outparamsptr != nullptr,); + + v3_param_id paramId; float curValue; - for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + for (v3_param_id i=kVst3InternalParameterActive; i<=kVst3InternalParameterSampleRate; ++i) + { + if (! fParameterValuesChangedDuringProcessing[i]) + continue; + + curValue = plainParameterToNormalized(i, fCachedParameterValues[i]); + fParameterValuesChangedDuringProcessing[i] = false; + addParameterDataToHostOutputEvents(outparamsptr, i, curValue); + } + + for (uint32_t i=0; irestart_component(fComponentHandler, V3_RESTART_LATENCY_CHANGED); + curValue = plainParameterToNormalized(kVst3InternalParameterLatency, + fCachedParameterValues[kVst3InternalParameterLatency]); + addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterLatency, curValue); } #endif } + bool addParameterDataToHostOutputEvents(v3_param_changes** const outparamsptr, + v3_param_id paramId, + const float curValue, + const int32_t offset = 0) + { + int32_t index = 0; + v3_param_value_queue** const queue = v3_cpp_obj(outparamsptr)->add_param_data(outparamsptr, + ¶mId, &index); + DISTRHO_SAFE_ASSERT_RETURN(queue != nullptr, false); + DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj(queue)->add_point(queue, 0, curValue, &index) == V3_OK, false); + + if (offset != 0) + v3_cpp_obj(queue)->add_point(queue, offset, curValue, &index); + + return true; + } + #if DISTRHO_PLUGIN_HAS_UI // ---------------------------------------------------------------------------------------------------------------- // helper functions called during message passing, can block v3_message** createMessage(const char* const id) const { - DISTRHO_SAFE_ASSERT_RETURN(fHostContext != nullptr, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(fHostApplication != nullptr, nullptr); v3_tuid iid; memcpy(iid, v3_message_iid, sizeof(v3_tuid)); v3_message** msg = nullptr; - const v3_result res = v3_cpp_obj(fHostContext)->create_instance(fHostContext, iid, iid, (void**)&msg); + const v3_result res = v3_cpp_obj(fHostApplication)->create_instance(fHostApplication, iid, iid, (void**)&msg); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr); DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr); @@ -2028,22 +2503,7 @@ private: return msg; } - void sendSampleRateToUI(const double sampleRate) const - { - v3_message** const message = createMessage("sample-rate"); - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - - v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); - DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); - - v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); - v3_cpp_obj(attrlist)->set_float(attrlist, "value", sampleRate); - v3_cpp_obj(fConnection)->notify(fConnection, message); - - v3_cpp_obj_unref(message); - } - - void sendParameterChangeToUI(const v3_param_id rindex, const double value) const + void sendParameterSetToUI(const v3_param_id rindex, const double value) const { v3_message** const message = createMessage("parameter-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); @@ -2054,12 +2514,12 @@ private: v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex); v3_cpp_obj(attrlist)->set_float(attrlist, "value", value); - v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message); v3_cpp_obj_unref(message); } - void sendStateChangeToUI(const char* const key, const char* const value) const + void sendStateSetToUI(const char* const key, const char* const value) const { v3_message** const message = createMessage("state-set"); DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); @@ -2072,7 +2532,7 @@ private: 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_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message); v3_cpp_obj_unref(message); } @@ -2086,7 +2546,7 @@ private: DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2); - v3_cpp_obj(fConnection)->notify(fConnection, message); + v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message); v3_cpp_obj_unref(message); } @@ -2095,25 +2555,10 @@ private: // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks - bool requestParameterValueChange(const uint32_t index, const float value) + bool requestParameterValueChange(const uint32_t index, float) { - DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); - - const uint32_t rindex = index + fParameterOffset; - const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value); - - const v3_result res_edit = v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex); - DISTRHO_SAFE_ASSERT_INT_RETURN(res_edit == V3_TRUE || res_edit == V3_FALSE, res_edit, res_edit); - - const v3_result res_perf = v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, rindex, normalized); - - if (res_perf == V3_TRUE) - fParameterValues[index] = value; - - if (res_edit == V3_TRUE) - v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex); - - return res_perf == V3_TRUE; + fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + index] = true; + return true; } #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST @@ -2216,62 +2661,61 @@ static uint32_t V3_API dpf_static_unref(void*) { return 0; } // v3_funknown for classes with a single instance template -static uint32_t V3_API dpf_single_instance_ref(void* self) +static uint32_t V3_API dpf_single_instance_ref(void* const self) { - return ++(*(T**)self)->refcounter; + return ++(*static_cast(self))->refcounter; } template -static uint32_t V3_API dpf_single_instance_unref(void* self) +static uint32_t V3_API dpf_single_instance_unref(void* const self) { - return --(*(T**)self)->refcounter; + return --(*static_cast(self))->refcounter; } -#if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- -// dpf_dsp_connection_point +// Store components that we can't delete properly, to be cleaned up on module unload -enum ConnectionPointType { - kConnectionPointComponent, - kConnectionPointController, - kConnectionPointBridge -}; +struct dpf_component; + +static std::vector gComponentGarbage; -static const char* ConnectionPointType2str(const ConnectionPointType type) +static uint32_t handleUncleanComponent(dpf_component** const componentptr) { - switch (type) - { - case kConnectionPointComponent: - return "kConnectionPointComponent"; - case kConnectionPointController: - return "kConnectionPointController"; - case kConnectionPointBridge: - return "kConnectionPointBridge"; - } + gComponentGarbage.push_back(componentptr); + return 0; +} + +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +// -------------------------------------------------------------------------------------------------------------------- +// Store controllers that we can't delete properly, to be cleaned up on module unload - return "[unknown]"; +struct dpf_edit_controller; + +static std::vector gControllerGarbage; + +static uint32_t handleUncleanController(dpf_edit_controller** const controllerptr) +{ + gControllerGarbage.push_back(controllerptr); + return 0; } -struct dpf_dsp_connection_point : v3_connection_point_cpp { +// -------------------------------------------------------------------------------------------------------------------- +// dpf_comp2ctrl_connection_point + +struct dpf_comp2ctrl_connection_point : v3_connection_point_cpp { std::atomic_int refcounter; ScopedPointer& vst3; - const ConnectionPointType type; v3_connection_point** other; - v3_connection_point** bridge; // when type is controller this points to ctrl<->view point - bool shortcircuit; // plugin as controller, should pass directly to view - dpf_dsp_connection_point(const ConnectionPointType t, ScopedPointer& v) + dpf_comp2ctrl_connection_point(ScopedPointer& v) : refcounter(1), vst3(v), - type(t), - other(nullptr), - bridge(nullptr), - shortcircuit(false) + other(nullptr) { // v3_funknown, single instance query_interface = query_interface_connection_point; - ref = dpf_single_instance_ref; - unref = dpf_single_instance_unref; + ref = dpf_single_instance_ref; + unref = dpf_single_instance_unref; // v3_connection_point point.connect = connect; @@ -2282,70 +2726,61 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_connection_point(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_comp2ctrl_connection_point* const point = *static_cast(self); - if (v3_tuid_match(iid, v3_connection_point_iid)) + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_connection_point_iid)) { + d_stdout("dpf_comp2ctrl_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); + ++point->refcounter; *iface = self; return V3_OK; } - *iface = NULL; + d_stdout("dpf_comp2ctrl_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + + *iface = nullptr; return V3_NO_INTERFACE; } // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point - static v3_result V3_API connect(void* self, v3_connection_point** other) + static v3_result V3_API connect(void* const self, v3_connection_point** const other) { - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); - d_stdout("DSP|dpf_dsp_connection_point::connect => %p %p | %d:%s %d", - self, other, point->type, ConnectionPointType2str(point->type), point->shortcircuit); + d_stdout("dpf_comp2ctrl_connection_point::connect => %p %p", self, other); + dpf_comp2ctrl_connection_point* const point = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); - DISTRHO_SAFE_ASSERT(point->bridge == nullptr); + DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG); point->other = other; - if (point->type == kConnectionPointComponent) - if (PluginVst3* const vst3 = point->vst3) - vst3->connect((v3_connection_point**)self); + if (PluginVst3* const vst3 = point->vst3) + vst3->comp2ctrl_connect(other); return V3_OK; } - static v3_result V3_API disconnect(void* self, v3_connection_point** other) + static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) { - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); - d_stdout("DSP|dpf_dsp_connection_point::disconnect => %p %p | %d:%s %d", - self, other, point->type, ConnectionPointType2str(point->type), point->shortcircuit); + d_stdout("dpf_comp2ctrl_connection_point => %p %p", self, other); + dpf_comp2ctrl_connection_point* const point = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG); - if (point->type == kConnectionPointComponent) - if (PluginVst3* const vst3 = point->vst3) - vst3->disconnect(); - - if (point->type == kConnectionPointBridge) - v3_cpp_obj_unref(point->other); + if (PluginVst3* const vst3 = point->vst3) + vst3->comp2ctrl_disconnect(); point->other = nullptr; - point->bridge = nullptr; return V3_OK; } - static v3_result V3_API notify(void* self, v3_message** message) + static v3_result V3_API notify(void* const self, v3_message** const message) { - dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + dpf_comp2ctrl_connection_point* const point = *static_cast(self); PluginVst3* const vst3 = point->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -2359,66 +2794,102 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { int64_t target = 0; const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target); DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res); - DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR); + DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1, target, V3_INTERNAL_ERR); - switch (point->type) - { - // message belongs to component (aka plugin) - case kConnectionPointComponent: - if (target == 1) - { - // view -> edit controller -> component - return vst3->notify(message); - } - else - { - // message is from component to controller to view - return v3_cpp_obj(other)->notify(other, message); - } + // view -> edit controller -> component + return vst3->comp2ctrl_notify(message); + } +}; +#endif // DPF_VST3_USES_SEPARATE_CONTROLLER - // message belongs to edit controller - case kConnectionPointController: - if (target == 1) - { - // we are in view<->dsp short-circuit, all happens in the controller without bridge - if (point->shortcircuit) - return vst3->notify(message); +#if DISTRHO_PLUGIN_HAS_UI +// -------------------------------------------------------------------------------------------------------------------- +// dpf_comp2ctrl_connection_point - // view -> edit controller -> component - return v3_cpp_obj(other)->notify(other, message); - } - else - { - // we are in view<->dsp short-circuit, all happens in the controller without bridge - if (point->shortcircuit) - return v3_cpp_obj(other)->notify(other, message); - - // message is from component to controller to view - v3_connection_point** const bridge = point->bridge; - DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALIZED); - return v3_cpp_obj(bridge)->notify(bridge, message); - } +struct dpf_ctrl2view_connection_point : v3_connection_point_cpp { + ScopedPointer& vst3; + v3_connection_point** other; - // message belongs to bridge (aka ui) - case kConnectionPointBridge: - if (target == 1) - { - // view -> edit controller -> component - v3_connection_point** const bridge = point->bridge; - DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALIZED); - return v3_cpp_obj(bridge)->notify(bridge, message); - } - else - { - // message is from component to controller to view - return v3_cpp_obj(other)->notify(other, message); - } - } + dpf_ctrl2view_connection_point(ScopedPointer& v) + : vst3(v), + other(nullptr) + { + // v3_funknown, single instance, used internally + query_interface = nullptr; + ref = nullptr; + unref = nullptr; - return V3_INTERNAL_ERR; + // v3_connection_point + point.connect = connect; + point.disconnect = disconnect; + point.notify = notify; + } + + // ---------------------------------------------------------------------------------------------------------------- + // v3_connection_point + + static v3_result V3_API connect(void* const self, v3_connection_point** const other) + { + d_stdout("dpf_ctrl2view_connection_point::connect => %p %p", self, other); + dpf_ctrl2view_connection_point* const point = *static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG); + + point->other = other; + + if (PluginVst3* const vst3 = point->vst3) + vst3->ctrl2view_connect(other); + + return V3_OK; + } + + static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) + { + d_stdout("dpf_ctrl2view_connection_point::disconnect => %p %p", self, other); + dpf_ctrl2view_connection_point* const point = *static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG); + + if (PluginVst3* const vst3 = point->vst3) + vst3->ctrl2view_disconnect(); + + v3_cpp_obj_unref(point->other); + point->other = nullptr; + + return V3_OK; + } + + static v3_result V3_API notify(void* const self, v3_message** const message) + { + dpf_ctrl2view_connection_point* const point = *static_cast(self); + + PluginVst3* const vst3 = point->vst3; + DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); + + v3_connection_point** const other = point->other; + DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALIZED); + + v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG); + + int64_t target = 0; + const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target); + DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR); + + if (target == 1) + { + // view -> edit controller + return vst3->ctrl2view_notify(message); + } + else + { + // edit controller -> view + return v3_cpp_obj(other)->notify(other, message); + } } }; -#endif +#endif // DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_WANT_MIDI_INPUT // -------------------------------------------------------------------------------------------------------------------- @@ -2439,46 +2910,38 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_midi_mapping(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_midi_mapping(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_midi_mapping_iid)) { + d_stdout("query_interface_midi_mapping => %p %s %p | OK", self, tuid2str(iid), iface); *iface = self; return V3_OK; } - if (v3_tuid_match(iid, v3_midi_mapping_iid)) - { - *iface = self; - return V3_OK; - } + d_stdout("query_interface_midi_mapping => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); - *iface = NULL; + *iface = nullptr; return V3_NO_INTERFACE; } // ---------------------------------------------------------------------------------------------------------------- // v3_midi_mapping - static v3_result V3_API get_midi_controller_assignment(void*, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id) + static v3_result V3_API get_midi_controller_assignment(void*, const int32_t bus, const int16_t channel, const int16_t cc, v3_param_id* const id) { DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE); DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE); DISTRHO_SAFE_ASSERT_INT_RETURN(cc >= 0 && cc < 130, cc, V3_FALSE); -# if DISTRHO_PLUGIN_WANT_PROGRAMS - static constexpr const v3_param_id offset = 1; -# else - static const constexpr v3_param_id offset = 0; -# endif - - *id = offset + channel * 130 + cc; + *id = kVst3InternalParameterMidiCC_start + channel * 130 + cc; return V3_TRUE; } DISTRHO_PREVENT_HEAP_ALLOCATION }; -#endif +#endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT // -------------------------------------------------------------------------------------------------------------------- // dpf_edit_controller @@ -2486,32 +2949,49 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { struct dpf_edit_controller : v3_edit_controller_cpp { std::atomic_int refcounter; #if DISTRHO_PLUGIN_HAS_UI - ScopedPointer connectionComp; // kConnectionPointController - ScopedPointer connectionBridge; // kConnectionPointBridge + ScopedPointer connectionCtrl2View; #endif +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + ScopedPointer connectionComp2Ctrl; + ScopedPointer vst3; +#else ScopedPointer& vst3; bool initialized; +#endif // cached values v3_component_handler** handler; - v3_host_application** const hostContextFromFactory; - v3_host_application** hostContextFromComponent; - v3_host_application** hostContextFromInitialize; + v3_host_application** const hostApplicationFromFactory; +#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER + v3_host_application** const hostApplicationFromComponent; +#endif + v3_host_application** hostApplicationFromInitialize; - dpf_edit_controller(ScopedPointer& v, v3_host_application** const hf, v3_host_application** const hc) +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + dpf_edit_controller(v3_host_application** const hostApp) + : refcounter(1), + vst3(nullptr), +#else + dpf_edit_controller(ScopedPointer& v, v3_host_application** const hostApp, v3_host_application** const hostComp) : refcounter(1), vst3(v), initialized(false), +#endif handler(nullptr), - hostContextFromFactory(hf), - hostContextFromComponent(hc), - hostContextFromInitialize(nullptr) + hostApplicationFromFactory(hostApp), +#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER + hostApplicationFromComponent(hostComp), +#endif + hostApplicationFromInitialize(nullptr) { - d_stdout("dpf_edit_controller() has contexts %p %p", - hostContextFromFactory, hostContextFromComponent); - - // make sure context is valid through this controller lifetime - if (hostContextFromComponent != nullptr) - v3_cpp_obj_ref(hostContextFromComponent); + d_stdout("dpf_edit_controller() with hostApplication %p", hostApplicationFromFactory); + + // make sure host application is valid through out this controller lifetime + if (hostApplicationFromFactory != nullptr) + v3_cpp_obj_ref(hostApplicationFromFactory); +#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER + if (hostApplicationFromComponent != nullptr) + v3_cpp_obj_ref(hostApplicationFromComponent); +#endif // v3_funknown, everything custom query_interface = query_interface_edit_controller; @@ -2538,61 +3018,46 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ctrl.create_view = create_view; } - void cleanup() + ~dpf_edit_controller() { - if (hostContextFromComponent != nullptr) - { - v3_cpp_obj_unref(hostContextFromComponent); - hostContextFromComponent = nullptr; - } - - d_stdout("dpf_edit_controller::cleanup() has contexts %p %p", - hostContextFromFactory, hostContextFromComponent); + d_stdout("~dpf_edit_controller()"); +#if DISTRHO_PLUGIN_HAS_UI + connectionCtrl2View = nullptr; +#endif +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + connectionComp2Ctrl = nullptr; + vst3 = nullptr; +#endif +#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER + if (hostApplicationFromComponent != nullptr) + v3_cpp_obj_unref(hostApplicationFromComponent); +#endif + if (hostApplicationFromFactory != nullptr) + v3_cpp_obj_unref(hostApplicationFromFactory); } // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_edit_controller(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_edit_controller(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_edit_controller* const controller = *static_cast(self); - if (v3_tuid_match(iid, v3_plugin_base_iid)) - { - *iface = self; - return V3_OK; - } - - if (v3_tuid_match(iid, v3_edit_controller_iid)) - { - *iface = self; - return V3_OK; - } - - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NO_INTERFACE); - -#if DISTRHO_PLUGIN_HAS_UI - if (v3_tuid_match(iid, v3_connection_point_iid)) + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_plugin_base_iid) || + v3_tuid_match(iid, v3_edit_controller_iid)) { - if (controller->connectionComp == nullptr) - controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, - controller->vst3); - else - ++controller->connectionComp->refcounter; - *iface = &controller->connectionComp; + d_stdout("query_interface_edit_controller => %p %s %p | OK", self, tuid2str(iid), iface); + ++controller->refcounter; + *iface = self; return V3_OK; } -#endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (v3_tuid_match(iid, v3_midi_mapping_iid)) { + d_stdout("query_interface_edit_controller => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_midi_mapping midi_mapping; static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; *iface = &midi_mapping_ptr; @@ -2600,19 +3065,38 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } #endif - *iface = NULL; +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + if (v3_tuid_match(iid, v3_connection_point_iid)) + { + d_stdout("query_interface_edit_controller => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, controller->connectionComp2Ctrl.get()); + + if (controller->connectionComp2Ctrl == nullptr) + controller->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(controller->vst3); + else + ++controller->connectionComp2Ctrl->refcounter; + *iface = &controller->connectionComp2Ctrl; + return V3_OK; + } +#endif + + d_stdout("query_interface_edit_controller => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + + *iface = nullptr; return V3_NO_INTERFACE; } - static uint32_t V3_API ref_edit_controller(void* self) + static uint32_t V3_API ref_edit_controller(void* const self) { - d_stdout("dpf_edit_controller::ref => %p", self); - return ++(*(dpf_edit_controller**)self)->refcounter; + dpf_edit_controller* const controller = *static_cast(self); + const int refcount = ++controller->refcounter; + d_stdout("dpf_edit_controller::ref => %p | refcount %i", self, refcount); + return refcount; } - static uint32_t V3_API unref_edit_controller(void* self) + static uint32_t V3_API unref_edit_controller(void* const self) { - dpf_edit_controller** const controllerptr = (dpf_edit_controller**)self; + dpf_edit_controller** const controllerptr = static_cast(self); dpf_edit_controller* const controller = *controllerptr; if (const int refcount = --controller->refcounter) @@ -2621,62 +3105,115 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return refcount; } - d_stdout("dpf_edit_controller::unref => %p is zero, doing cleanup", self); - controller->cleanup(); +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + /** + * Some hosts will have unclean instances of a few of the controller child classes at this point. + * We check for those here, going through the whole possible chain to see if it is safe to delete. + * If not, we add this controller to the `gControllerGarbage` global which will take care of it during unload. + */ + + bool unclean = false; + + if (dpf_comp2ctrl_connection_point* const point = controller->connectionComp2Ctrl) + { + if (const int refcount = point->refcounter) + { + unclean = true; + d_stderr("DPF warning: asked to delete controller while component connection point still active (refcount %d)", refcount); + } + } + + if (unclean) + return handleUncleanController(controllerptr); + + d_stdout("dpf_edit_controller::unref => %p | refcount is zero, deleting everything now!", self); + + delete controller; + delete controllerptr; +#else + d_stdout("dpf_edit_controller::unref => %p | refcount is zero, deletion will be done by component later", self); +#endif return 0; } // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_base - static v3_result V3_API initialize(void* self, v3_plugin_base::v3_funknown** context) + static v3_result V3_API initialize(void* const self, v3_plugin_base::v3_funknown** const context) { - d_stdout("dpf_edit_controller::initialize => %p %p", self, context); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + dpf_edit_controller* const controller = *static_cast(self); - const bool initialized = controller->initialized; - DISTRHO_SAFE_ASSERT_RETURN(! initialized, V3_INVALID_ARG); + // check if already initialized +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 == nullptr, V3_INVALID_ARG); +#else + DISTRHO_SAFE_ASSERT_RETURN(! controller->initialized, V3_INVALID_ARG); +#endif - // query for host context - v3_host_application** host = nullptr; - v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + // query for host application + v3_host_application** hostApplication = nullptr; + if (context != nullptr) + v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication); - d_stdout("dpf_edit_controller::initialize => %p %p | host %p", self, context, host); + d_stdout("dpf_edit_controller::initialize => %p %p | host %p", self, context, hostApplication); // save it for later so we can unref it - controller->hostContextFromInitialize = host; + controller->hostApplicationFromInitialize = hostApplication; +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + // provide the factory application to the plugin if this new one is missing + if (hostApplication == nullptr) + hostApplication = controller->hostApplicationFromFactory; + + // default early values + if (d_nextBufferSize == 0) + d_nextBufferSize = 1024; + if (d_nextSampleRate <= 0.0) + d_nextSampleRate = 44100.0; + + d_nextCanRequestParameterValueChanges = true; + + // create the actual plugin + controller->vst3 = new PluginVst3(hostApplication); + + // set connection point if needed + if (dpf_comp2ctrl_connection_point* const point = controller->connectionComp2Ctrl) + { + if (point->other != nullptr) + controller->vst3->comp2ctrl_connect(point->other); + } +#else + // mark as initialized controller->initialized = true; +#endif + return V3_OK; } static v3_result V3_API terminate(void* self) { - d_stdout("dpf_edit_controller::terminate => %p", self); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_edit_controller::terminate => %p", self); + dpf_edit_controller* const controller = *static_cast(self); - const bool initialized = controller->initialized; - DISTRHO_SAFE_ASSERT_RETURN(initialized, V3_INVALID_ARG); - - controller->initialized = false; +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + // check if already terminated + DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_INVALID_ARG); -#if DISTRHO_PLUGIN_HAS_UI - // take the chance to do some cleanup if possible (we created the bridge, we need to destroy it) - if (controller->connectionBridge != nullptr) - if (controller->connectionBridge->refcounter == 1) - controller->connectionBridge = nullptr; + // delete actual plugin + controller->vst3 = nullptr; +#else + // check if already terminated + DISTRHO_SAFE_ASSERT_RETURN(controller->initialized, V3_INVALID_ARG); - if (controller->connectionComp != nullptr && controller->connectionComp->shortcircuit) - if (controller->connectionComp->refcounter == 1) - controller->connectionComp = nullptr; + // mark as uninitialzed + controller->initialized = false; #endif - if (controller->hostContextFromInitialize != nullptr) + // unref host application received during initialize + if (controller->hostApplicationFromInitialize != nullptr) { - v3_cpp_obj_unref(controller->hostContextFromInitialize); - controller->hostContextFromInitialize = nullptr; + v3_cpp_obj_unref(controller->hostApplicationFromInitialize); + controller->hostApplicationFromInitialize = nullptr; } return V3_OK; @@ -2685,58 +3222,62 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_edit_controller - static v3_result V3_API set_component_state(void* self, v3_bstream* stream) + static v3_result V3_API set_component_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); + +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); -#if 0 - return vst3->setComponentState(stream); -#endif - - // TODO, returning ok to make renoise happy + return vst3->setState(stream); +#else return V3_OK; + + // unused + (void)self; + (void)stream; +#endif } - static v3_result V3_API set_state(void* self, v3_bstream* stream) + static v3_result V3_API set_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); -#if 0 - return vst3->setState(stream); +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + dpf_edit_controller* const controller = *static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED); #endif + return V3_NOT_IMPLEMENTED; + + // maybe unused + (void)self; + (void)stream; } - static v3_result V3_API get_state(void* self, v3_bstream* stream) + static v3_result V3_API get_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); - - PluginVst3* const vst3 = controller->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); -#if 0 - return vst3->getState(stream); +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + dpf_edit_controller* const controller = *static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED); #endif + return V3_NOT_IMPLEMENTED; + + // maybe unused + (void)self; + (void)stream; } static int32_t V3_API get_parameter_count(void* self) { - // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + // d_stdout("dpf_edit_controller::get_parameter_count => %p", self); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -2746,9 +3287,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API get_parameter_info(void* self, int32_t param_idx, v3_param_info* param_info) { - // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -2760,8 +3300,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { // NOTE very noisy, called many times // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -2772,8 +3311,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output) { d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -2783,32 +3321,29 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static double V3_API normalised_parameter_to_plain(void* self, v3_param_id index, double normalised) { - d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->normalisedParameterToPlain(index, normalised); + return vst3->normalizedParameterToPlain(index, normalised); } static double V3_API plain_parameter_to_normalised(void* self, v3_param_id index, double plain) { - d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->plainParameterToNormalised(index, plain); + return vst3->plainParameterToNormalized(index, plain); } static double V3_API get_parameter_normalised(void* self, v3_param_id index) { - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0); @@ -2816,10 +3351,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return vst3->getParameterNormalized(index); } - static v3_result V3_API set_parameter_normalised(void* self, v3_param_id index, double normalised) + static v3_result V3_API set_parameter_normalised(void* const self, const v3_param_id index, const double normalised) { - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + // d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised); + dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -2829,9 +3364,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API set_component_handler(void* self, v3_component_handler** handler) { - d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler); + dpf_edit_controller* const controller = *static_cast(self); controller->handler = handler; @@ -2843,45 +3377,32 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_plugin_view** V3_API create_view(void* self, const char* name) { - d_stdout("dpf_edit_controller::create_view => %p %s", self, name); - dpf_edit_controller* const controller = *(dpf_edit_controller**)self; - DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, nullptr); + d_stdout("dpf_edit_controller::create_view => %p %s", self, name); + dpf_edit_controller* const controller = *static_cast(self); d_stdout("create_view has contexts %p %p", - controller->hostContextFromFactory, controller->hostContextFromComponent); + controller->hostApplicationFromFactory, controller->hostApplicationFromInitialize); #if DISTRHO_PLUGIN_HAS_UI // plugin must be initialized PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); - d_stdout("dpf_edit_controller::create_view => %p %s | edit-ctrl %p, host %p, factory %p", + d_stdout("dpf_edit_controller::create_view => %p %s | edit-ctrl %p, factory %p", self, name, - controller->hostContextFromInitialize, - controller->hostContextFromComponent, - controller->hostContextFromFactory); - - // we require a host context for message creation - v3_host_application** host = controller->hostContextFromInitialize != nullptr - ? controller->hostContextFromInitialize - : controller->hostContextFromComponent != nullptr - ? controller->hostContextFromComponent - : controller->hostContextFromFactory; + controller->hostApplicationFromInitialize, + controller->hostApplicationFromFactory); + + // we require a host application for message creation + v3_host_application** const host = controller->hostApplicationFromInitialize != nullptr + ? controller->hostApplicationFromInitialize +#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER + : controller->hostApplicationFromComponent != nullptr + ? controller->hostApplicationFromComponent +#endif + : controller->hostApplicationFromFactory; DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); - // if there is a component connection, we require it to be active - if (controller->connectionComp != nullptr) - { - DISTRHO_SAFE_ASSERT_RETURN(controller->connectionComp->other != nullptr, nullptr); - } - // otherwise short-circuit and deal with this ourselves (assume local usage) - else - { - controller->connectionComp = new dpf_dsp_connection_point(kConnectionPointController, - controller->vst3); - controller->connectionComp->shortcircuit = true; - } - v3_plugin_view** const view = dpf_plugin_view_create(host, vst3->getInstancePointer(), vst3->getSampleRate()); @@ -2890,32 +3411,17 @@ struct dpf_edit_controller : v3_edit_controller_cpp { v3_connection_point** uiconn = nullptr; if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &uiconn) == V3_OK) { - d_stdout("view connection query ok %p | shortcircuit %d", - uiconn, controller->connectionComp->shortcircuit); - - v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionComp; - - if (controller->connectionComp->shortcircuit) - { - vst3->disconnect(); - - v3_cpp_obj(uiconn)->connect(uiconn, ctrlconn); - v3_cpp_obj(ctrlconn)->connect(ctrlconn, uiconn); - - vst3->connect(ctrlconn); - } - else - { - controller->connectionBridge = new dpf_dsp_connection_point(kConnectionPointBridge, - controller->vst3); + d_stdout("view connection query ok %p", uiconn); + controller->connectionCtrl2View = new dpf_ctrl2view_connection_point(controller->vst3); - v3_connection_point** const bridgeconn = (v3_connection_point**)&controller->connectionBridge; - v3_cpp_obj(uiconn)->connect(uiconn, bridgeconn); - v3_cpp_obj(bridgeconn)->connect(bridgeconn, uiconn); + v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionCtrl2View; - controller->connectionComp->bridge = bridgeconn; - controller->connectionBridge->bridge = ctrlconn; - } + v3_cpp_obj(uiconn)->connect(uiconn, ctrlconn); + v3_cpp_obj(ctrlconn)->connect(ctrlconn, uiconn); + } + else + { + controller->connectionCtrl2View = nullptr; } return view; @@ -2943,21 +3449,19 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_process_context_requirements(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_process_context_requirements(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_process_context_requirements_iid)) { + d_stdout("query_interface_process_context_requirements => %p %s %p | OK", self, tuid2str(iid), iface); *iface = self; return V3_OK; } - if (v3_tuid_match(iid, v3_process_context_requirements_iid)) - { - *iface = self; - return V3_OK; - } + d_stdout("query_interface_process_context_requirements => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); - *iface = NULL; + *iface = nullptr; return V3_NO_INTERFACE; } @@ -2968,11 +3472,11 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { { #if DISTRHO_PLUGIN_WANT_TIMEPOS return 0x0 - |V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID - |V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID - |V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID - |V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID - |V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING + | V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID + | V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID + | V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID + | V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID + | V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING #else return 0x0; #endif @@ -3011,43 +3515,45 @@ struct dpf_audio_processor : v3_audio_processor_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_audio_processor(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_audio_processor(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_audio_processor* const processor = *static_cast(self); - if (v3_tuid_match(iid, v3_audio_processor_iid)) + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_audio_processor_iid)) { + d_stdout("query_interface_audio_processor => %p %s %p | OK", self, tuid2str(iid), iface); + ++processor->refcounter; *iface = self; return V3_OK; } if (v3_tuid_match(iid, v3_process_context_requirements_iid)) { + d_stdout("query_interface_audio_processor => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_process_context_requirements context_req; static dpf_process_context_requirements* context_req_ptr = &context_req; *iface = &context_req_ptr; return V3_OK; } - *iface = NULL; + d_stdout("query_interface_audio_processor => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + + *iface = nullptr; return V3_NO_INTERFACE; } // ---------------------------------------------------------------------------------------------------------------- // v3_audio_processor - static v3_result V3_API set_bus_arrangements(void* self, - v3_speaker_arrangement* inputs, int32_t num_inputs, - v3_speaker_arrangement* outputs, int32_t num_outputs) + static v3_result V3_API set_bus_arrangements(void* const self, + v3_speaker_arrangement* const inputs, const int32_t num_inputs, + v3_speaker_arrangement* const outputs, const int32_t num_outputs) { // NOTE this is called a bunch of times - // d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", self, inputs, num_inputs, outputs, num_outputs); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); + // d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", + // self, inputs, num_inputs, outputs, num_outputs); + dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3055,12 +3561,12 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->setBusArrangements(inputs, num_inputs, outputs, num_outputs); } - static v3_result V3_API get_bus_arrangement(void* self, int32_t bus_direction, - int32_t idx, v3_speaker_arrangement* arr) + static v3_result V3_API get_bus_arrangement(void* const self, const int32_t bus_direction, + const int32_t idx, v3_speaker_arrangement* const arr) { - d_stdout("dpf_audio_processor::get_bus_arrangement => %p %i %i %p", self, bus_direction, idx, arr); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_audio_processor::get_bus_arrangement => %p %s %p", + self, v3_bus_direction_str(bus_direction), idx, arr); + dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3068,17 +3574,17 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->getBusArrangement(bus_direction, idx, arr); } - static v3_result V3_API can_process_sample_size(void* self, int32_t symbolic_sample_size) + static v3_result V3_API can_process_sample_size(void*, const int32_t symbolic_sample_size) { - d_stdout("dpf_audio_processor::can_process_sample_size => %p %i", self, symbolic_sample_size); + // NOTE runs during RT + // d_stdout("dpf_audio_processor::can_process_sample_size => %i", symbolic_sample_size); return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED; } - static uint32_t V3_API get_latency_samples(void* self) + static uint32_t V3_API get_latency_samples(void* const self) { - d_stdout("dpf_audio_processor::get_latency_samples => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + d_stdout("dpf_audio_processor::get_latency_samples => %p", self); + dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); @@ -3086,11 +3592,10 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->getLatencySamples(); } - static v3_result V3_API setup_processing(void* self, v3_process_setup* setup) + static v3_result V3_API setup_processing(void* const self, v3_process_setup* const setup) { - d_stdout("dpf_audio_processor::setup_processing => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_audio_processor::setup_processing => %p %p", self, setup); + dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3100,11 +3605,10 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->setupProcessing(setup); } - static v3_result V3_API set_processing(void* self, v3_bool state) + static v3_result V3_API set_processing(void* const self, const v3_bool state) { - d_stdout("dpf_audio_processor::set_processing => %p %u", self, state); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_audio_processor::set_processing => %p %u", self, state); + dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3112,12 +3616,11 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->setProcessing(state); } - static v3_result V3_API process(void* self, v3_process_data* data) + static v3_result V3_API process(void* const self, v3_process_data* const data) { // NOTE runs during RT - // d_stdout("dpf_audio_processor::process => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, V3_NOT_INITIALIZED); + // d_stdout("dpf_audio_processor::process => %p", self); + dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3125,11 +3628,10 @@ struct dpf_audio_processor : v3_audio_processor_cpp { return processor->vst3->process(data); } - static uint32_t V3_API get_tail_samples(void* self) + static uint32_t V3_API get_tail_samples(void* const self) { - d_stdout("dpf_audio_processor::get_tail_samples => %p", self); - dpf_audio_processor* const processor = *(dpf_audio_processor**)self; - DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, 0); + d_stdout("dpf_audio_processor::get_tail_samples => %p", self); + dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0); @@ -3138,41 +3640,31 @@ struct dpf_audio_processor : v3_audio_processor_cpp { } }; -// -------------------------------------------------------------------------------------------------------------------- -// Store components that we can't delete properly, to be cleaned up on module unload - -struct dpf_component; - -std::vector gComponentGarbage; - -static v3_result handleUncleanComponent(dpf_component** const componentptr) -{ - gComponentGarbage.push_back(componentptr); - return V3_INVALID_ARG; -} - // -------------------------------------------------------------------------------------------------------------------- // dpf_component struct dpf_component : v3_component_cpp { std::atomic_int refcounter; ScopedPointer processor; -#if DISTRHO_PLUGIN_HAS_UI - ScopedPointer connection; // kConnectionPointComponent -#endif +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + ScopedPointer connectionComp2Ctrl; +#else ScopedPointer controller; +#endif ScopedPointer vst3; - v3_host_application** const hostContextFromFactory; - v3_host_application** hostContextFromInitialize; + v3_host_application** const hostApplicationFromFactory; + v3_host_application** hostApplicationFromInitialize; - dpf_component(v3_host_application** const h) + dpf_component(v3_host_application** const host) : refcounter(1), - hostContextFromFactory(h), - hostContextFromInitialize(nullptr) + hostApplicationFromFactory(host), + hostApplicationFromInitialize(nullptr) { - // make sure context is valid through this component lifetime - if (hostContextFromFactory != nullptr) - v3_cpp_obj_ref(hostContextFromFactory); + d_stdout("dpf_component() with hostApplication %p", hostApplicationFromFactory); + + // make sure host application is valid through out this component lifetime + if (hostApplicationFromFactory != nullptr) + v3_cpp_obj_ref(hostApplicationFromFactory); // v3_funknown, everything custom query_interface = query_interface_component; @@ -3195,51 +3687,54 @@ struct dpf_component : v3_component_cpp { comp.get_state = get_state; } - void cleanup() + ~dpf_component() { - vst3 = nullptr; + d_stdout("~dpf_component()"); processor = nullptr; -#if DISTRHO_PLUGIN_HAS_UI - connection = nullptr; +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + connectionComp2Ctrl = nullptr; +#else + controller = nullptr; #endif - if (controller != nullptr) - { - controller->cleanup(); - controller = nullptr; - } + vst3 = nullptr; - if (hostContextFromFactory != nullptr) - v3_cpp_obj_unref(hostContextFromFactory); + if (hostApplicationFromFactory != nullptr) + v3_cpp_obj_unref(hostApplicationFromFactory); } // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_component(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_component(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_component* const component = *static_cast(self); - if (v3_tuid_match(iid, v3_plugin_base_iid)) + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_plugin_base_iid) || + v3_tuid_match(iid, v3_component_iid)) { + d_stdout("query_interface_component => %p %s %p | OK", self, tuid2str(iid), iface); + ++component->refcounter; *iface = self; return V3_OK; } - if (v3_tuid_match(iid, v3_component_iid)) +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (v3_tuid_match(iid, v3_midi_mapping_iid)) { - *iface = self; + d_stdout("query_interface_component => %p %s %p | OK convert static", self, tuid2str(iid), iface); + static dpf_midi_mapping midi_mapping; + static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; + *iface = &midi_mapping_ptr; return V3_OK; } - - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NO_INTERFACE); +#endif if (v3_tuid_match(iid, v3_audio_processor_iid)) { + d_stdout("query_interface_component => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, component->processor.get()); + if (component->processor == nullptr) component->processor = new dpf_audio_processor(component->vst3); else @@ -3248,51 +3743,58 @@ struct dpf_component : v3_component_cpp { return V3_OK; } -#if DISTRHO_PLUGIN_HAS_UI +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER if (v3_tuid_match(iid, v3_connection_point_iid)) { - if (component->connection == nullptr) - component->connection = new dpf_dsp_connection_point(kConnectionPointComponent, - component->vst3); + d_stdout("query_interface_component => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, component->connectionComp2Ctrl.get()); + + if (component->connectionComp2Ctrl == nullptr) + component->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(component->vst3); else - ++component->connection->refcounter; - *iface = &component->connection; + ++component->connectionComp2Ctrl->refcounter; + *iface = &component->connectionComp2Ctrl; return V3_OK; } -#endif - +#else if (v3_tuid_match(iid, v3_edit_controller_iid)) { - d_stdout("query_interface_component called with contexts %p %p", - component->hostContextFromFactory, component->hostContextFromInitialize); + d_stdout("query_interface_component => %p %s %p | OK convert %p", + self, tuid2str(iid), iface, component->controller.get()); if (component->controller == nullptr) component->controller = new dpf_edit_controller(component->vst3, - component->hostContextFromFactory, - component->hostContextFromInitialize); + component->hostApplicationFromFactory, + component->hostApplicationFromInitialize); else ++component->controller->refcounter; *iface = &component->controller; return V3_OK; } +#endif - *iface = NULL; + d_stdout("query_interface_component => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + + *iface = nullptr; return V3_NO_INTERFACE; } - static uint32_t V3_API ref_component(void* self) + static uint32_t V3_API ref_component(void* const self) { - return ++(*(dpf_component**)self)->refcounter; + dpf_component* const component = *static_cast(self); + const int refcount = ++component->refcounter; + d_stdout("dpf_component::ref => %p | refcount %i", self, refcount); + return refcount; } - static uint32_t V3_API unref_component(void* self) + static uint32_t V3_API unref_component(void* const self) { - dpf_component** const componentptr = (dpf_component**)self; + dpf_component** const componentptr = static_cast(self); dpf_component* const component = *componentptr; if (const int refcount = --component->refcounter) { - d_stdout("dpf_component::unref => %p | refcount %i", self, refcount); + d_stdout("dpf_component::unref => %p | refcount %i", self, refcount); return refcount; } @@ -3303,6 +3805,7 @@ struct dpf_component : v3_component_cpp { */ bool unclean = false; + if (dpf_audio_processor* const proc = component->processor) { if (const int refcount = proc->refcounter) @@ -3312,53 +3815,30 @@ struct dpf_component : v3_component_cpp { } } -#if DISTRHO_PLUGIN_HAS_UI - if (dpf_dsp_connection_point* const conn = component->connection) +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl) { - if (const int refcount = conn->refcounter) + if (const int refcount = point->refcounter) { unclean = true; d_stderr("DPF warning: asked to delete component while connection point still active (refcount %d)", refcount); } } -#endif - - if (dpf_edit_controller* const ctrl = component->controller) +#else + if (dpf_edit_controller* const controller = component->controller) { - if (const int refcount = ctrl->refcounter) + if (const int refcount = controller->refcounter) { unclean = true; d_stderr("DPF warning: asked to delete component while edit controller still active (refcount %d)", refcount); } - -#if DISTRHO_PLUGIN_HAS_UI - if (dpf_dsp_connection_point* const comp = ctrl->connectionComp) - { - if (const int refcount = comp->refcounter) - { - unclean = true; - d_stderr("DPF warning: asked to delete component while edit controller connection point still active (refcount %d)", refcount); - } - } - - if (dpf_dsp_connection_point* const bridge = ctrl->connectionBridge) - { - if (const int refcount = bridge->refcounter) - { - unclean = true; - d_stderr("DPF warning: asked to delete component while view bridge connection still active (refcount %d)", refcount); - } - } -#endif } +#endif if (unclean) return handleUncleanComponent(componentptr); - d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); - - if (component->hostContextFromFactory != nullptr) - v3_cpp_obj_unref(component->hostContextFromFactory); + d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self); delete component; delete componentptr; @@ -3368,52 +3848,66 @@ struct dpf_component : v3_component_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_base - static v3_result V3_API initialize(void* self, v3_plugin_base::v3_funknown** context) + static v3_result V3_API initialize(void* const self, v3_plugin_base::v3_funknown** const context) { - d_stdout("dpf_component::initialize => %p %p", self, context); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + dpf_component* const component = *static_cast(self); + + // check if already initialized DISTRHO_SAFE_ASSERT_RETURN(component->vst3 == nullptr, V3_INVALID_ARG); - // query for host context - v3_host_application** host = nullptr; + // query for host application + v3_host_application** hostApplication = nullptr; if (context != nullptr) - v3_cpp_obj_query_interface(context, v3_host_application_iid, &host); + v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication); - d_stdout("dpf_component::initialize => %p %p | host %p", self, context, host); + d_stdout("dpf_component::initialize => %p %p | hostApplication %p", self, context, hostApplication); // save it for later so we can unref it - component->hostContextFromInitialize = host; + component->hostApplicationFromInitialize = hostApplication; - // provide the factory context to the plugin if this new one is invalid - if (host == nullptr) - host = component->hostContextFromFactory; + // provide the factory application to the plugin if this new one is missing + if (hostApplication == nullptr) + hostApplication = component->hostApplicationFromFactory; // default early values if (d_nextBufferSize == 0) - d_nextBufferSize = 2048; + d_nextBufferSize = 1024; if (d_nextSampleRate <= 0.0) d_nextSampleRate = 44100.0; d_nextCanRequestParameterValueChanges = true; - component->vst3 = new PluginVst3(host); + // create the actual plugin + component->vst3 = new PluginVst3(hostApplication); + + #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + // set connection point if needed + if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl) + { + if (point->other != nullptr) + component->vst3->comp2ctrl_connect(point->other); + } + #endif + return V3_OK; } - static v3_result V3_API terminate(void* self) + static v3_result V3_API terminate(void* const self) { - d_stdout("dpf_component::terminate => %p", self); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::terminate => %p", self); + dpf_component* const component = *static_cast(self); + + // check if already terminated DISTRHO_SAFE_ASSERT_RETURN(component->vst3 != nullptr, V3_INVALID_ARG); + // delete actual plugin component->vst3 = nullptr; - if (component->hostContextFromInitialize != nullptr) + // unref host application received during initialize + if (component->hostApplicationFromInitialize != nullptr) { - v3_cpp_obj_unref(component->hostContextFromInitialize); - component->hostContextFromInitialize = nullptr; + v3_cpp_obj_unref(component->hostApplicationFromInitialize); + component->hostApplicationFromInitialize = nullptr; } return V3_OK; @@ -3422,24 +3916,19 @@ struct dpf_component : v3_component_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_component - static v3_result V3_API get_controller_class_id(void* self, v3_tuid class_id) + static v3_result V3_API get_controller_class_id(void*, v3_tuid class_id) { - d_stdout("dpf_component::get_controller_class_id => %p %s", self, tuid2str(class_id)); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); - - PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::get_controller_class_id => %p", class_id); - // TODO - return V3_NOT_IMPLEMENTED; + std::memcpy(class_id, dpf_tuid_controller, sizeof(v3_tuid)); + return V3_OK; } - static v3_result V3_API set_io_mode(void* self, int32_t io_mode) + static v3_result V3_API set_io_mode(void* const self, const int32_t io_mode) { - d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode); + + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3448,25 +3937,27 @@ struct dpf_component : v3_component_cpp { return V3_NOT_IMPLEMENTED; } - static int32_t V3_API get_bus_count(void* self, int32_t media_type, int32_t bus_direction) + static int32_t V3_API get_bus_count(void* const self, const int32_t media_type, const int32_t bus_direction) { // NOTE runs during RT - // d_stdout("dpf_component::get_bus_count => %p %i %i", self, media_type, bus_direction); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + // d_stdout("dpf_component::get_bus_count => %p %s %s", + // self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction)); + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); - return vst3->getBusCount(media_type, bus_direction); + const int32_t ret = vst3->getBusCount(media_type, bus_direction); + // d_stdout("dpf_component::get_bus_count returns %i", ret); + return ret; } - static v3_result V3_API get_bus_info(void* self, int32_t media_type, int32_t bus_direction, - int32_t bus_idx, v3_bus_info* info) + static v3_result V3_API get_bus_info(void* const self, const int32_t media_type, const int32_t bus_direction, + const int32_t bus_idx, v3_bus_info* const info) { - d_stdout("dpf_component::get_bus_info => %p %i %i %i %p", self, media_type, bus_direction, bus_idx, info); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::get_bus_info => %p %s %s %i %p", + self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, info); + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3474,11 +3965,10 @@ struct dpf_component : v3_component_cpp { return vst3->getBusInfo(media_type, bus_direction, bus_idx, info); } - static v3_result V3_API get_routing_info(void* self, v3_routing_info* input, v3_routing_info* output) + static v3_result V3_API get_routing_info(void* const self, v3_routing_info* const input, v3_routing_info* const output) { - d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output); + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3486,13 +3976,13 @@ struct dpf_component : v3_component_cpp { return vst3->getRoutingInfo(input, output); } - static v3_result V3_API activate_bus(void* self, int32_t media_type, int32_t bus_direction, - int32_t bus_idx, v3_bool state) + static v3_result V3_API activate_bus(void* const self, const int32_t media_type, const int32_t bus_direction, + const int32_t bus_idx, const v3_bool state) { // NOTE this is called a bunch of times - // d_stdout("dpf_component::activate_bus => %p %i %i %i %u", self, media_type, bus_direction, bus_idx, state); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + // d_stdout("dpf_component::activate_bus => %p %s %s %i %u", + // self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, state); + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3500,11 +3990,10 @@ struct dpf_component : v3_component_cpp { return vst3->activateBus(media_type, bus_direction, bus_idx, state); } - static v3_result V3_API set_active(void* self, v3_bool state) + static v3_result V3_API set_active(void* const self, const v3_bool state) { - d_stdout("dpf_component::set_active => %p %u", self, state); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::set_active => %p %u", self, state); + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3512,11 +4001,10 @@ struct dpf_component : v3_component_cpp { return component->vst3->setActive(state); } - static v3_result V3_API set_state(void* self, v3_bstream** stream) + static v3_result V3_API set_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_component::set_state => %p", self); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::set_state => %p", self); + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3524,11 +4012,10 @@ struct dpf_component : v3_component_cpp { return vst3->setState(stream); } - static v3_result V3_API get_state(void* self, v3_bstream** stream) + static v3_result V3_API get_state(void* const self, v3_bstream** const stream) { - d_stdout("dpf_component::get_state => %p %p", self, stream); - dpf_component* const component = *(dpf_component**)self; - DISTRHO_SAFE_ASSERT_RETURN(component != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_component::get_state => %p %p", self, stream); + dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); @@ -3542,7 +4029,7 @@ struct dpf_component : v3_component_cpp { static const PluginExporter& _getPluginInfo() { - d_nextBufferSize = 512; + d_nextBufferSize = 1024; d_nextSampleRate = 44100.0; d_nextPluginIsDummy = true; d_nextCanRequestParameterValueChanges = true; @@ -3612,19 +4099,19 @@ static const char* getPluginVersion() // dpf_factory struct dpf_factory : v3_plugin_factory_cpp { + std::atomic_int refcounter; + // cached values v3_funknown** hostContext; dpf_factory() - : hostContext(nullptr) + : refcounter(1), + hostContext(nullptr) { - dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] - = dpf_tuid_processor[2] = dpf_tuid_view[2] = getPluginInfo().getUniqueId(); - // v3_funknown, static query_interface = query_interface_factory; - ref = dpf_static_ref; - unref = dpf_static_unref; + ref = ref_factory; + unref = unref_factory; // v3_plugin_factory v1.get_factory_info = get_factory_info; @@ -3646,55 +4133,89 @@ struct dpf_factory : v3_plugin_factory_cpp { if (hostContext != nullptr) v3_cpp_obj_unref(hostContext); - if (gComponentGarbage.size() == 0) - return; + #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + if (gControllerGarbage.size() != 0) + { + d_stdout("DPF notice: cleaning up previously undeleted controllers now"); + + for (std::vector::iterator it = gControllerGarbage.begin(); + it != gControllerGarbage.end(); ++it) + { + dpf_edit_controller** const controllerptr = *it; + dpf_edit_controller* const controller = *controllerptr; + delete controller; + delete controllerptr; + } - d_stdout("DPF notice: cleaning up previously undeleted components now"); + gControllerGarbage.clear(); + } + #endif - for (std::vector::iterator it = gComponentGarbage.begin(); - it != gComponentGarbage.end(); ++it) + if (gComponentGarbage.size() != 0) { - dpf_component** const componentptr = *it; - dpf_component* const component = *componentptr; - component->cleanup(); - delete component; - delete componentptr; - } + d_stdout("DPF notice: cleaning up previously undeleted components now"); - gComponentGarbage.clear(); + for (std::vector::iterator it = gComponentGarbage.begin(); + it != gComponentGarbage.end(); ++it) + { + dpf_component** const componentptr = *it; + dpf_component* const component = *componentptr; + delete component; + delete componentptr; + } + + gComponentGarbage.clear(); + } } // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_factory(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_factory(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_factory* const factory = *static_cast(self); - if (v3_tuid_match(iid, v3_plugin_factory_iid)) + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_plugin_factory_iid) || + v3_tuid_match(iid, v3_plugin_factory_2_iid) || + v3_tuid_match(iid, v3_plugin_factory_3_iid)) { + d_stdout("query_interface_factory => %p %s %p | OK", self, tuid2str(iid), iface); + ++factory->refcounter; *iface = self; return V3_OK; } - if (v3_tuid_match(iid, v3_plugin_factory_2_iid)) - { - *iface = self; - return V3_OK; - } + d_stdout("query_interface_factory => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + + *iface = nullptr; + return V3_NO_INTERFACE; + } + + static uint32_t V3_API ref_factory(void* const self) + { + dpf_factory* const factory = *static_cast(self); + const int refcount = ++factory->refcounter; + d_stdout("ref_factory::ref => %p | refcount %i", self, refcount); + return refcount; + } - if (v3_tuid_match(iid, v3_plugin_factory_3_iid)) + static uint32_t V3_API unref_factory(void* const self) + { + dpf_factory** const factoryptr = static_cast(self); + dpf_factory* const factory = *factoryptr; + + if (const int refcount = --factory->refcounter) { - *iface = self; - return V3_OK; + d_stdout("unref_factory::unref => %p | refcount %i", self, refcount); + return refcount; } - *iface = NULL; - return V3_NO_INTERFACE; + d_stdout("unref_factory::unref => %p | refcount is zero, deleting factory", self); + + delete factory; + delete factoryptr; + return 0; } // ---------------------------------------------------------------------------------------------------------------- @@ -3702,70 +4223,90 @@ struct dpf_factory : v3_plugin_factory_cpp { static v3_result V3_API get_factory_info(void*, v3_factory_info* const info) { + d_stdout("dpf_factory::get_factory_info => %p", info); std::memset(info, 0, sizeof(*info)); + + info->flags = 0x10; DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->url, getPluginInfo().getHomePage(), ARRAY_SIZE(info->url)); // DISTRHO_NAMESPACE::strncpy(info->email, "", ARRAY_SIZE(info->email)); // TODO - info->flags = 0x10; return V3_OK; } static int32_t V3_API num_classes(void*) { + d_stdout("dpf_factory::num_classes"); return 1; } - static v3_result V3_API get_class_info(void*, int32_t idx, v3_class_info* const info) + static v3_result V3_API get_class_info(void*, const int32_t idx, v3_class_info* const info) { + d_stdout("dpf_factory::get_class_info => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); return V3_OK; } - static v3_result V3_API create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) + static v3_result V3_API create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** const instance) { d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance); - DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && - v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); - - dpf_factory* const factory = *(dpf_factory**)self; - DISTRHO_SAFE_ASSERT_RETURN(factory != nullptr, V3_NOT_INITIALIZED); + dpf_factory* const factory = *static_cast(self); - // query for host context - v3_host_application** host = nullptr; + // query for host application + v3_host_application** hostApplication = nullptr; if (factory->hostContext != nullptr) - v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &host); + v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &hostApplication); - dpf_component** const componentptr = new dpf_component*; - *componentptr = new dpf_component(host); - *instance = static_cast(componentptr); - return V3_OK; + // create component + if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid)) + { + dpf_component** const componentptr = new dpf_component*; + *componentptr = new dpf_component(hostApplication); + *instance = static_cast(componentptr); + return V3_OK; + } + + #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + // create edit controller + if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_controller) && v3_tuid_match(iid, v3_edit_controller_iid)) + { + dpf_edit_controller** const controllerptr = new dpf_edit_controller*; + *controllerptr = new dpf_edit_controller(hostApplication); + *instance = static_cast(controllerptr); + return V3_OK; + } + #endif + + // unsupported, roll back host application + if (hostApplication != nullptr) + v3_cpp_obj_unref(hostApplication); + + return V3_NO_INTERFACE; } // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_factory_2 - static v3_result V3_API get_class_info_2(void*, int32_t idx, v3_class_info_2* info) + static v3_result V3_API get_class_info_2(void*, const int32_t idx, v3_class_info_2* const info) { + d_stdout("dpf_factory::get_class_info_2 => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); - DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); - -#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS - info->class_flags = 0; -#else + // TODO FIXME +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER info->class_flags = V3_DISTRIBUTABLE; #endif + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); + DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); @@ -3775,29 +4316,28 @@ struct dpf_factory : v3_plugin_factory_cpp { // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_3 - static v3_result V3_API get_class_info_utf16(void*, int32_t idx, v3_class_info_3* info) + static v3_result V3_API get_class_info_utf16(void*, const int32_t idx, v3_class_info_3* const info) { + d_stdout("dpf_factory::get_class_info_utf16 => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); - DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); - -#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS - info->class_flags = 0; -#else + // TODO FIXME +#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER info->class_flags = V3_DISTRIBUTABLE; #endif + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); + DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy_utf16(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); return V3_OK; } - static v3_result V3_API set_host_context(void* self, v3_funknown** context) + static v3_result V3_API set_host_context(void* const self, v3_funknown** const context) { d_stdout("dpf_factory::set_host_context => %p %p", self, context); dpf_factory* const factory = *static_cast(self); @@ -3815,8 +4355,6 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; } - - DISTRHO_PREVENT_HEAP_ALLOCATION }; END_NAMESPACE_DISTRHO @@ -3830,9 +4368,9 @@ const void* GetPluginFactory(void); const void* GetPluginFactory(void) { USE_NAMESPACE_DISTRHO; - static const dpf_factory factory; - static const v3_funknown* const factoryptr = &factory; - return &factoryptr; + dpf_factory** const factoryptr = new dpf_factory*; + *factoryptr = new dpf_factory; + return static_cast(factoryptr); } // -------------------------------------------------------------------------------------------------------------------- @@ -3854,8 +4392,9 @@ bool ENTRYFNNAME(void*); bool ENTRYFNNAME(void*) { - // find plugin bundle USE_NAMESPACE_DISTRHO; + + // find plugin bundle static String bundlePath; if (bundlePath.isEmpty()) { @@ -3869,6 +4408,10 @@ bool ENTRYFNNAME(void*) d_nextBundlePath = bundlePath.buffer(); } + // init dummy plugin and set uniqueId + dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] + = dpf_tuid_processor[2] = dpf_tuid_view[2] = getPluginInfo().getUniqueId(); + return true; } diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 6d837cb1..1f1192a8 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -287,6 +287,28 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8 typedef void (*setSizeFunc) (void* ptr, uint width, uint height); typedef bool (*fileRequestFunc) (void* ptr, const char* key); +#if defined(DISTRHO_PLUGIN_TARGET_VST3) && !defined(DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED) +enum Vst3InternalParameters { + kVst3InternalParameterActive = 0, + kVst3InternalParameterBufferSize, + kVst3InternalParameterSampleRate, +# if DISTRHO_PLUGIN_WANT_LATENCY + kVst3InternalParameterLatency, +# endif +# if DISTRHO_PLUGIN_WANT_PROGRAMS + kVst3InternalParameterProgram, +# endif + kVst3InternalParameterBaseCount, +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, + kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, + kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end +# else + kVst3InternalParameterCount = kVst3InternalParameterBaseCount +# endif +}; +#endif + // ----------------------------------------------------------------------- // UI private data @@ -358,12 +380,7 @@ struct UI::PrivateData { #endif #ifdef DISTRHO_PLUGIN_TARGET_VST3 -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - parameterOffset += 130 * 16; // all MIDI CCs plus aftertouch and pitchbend -# endif -# if DISTRHO_PLUGIN_WANT_PROGRAMS - parameterOffset += 1; -# endif + parameterOffset += kVst3InternalParameterCount; #endif } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index e3031d2b..b037a4ff 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -39,6 +39,8 @@ namespace std { /* TODO items: * - mousewheel event * - key down/up events + * - host-side resizing + * - file request? */ #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) @@ -70,17 +72,20 @@ struct ScopedUTF16String { int16_t* str; ScopedUTF16String(const char* const s) noexcept; ~ScopedUTF16String() noexcept; - operator int16_t*() const noexcept; + operator const int16_t*() const noexcept; }; // -------------------------------------------------------------------------------------------------------------------- -static bool applyGeometryConstraints(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, +static void applyGeometryConstraints(const uint minimumWidth, + const uint minimumHeight, + 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); const int32_t minWidth = static_cast(minimumWidth); const int32_t minHeight = static_cast(minimumHeight); - bool changed = false; if (keepAspectRatio) { @@ -89,8 +94,6 @@ static bool applyGeometryConstraints(const uint minimumWidth, const uint minimum if (d_isNotEqual(ratio, reqRatio)) { - changed = true; - // fix width if (reqRatio > ratio) rect->right = static_cast(rect->bottom * ratio + 0.5); @@ -100,14 +103,13 @@ static bool applyGeometryConstraints(const uint minimumWidth, const uint minimum } } - if (minWidth > rect->right || minHeight > rect->bottom) - { - changed = true; + if (minWidth > rect->right) rect->right = minWidth; + if (minHeight > rect->bottom) rect->bottom = minHeight; - } - return changed; + d_stdout("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | AFTER", + minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); } // -------------------------------------------------------------------------------------------------------------------- @@ -118,10 +120,6 @@ static bool applyGeometryConstraints(const uint minimumWidth, const uint minimum * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things. * The UI is created during the "attach" view event, and destroyed during "removed". * - * Note that DPF VST3 implementation works over the connection point interface, - * rather than using edit controller directly. - * This allows the UI to be running remotely from the DSP. - * * The low-level VST3 stuff comes after. */ class UIVst3 @@ -140,7 +138,7 @@ public: void* const instancePointer, const bool willResizeFromHost) : fView(view), - fHostContext(host), + fHostApplication(host), fConnection(connection), fFrame(frame), fReadyForPluginData(false), @@ -169,10 +167,16 @@ public: disconnect(); } - void postInit(const int32_t nextWidth, const int32_t nextHeight) + void postInit(const uint32_t nextWidth, const uint32_t nextHeight) { if (fIsResizingFromHost && nextWidth > 0 && nextHeight > 0) - fUI.setWindowSizeForVST3(nextWidth, nextHeight); + { + if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight) + { + d_stdout("postInit sets new size as %u %u", nextWidth, nextHeight); + fUI.setWindowSizeForVST3(nextWidth, nextHeight); + } + } if (fConnection != nullptr) connect(fConnection); @@ -305,7 +309,8 @@ public: uint minimumWidth, minimumHeight; bool keepAspectRatio; fUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); - return applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; + applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); + return V3_TRUE; } // ---------------------------------------------------------------------------------------------------------------- @@ -379,22 +384,27 @@ public: res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) - { - DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INTERNAL_ERR); - - fUI.programLoaded(static_cast(value + 0.5)); - } - else -#endif + if (rindex < kVst3InternalParameterBaseCount) { - rindex -= fUI.getParameterOffset(); - DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + switch (rindex) + { + case kVst3InternalParameterSampleRate: + DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG); + fUI.setSampleRate(value, true); + break; + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case kVst3InternalParameterProgram: + DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG); + fUI.programLoaded(static_cast(value + 0.5)); + break; + #endif + } - fUI.parameterChanged(static_cast(rindex), value); + return V3_OK; } + const uint32_t index = static_cast(rindex) - kVst3InternalParameterBaseCount; + fUI.parameterChanged(index, value); return V3_OK; } @@ -420,10 +430,13 @@ public: DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, keyLength, res); - res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + if (valueLength != 0) + { + res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength); + DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, valueLength, res); + } // do cheap inline conversion char* const key = (char*)key16; @@ -445,19 +458,6 @@ public: } #endif - if (std::strcmp(msgid, "sample-rate") == 0) - { - double sampleRate; - v3_result res; - - res = v3_cpp_obj(attrs)->get_float(attrs, "value", &sampleRate); - DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); - DISTRHO_SAFE_ASSERT_RETURN(sampleRate > 0, V3_INVALID_ARG); - - fUI.setSampleRate(sampleRate, true); - return V3_OK; - } - d_stdout("UIVst3 received unknown msg '%s'", msgid); return V3_NOT_IMPLEMENTED; @@ -522,7 +522,7 @@ public: private: // VST3 stuff v3_plugin_view** const fView; - v3_host_application** const fHostContext; + v3_host_application** const fHostApplication; v3_connection_point** fConnection; v3_plugin_frame** fFrame; @@ -540,12 +540,12 @@ private: v3_message** createMessage(const char* const id) const { - DISTRHO_SAFE_ASSERT_RETURN(fHostContext != nullptr, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(fHostApplication != nullptr, nullptr); v3_tuid iid; - memcpy(iid, v3_message_iid, sizeof(v3_tuid)); + std::memcpy(iid, v3_message_iid, sizeof(v3_tuid)); v3_message** msg = nullptr; - const v3_result res = v3_cpp_obj(fHostContext)->create_instance(fHostContext, iid, iid, (void**)&msg); + const v3_result res = v3_cpp_obj(fHostApplication)->create_instance(fHostApplication, iid, iid, (void**)&msg); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr); DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr); @@ -718,15 +718,15 @@ private: // v3_funknown for classes with a single instance template -static uint32_t V3_API dpf_single_instance_ref(void* self) +static uint32_t V3_API dpf_single_instance_ref(void* const self) { - return ++(*(T**)self)->refcounter; + return ++(*static_cast(self))->refcounter; } template -static uint32_t V3_API dpf_single_instance_unref(void* self) +static uint32_t V3_API dpf_single_instance_unref(void* const self) { - return --(*(T**)self)->refcounter; + return --(*static_cast(self))->refcounter; } // -------------------------------------------------------------------------------------------------------------------- @@ -756,21 +756,20 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_connection_point(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface) { - d_stdout("UI|query_interface_connection_point => %p", self); + dpf_ui_connection_point* const point = *static_cast(self); - if (v3_tuid_match(iid, v3_funknown_iid)) + 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); + ++point->refcounter; *iface = self; return V3_OK; } - if (v3_tuid_match(iid, v3_connection_point_iid)) - { - *iface = self; - return V3_OK; - } + d_stdout("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); *iface = NULL; return V3_NO_INTERFACE; @@ -779,11 +778,11 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point - static v3_result V3_API connect(void* self, v3_connection_point** other) + static v3_result V3_API connect(void* const self, v3_connection_point** const other) { - d_stdout("UI|dpf_ui_connection_point::connect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + dpf_ui_connection_point* const point = *static_cast(self); + d_stdout("UI|dpf_ui_connection_point::connect => %p %p", self, other); + DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); point->other = other; @@ -794,11 +793,11 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { return V3_OK; }; - static v3_result V3_API disconnect(void* self, v3_connection_point** other) + static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) { - d_stdout("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + d_stdout("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); + dpf_ui_connection_point* const point = *static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); point->other = nullptr; @@ -809,10 +808,9 @@ struct dpf_ui_connection_point : v3_connection_point_cpp { return V3_OK; }; - static v3_result V3_API notify(void* self, v3_message** message) + static v3_result V3_API notify(void* const self, v3_message** const message) { - dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self; - DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED); + dpf_ui_connection_point* const point = *static_cast(self); UIVst3* const uivst3 = point->uivst3; DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); @@ -847,20 +845,21 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_funknown - static v3_result V3_API query_interface_view_content_scale(void* self, const v3_tuid iid, void** iface) + static v3_result V3_API query_interface_view_content_scale(void* const self, const v3_tuid iid, void** const iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_plugin_view_content_scale* const scale = *static_cast(self); - if (v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) + 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); + ++scale->refcounter; *iface = self; return V3_OK; } + d_stdout("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + *iface = NULL; return V3_NO_INTERFACE; } @@ -868,11 +867,10 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view_content_scale - static v3_result V3_API set_content_scale_factor(void* self, float factor) + static v3_result V3_API set_content_scale_factor(void* const self, const float factor) { + dpf_plugin_view_content_scale* const scale = *static_cast(self); d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); - dpf_plugin_view_content_scale* const scale = *(dpf_plugin_view_content_scale**)self; - DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALIZED); scale->scaleFactor = factor; @@ -903,7 +901,7 @@ struct dpf_timer_handler : v3_timer_handler_cpp { unref = dpf_single_instance_unref; // v3_timer_handler - handler.on_timer = on_timer; + timer.on_timer = on_timer; } // ---------------------------------------------------------------------------------------------------------------- @@ -911,18 +909,19 @@ struct dpf_timer_handler : v3_timer_handler_cpp { static v3_result V3_API query_interface_timer_handler(void* self, const v3_tuid iid, void** iface) { - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_timer_handler* const timer = *static_cast(self); - if (v3_tuid_match(iid, v3_timer_handler_iid)) + 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); + ++timer->refcounter; *iface = self; return V3_OK; } + d_stdout("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + *iface = NULL; return V3_NO_INTERFACE; } @@ -932,11 +931,11 @@ struct dpf_timer_handler : v3_timer_handler_cpp { static void V3_API on_timer(void* self) { - dpf_timer_handler* const handler = *(dpf_timer_handler**)self; - DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(handler->valid,); + dpf_timer_handler* const timer = *static_cast(self); - handler->uivst3->onTimer(); + DISTRHO_SAFE_ASSERT_RETURN(timer->valid,); + + timer->uivst3->onTimer(); } }; #endif @@ -945,9 +944,9 @@ struct dpf_timer_handler : v3_timer_handler_cpp { // dpf_plugin_view static const char* const kSupportedPlatforms[] = { -#ifdef _WIN32 +#if defined(DISTRHO_OS_WINDOWS) V3_VIEW_PLATFORM_TYPE_HWND, -#elif defined(__APPLE__) +#elif defined(DISTRHO_OS_MAC) V3_VIEW_PLATFORM_TYPE_NSVIEW, #else V3_VIEW_PLATFORM_TYPE_X11, @@ -963,21 +962,29 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif ScopedPointer uivst3; // cached values - v3_host_application** const host; + v3_host_application** const hostApplication; void* const instancePointer; double sampleRate; v3_plugin_frame** frame; - int32_t nextWidth, nextHeight; + v3_run_loop** runloop; + uint32_t nextWidth, nextHeight; - dpf_plugin_view(v3_host_application** const h, void* const instance, const double sr) + dpf_plugin_view(v3_host_application** const host, void* const instance, const double sr) : refcounter(1), - host(h), + hostApplication(host), instancePointer(instance), sampleRate(sr), frame(nullptr), + runloop(nullptr), nextWidth(0), nextHeight(0) { + d_stdout("dpf_plugin_view() with hostApplication %p", hostApplication); + + // make sure host application is valid through out this view lifetime + if (hostApplication != nullptr) + v3_cpp_obj_ref(hostApplication); + // v3_funknown, everything custom query_interface = query_interface_view; ref = ref_view; @@ -998,31 +1005,42 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view.check_size_constraint = check_size_constraint; } + ~dpf_plugin_view() + { + d_stdout("~dpf_plugin_view()"); + + connection = nullptr; + scale = nullptr; + #ifdef DPF_VST3_USING_HOST_RUN_LOOP + timer = nullptr; + #endif + uivst3 = nullptr; + + if (hostApplication != nullptr) + v3_cpp_obj_unref(hostApplication); + } + // ---------------------------------------------------------------------------------------------------------------- // v3_funknown static v3_result V3_API query_interface_view(void* self, const v3_tuid iid, void** iface) { - d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); - - if (v3_tuid_match(iid, v3_funknown_iid)) - { - *iface = self; - return V3_OK; - } + dpf_plugin_view* const view = *static_cast(self); - if (v3_tuid_match(iid, v3_plugin_view_iid)) + 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); + ++view->refcounter; *iface = self; return V3_OK; } - dpf_plugin_view* const view = *static_cast(self); - 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()); + if (view->connection == nullptr) view->connection = new dpf_ui_connection_point(view->uivst3); else @@ -1033,6 +1051,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { 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()); + if (view->scale == nullptr) view->scale = new dpf_plugin_view_content_scale(view->uivst3); else @@ -1041,12 +1062,18 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; } + d_stdout("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); + + *iface = nullptr; return V3_NO_INTERFACE; } static uint32_t V3_API ref_view(void* self) { - return ++(*static_cast(self))->refcounter; + dpf_plugin_view* const view = *static_cast(self); + const int refcount = ++view->refcounter; + d_stdout("dpf_plugin_view::ref => %p | refcount %i", self, refcount); + return refcount; } static uint32_t V3_API unref_view(void* self) @@ -1056,22 +1083,28 @@ 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_stdout("dpf_plugin_view::unref => %p | refcount %i", self, refcount); return refcount; } - d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); - if (view->connection != nullptr && view->connection->other) v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, (v3_connection_point**)&view->connection); + /** + * Some hosts will have unclean instances of a few of the view child classes at this point. + * We check for those here, going through the whole possible chain to see if it is safe to delete. + * TODO cleanup. + */ + + bool unclean = false; + if (dpf_ui_connection_point* const conn = view->connection) { if (const int refcount = conn->refcounter) { + unclean = true; d_stderr("DPF warning: asked to delete view while connection point still active (refcount %d)", refcount); - return V3_INVALID_ARG; } } @@ -1079,11 +1112,16 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { if (const int refcount = scale->refcounter) { + unclean = true; d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount); - return V3_INVALID_ARG; } } + if (unclean) + return 0; + + d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); + delete view; delete viewptr; return 0; @@ -1092,7 +1130,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_view - static v3_result V3_API is_platform_type_supported(void* self, const char* platform_type) + static v3_result V3_API is_platform_type_supported(void* const self, const char* const platform_type) { d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); @@ -1105,9 +1143,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_NOT_IMPLEMENTED; } - static v3_result V3_API attached(void* self, void* parent, const char* platform_type) + static v3_result V3_API attached(void* const self, void* const parent, const char* const platform_type) { - d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); + d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); dpf_plugin_view* const view = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); @@ -1122,11 +1160,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { v3_run_loop** runloop = nullptr; v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); + + view->runloop = runloop; #endif const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)self, - view->host, + view->hostApplication, view->connection != nullptr ? view->connection->other : nullptr, view->frame, (uintptr_t)parent, @@ -1154,29 +1194,20 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_NOT_IMPLEMENTED; } - static v3_result V3_API removed(void* self) + static v3_result V3_API removed(void* const self) { - d_stdout("dpf_plugin_view::removed => %p", self); + d_stdout("dpf_plugin_view::removed => %p", self); dpf_plugin_view* const view = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); #ifdef DPF_VST3_USING_HOST_RUN_LOOP // unregister our timer as needed - if (view->timer != nullptr) + if (v3_run_loop** const runloop = view->runloop) { - v3_run_loop** runloop = nullptr; - - if (view->frame != nullptr) - v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); - - if (runloop != nullptr) + if (view->timer != nullptr && view->timer->valid) { v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); - // we query it 2 times in total, so lets unref 2 times as well - v3_cpp_obj_unref(runloop); - v3_cpp_obj_unref(runloop); - if (const int refcount = --view->timer->refcounter) { view->timer->valid = false; @@ -1187,11 +1218,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->timer = nullptr; } } - else - { - view->timer->valid = false; - d_stderr("VST3 warning: Host run loop not available during dpf_plugin_view::removed"); - } + + v3_cpp_obj_unref(runloop); + view->runloop = nullptr; } #endif @@ -1199,10 +1228,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; } - static v3_result V3_API on_wheel(void* self, float distance) + 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_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1216,10 +1245,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif } - static v3_result V3_API on_key_down(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) + static v3_result V3_API on_key_down(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); + d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1233,10 +1262,10 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif } - static v3_result V3_API on_key_up(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) + static v3_result V3_API on_key_up(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); + d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1250,9 +1279,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif } - static v3_result V3_API get_size(void* self, v3_view_rect* rect) + static v3_result V3_API get_size(void* const self, v3_view_rect* const rect) { - d_stdout("dpf_plugin_view::get_size => %p", self); + d_stdout("dpf_plugin_view::get_size => %p", self); dpf_plugin_view* const view = *static_cast(self); if (UIVst3* const uivst3 = view->uivst3) @@ -1263,27 +1292,30 @@ struct dpf_plugin_view : v3_plugin_view_cpp { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, view->instancePointer, scaleFactor); rect->left = rect->top = 0; - rect->right = tmpUI.getWidth(); - rect->bottom = tmpUI.getHeight(); + rect->right = view->nextWidth = tmpUI.getWidth(); + rect->bottom = view->nextHeight = tmpUI.getHeight(); return V3_OK; } - static v3_result V3_API on_size(void* self, v3_view_rect* rect) + static v3_result V3_API on_size(void* const self, v3_view_rect* const rect) { + 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); + dpf_plugin_view* const view = *static_cast(self); if (UIVst3* const uivst3 = view->uivst3) return uivst3->onSize(rect); - view->nextWidth = rect->right - rect->left; - view->nextHeight = rect->bottom - rect->top; + view->nextWidth = static_cast(rect->right - rect->left); + view->nextHeight = static_cast(rect->bottom - rect->top); return V3_OK; } - static v3_result V3_API on_focus(void* self, v3_bool state) + 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_stdout("dpf_plugin_view::on_focus => %p %u", self, state); dpf_plugin_view* const view = *static_cast(self); UIVst3* const uivst3 = view->uivst3; @@ -1297,7 +1329,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif } - static v3_result V3_API set_frame(void* self, v3_plugin_frame** frame) + static v3_result V3_API set_frame(void* const self, v3_plugin_frame** const frame) { dpf_plugin_view* const view = *static_cast(self); @@ -1309,7 +1341,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_NOT_INITIALIZED; } - static v3_result V3_API can_resize(void* self) + static v3_result V3_API can_resize(void* const self) { #if DISTRHO_UI_USER_RESIZABLE dpf_plugin_view* const view = *static_cast(self); @@ -1326,7 +1358,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif } - static v3_result V3_API check_size_constraint(void* self, v3_view_rect* rect) + static v3_result V3_API check_size_constraint(void* const self, v3_view_rect* const rect) { dpf_plugin_view* const view = *static_cast(self); @@ -1340,7 +1372,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { uint minimumWidth, minimumHeight; bool keepAspectRatio; tmpUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); - return applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect) ? V3_FALSE : V3_TRUE; + applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); + return V3_TRUE; } }; @@ -1355,7 +1388,7 @@ v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host, { dpf_plugin_view** const viewptr = new dpf_plugin_view*; *viewptr = new dpf_plugin_view(host, instancePointer, sampleRate); - return (v3_plugin_view**)static_cast(viewptr); + return static_cast(static_cast(viewptr)); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 417d3061..c5890334 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -29,6 +29,7 @@ /** * cast object into its proper C++ type. * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. + * * we can use this as a little helper for keeping both C and C++ compatiblity. * specialized templated calls are defined where required * (that is, object inherits from something other than `v3_funknown`) diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 055e7d71..9edb151a 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -79,9 +79,9 @@ struct v3_param_info { struct v3_edit_controller { struct v3_plugin_base; - v3_result (V3_API* set_component_state)(void* self, struct v3_bstream*); - v3_result (V3_API* set_state)(void* self, struct v3_bstream*); - v3_result (V3_API* get_state)(void* self, struct v3_bstream*); + v3_result (V3_API* set_component_state)(void* self, struct v3_bstream**); + v3_result (V3_API* set_state)(void* self, struct v3_bstream**); + v3_result (V3_API* get_state)(void* self, struct v3_bstream**); int32_t (V3_API* get_parameter_count)(void* self); v3_result (V3_API* get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); v3_result (V3_API* get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index 0abbd055..0abc3be1 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -176,7 +176,7 @@ struct v3_event_handler_cpp : v3_funknown { }; struct v3_timer_handler_cpp : v3_funknown { - v3_timer_handler handler; + v3_timer_handler timer; }; struct v3_run_loop_cpp : v3_funknown { diff --git a/examples/Info/Makefile b/examples/Info/Makefile index 84d5b84a..163ec7e1 100644 --- a/examples/Info/Makefile +++ b/examples/Info/Makefile @@ -15,7 +15,7 @@ NAME = d_info FILES_DSP = \ InfoExamplePlugin.cpp -FILES_UI = \ +FILES_UI = \ InfoExampleUI.cpp # -------------------------------------------------------------- From c7e77db0ac265dd031c14a912108235c39733971 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 28 Dec 2021 16:37:05 +0000 Subject: [PATCH 265/504] Silence a compiler warning Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 0363bc36..b1d9703e 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 0363bc36379a268877af147e759fd6fd5f576c36 +Subproject commit b1d9703ecbdb0a033fe0b9acdf58b90f7d81a8e5 From a3cc61e48284e8826413fec4459021f8d805c376 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 28 Dec 2021 21:29:46 +0000 Subject: [PATCH 266/504] VST3 resize fixes for macOS --- dgl/src/WindowPrivateData.cpp | 4 ++- distrho/src/DistrhoUIVST3.cpp | 65 +++++++++++++++++++++++++---------- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index a00320c7..028170fd 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -748,7 +748,9 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu { Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) - printEvent(event, "pugl event: ", true); + if (event->type != PUGL_TIMER) { + printEvent(event, "pugl event: ", true); + } #endif switch (event->type) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index b037a4ff..c7de0a69 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -167,10 +167,15 @@ public: disconnect(); } - void postInit(const uint32_t nextWidth, const uint32_t nextHeight) + void postInit(uint32_t nextWidth, uint32_t nextHeight) { if (fIsResizingFromHost && nextWidth > 0 && nextHeight > 0) { +#ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + nextWidth *= scaleFactor; + nextHeight *= scaleFactor; +#endif if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight) { d_stdout("postInit sets new size as %u %u", nextWidth, nextHeight); @@ -278,18 +283,27 @@ public: return V3_OK; } - v3_result onSize(v3_view_rect* const rect) + v3_result onSize(v3_view_rect* const orect) { + v3_view_rect rect = *orect; +#ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + rect.top *= scaleFactor; + rect.left *= scaleFactor; + rect.right *= scaleFactor; + rect.bottom *= scaleFactor; +#endif + if (fIsResizingFromPlugin) { d_stdout("host->plugin onSize request %i %i (IGNORED, plugin resize active)", - rect->right - rect->left, rect->bottom - rect->top); + rect.right - rect.left, rect.bottom - rect.top); return V3_OK; } - d_stdout("host->plugin onSize request %i %i (OK)", rect->right - rect->left, rect->bottom - rect->top); + d_stdout("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.setWindowSizeForVST3(rect.right - rect.left, rect.bottom - rect.top); return V3_OK; } @@ -623,6 +637,12 @@ private: DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); +#ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + width /= scaleFactor; + height /= scaleFactor; +#endif + if (fIsResizingFromHost) { d_stdout("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); @@ -631,12 +651,6 @@ private: d_stdout("plugin->host setSize %u %u (OK)", width, height); -#ifdef DISTRHO_OS_MAC - const double scaleFactor = fUI.getScaleFactor(); - width /= scaleFactor; - height /= scaleFactor; -#endif - fIsResizingFromPlugin = true; v3_view_rect rect; @@ -1049,6 +1063,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; } +#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", @@ -1061,6 +1076,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { *iface = &view->scale; return V3_OK; } +#endif d_stdout("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); @@ -1108,6 +1124,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { } } +#ifndef DISTRHO_OS_MAC if (dpf_plugin_view_content_scale* const scale = view->scale) { if (const int refcount = scale->refcounter) @@ -1116,6 +1133,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount); } } +#endif if (unclean) return 0; @@ -1164,13 +1182,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->runloop = runloop; #endif - const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; + const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; view->uivst3 = new UIVst3((v3_plugin_view**)self, view->hostApplication, view->connection != nullptr ? view->connection->other : nullptr, view->frame, (uintptr_t)parent, - scaleFactor, + lastScaleFactor, view->sampleRate, view->instancePointer, view->nextWidth > 0 && view->nextHeight > 0); @@ -1287,13 +1305,21 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->getSize(rect); - const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; + const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->instancePointer, scaleFactor); + view->instancePointer, lastScaleFactor); rect->left = rect->top = 0; - rect->right = view->nextWidth = tmpUI.getWidth(); - rect->bottom = view->nextHeight = tmpUI.getHeight(); + view->nextWidth = tmpUI.getWidth(); + view->nextHeight = tmpUI.getHeight(); +#ifdef DISTRHO_OS_MAC + const double scaleFactor = tmpUI.getScaleFactor(); + view->nextWidth /= scaleFactor; + view->nextHeight /= scaleFactor; +#endif + rect->right = view->nextWidth; + rect->bottom = view->nextHeight; + tmpUI.quit(); return V3_OK; } @@ -1365,13 +1391,14 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->checkSizeConstraint(rect); - const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; + const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->instancePointer, scaleFactor); + view->instancePointer, lastScaleFactor); uint minimumWidth, minimumHeight; bool keepAspectRatio; tmpUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); + tmpUI.quit(); applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); return V3_TRUE; } From 2b2f64ab47c6afad3740e75d3fb4d25bff5c1802 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 01:29:51 +0000 Subject: [PATCH 267/504] Code tweaks to better support VST3 host-side resizing, fix autoscale Signed-off-by: falkTX --- dgl/TopLevelWidget.hpp | 5 +++- dgl/Window.hpp | 4 ++- dgl/src/TopLevelWidget.cpp | 17 ++++++++++-- dgl/src/TopLevelWidgetPrivateData.hpp | 2 +- dgl/src/Window.cpp | 40 +++++++++++++++++++-------- dgl/src/WindowPrivateData.cpp | 6 +++- dgl/src/WindowPrivateData.hpp | 5 +++- distrho/DistrhoUI.hpp | 6 +++- distrho/src/DistrhoUI.cpp | 19 ++++++++++--- distrho/src/DistrhoUIInternal.hpp | 1 + distrho/src/DistrhoUIPrivateData.hpp | 15 ++++++++-- 11 files changed, 94 insertions(+), 26 deletions(-) diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 522dfaf8..5e53784e 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -110,7 +110,8 @@ public: void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio = false, - bool automaticallyScale = false); + bool automaticallyScale = false, + bool resizeNowIfAutoScaling = true); DISTRHO_DEPRECATED_BY("getApp()") Application& getParentApp() const noexcept { return getApp(); } @@ -132,6 +133,8 @@ private: #ifdef DISTRHO_DEFINES_H_INCLUDED friend class DISTRHO_NAMESPACE::UI; #endif + /** @internal */ + virtual void requestSizeChange(uint width, uint height); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TopLevelWidget) }; diff --git a/dgl/Window.hpp b/dgl/Window.hpp index f91bb41e..3de57e86 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -399,7 +399,8 @@ public: void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio = false, - bool automaticallyScale = false); + bool automaticallyScale = false, + bool resizeNowIfAutoScaling = true); /** DEPRECATED Use isIgnoringKeyRepeat(). */ DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") @@ -473,6 +474,7 @@ private: uint height, double scaleFactor, bool resizable, + bool isVST3, bool doPostInit); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 9a6b5c26..f577a1c9 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -60,7 +60,7 @@ void TopLevelWidget::setSize(const Size& size) pData->window.setSize(size); } -bool TopLevelWidget::setClipboard(const char* mimeType, const void* data, size_t dataSize) +bool TopLevelWidget::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) { return pData->window.setClipboard(mimeType, data, dataSize); } @@ -103,9 +103,14 @@ void TopLevelWidget::repaint(const Rectangle& rect) noexcept void TopLevelWidget::setGeometryConstraints(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, - const bool automaticallyScale) + const bool automaticallyScale, + const bool resizeNowIfAutoScaling) { - pData->window.setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); + pData->window.setGeometryConstraints(minimumWidth, + minimumHeight, + keepAspectRatio, + automaticallyScale, + resizeNowIfAutoScaling); } // -------------------------------------------------------------------------------------------------------------------- @@ -137,4 +142,10 @@ bool TopLevelWidget::onScroll(const ScrollEvent& ev) // -------------------------------------------------------------------------------------------------------------------- +void TopLevelWidget::requestSizeChange(uint, uint) +{ +} + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DGL diff --git a/dgl/src/TopLevelWidgetPrivateData.hpp b/dgl/src/TopLevelWidgetPrivateData.hpp index 7105b2f8..fb4e77b2 100644 --- a/dgl/src/TopLevelWidgetPrivateData.hpp +++ b/dgl/src/TopLevelWidgetPrivateData.hpp @@ -30,7 +30,7 @@ struct TopLevelWidget::PrivateData { Widget* const selfw; Window& window; - explicit PrivateData(TopLevelWidget* const s, Window& w); + explicit PrivateData(TopLevelWidget* self, Window& window); ~PrivateData(); void display(); bool keyboardEvent(const KeyboardEvent& ev); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 1e2f3a12..146fffc7 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -15,6 +15,7 @@ */ #include "WindowPrivateData.hpp" +#include "../TopLevelWidget.hpp" #include "pugl.hpp" @@ -87,7 +88,7 @@ Window::Window(Application& app, const uint height, const double scaleFactor, const bool resizable) - : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaleFactor, resizable)) + : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaleFactor, resizable, false)) { pData->initPost(); } @@ -98,8 +99,9 @@ Window::Window(Application& app, const uint height, const double scaleFactor, const bool resizable, + const bool isVST3, const bool doPostInit) - : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaleFactor, resizable)) + : pData(new PrivateData(app, this, parentWindowHandle, width, height, scaleFactor, resizable, isVST3)) { if (doPostInit) pData->initPost(); @@ -228,7 +230,19 @@ void Window::setSize(uint width, uint height) } } - puglSetWindowSize(pData->view, width, height); + if (pData->usesSizeRequest) + { + DISTRHO_SAFE_ASSERT_RETURN(pData->topLevelWidgets.size() != 0,); + + TopLevelWidget* const topLevelWidget = pData->topLevelWidgets.front(); + DISTRHO_SAFE_ASSERT_RETURN(topLevelWidget != nullptr,); + + topLevelWidget->requestSizeChange(width, height); + } + else + { + puglSetWindowSize(pData->view, width, height); + } } void Window::setSize(const Size& size) @@ -371,10 +385,11 @@ Size Window::getGeometryConstraints(bool& keepAspectRatio) return Size(pData->minWidth, pData->minHeight); } -void Window::setGeometryConstraints(const uint minimumWidth, - const uint minimumHeight, +void Window::setGeometryConstraints(uint minimumWidth, + uint minimumHeight, const bool keepAspectRatio, - const bool automaticallyScale) + const bool automaticallyScale, + const bool resizeNowIfAutoScaling) { DISTRHO_SAFE_ASSERT_RETURN(minimumWidth > 0,); DISTRHO_SAFE_ASSERT_RETURN(minimumHeight > 0,); @@ -389,12 +404,15 @@ void Window::setGeometryConstraints(const uint minimumWidth, const double scaleFactor = pData->scaleFactor; - puglSetGeometryConstraints(pData->view, - static_cast(minimumWidth * scaleFactor + 0.5), - static_cast(minimumHeight * scaleFactor + 0.5), - keepAspectRatio); + if (automaticallyScale && scaleFactor != 1.0) + { + minimumWidth *= scaleFactor; + minimumHeight *= scaleFactor; + } + + puglSetGeometryConstraints(pData->view, minimumWidth, minimumHeight, keepAspectRatio); - if (scaleFactor != 1.0) + if (scaleFactor != 1.0 && automaticallyScale && resizeNowIfAutoScaling) { const Size size(getSize()); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 028170fd..6a9467d5 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -76,6 +76,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) isClosed(true), isVisible(false), isEmbed(false), + usesSizeRequest(false), scaleFactor(getDesktopScaleFactor(view)), autoScaling(false), autoScaleFactor(1.0), @@ -103,6 +104,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c isClosed(true), isVisible(false), isEmbed(false), + usesSizeRequest(false), scaleFactor(ppData->scaleFactor), autoScaling(false), autoScaleFactor(1.0), @@ -134,6 +136,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), + usesSizeRequest(false), scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), autoScaling(false), autoScaleFactor(1.0), @@ -157,7 +160,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window::PrivateData::PrivateData(Application& a, Window* const s, const uintptr_t parentWindowHandle, const uint width, const uint height, - const double scale, const bool resizable) + const double scale, const bool resizable, const bool isVST3) : app(a), appData(a.pData), self(s), @@ -167,6 +170,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0 && view != nullptr), isEmbed(parentWindowHandle != 0), + usesSizeRequest(isVST3), scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), autoScaling(false), autoScaleFactor(1.0), diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index ffb2237a..36d2ce43 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -63,6 +63,9 @@ struct Window::PrivateData : IdleCallback { /** Whether this Window is embed into another (usually not DGL-controlled) Window. */ const bool isEmbed; + /** Whether to ignore resize requests and feed them into the host instead. used for VST3 */ + const bool usesSizeRequest; + /** Scale factor to report to widgets on request, purely informational. */ double scaleFactor; @@ -127,7 +130,7 @@ struct Window::PrivateData : IdleCallback { /** Constructor for an embed Window, with a few extra hints from the host side. */ explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, - uint width, uint height, double scaling, bool resizable); + uint width, uint height, double scaling, bool resizable, bool isVST3); /** Destructor. */ ~PrivateData() override; diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index e7140828..dc09efda 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -84,7 +84,7 @@ public: It assumes aspect ratio is meant to be kept. Manually call setGeometryConstraints instead if keeping UI aspect ratio is not required. */ - UI(uint width = 0, uint height = 0, bool automaticallyScale = false); + UI(uint width = 0, uint height = 0, bool automaticallyScaleAndSetAsMinimumSize = false); /** Destructor. @@ -358,6 +358,10 @@ private: PrivateData* const uiData; friend class DGL_NAMESPACE::PluginWindow; friend class UIExporter; +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + /** @internal */ + void requestSizeChange(uint width, uint height) override; +#endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) }; diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index ee62ee68..7f26308c 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -189,21 +189,21 @@ UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint hei /* ------------------------------------------------------------------------------------------------------------ * UI */ -UI::UI(const uint width, const uint height, const bool automaticallyScale) +UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize) : UIWidget(UI::PrivateData::createNextWindow(this, width, height)), uiData(UI::PrivateData::s_nextPrivateData) { #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - if (width > 0 && height > 0) + if (width != 0 && height != 0) { Widget::setSize(width, height); - if (automaticallyScale) + if (automaticallyScaleAndSetAsMinimumSize) setGeometryConstraints(width, height, true, true); } #else // unused - return; (void)automaticallyScale; + (void)automaticallyScaleAndSetAsMinimumSize; #endif } @@ -370,9 +370,20 @@ void UI::onResize(const ResizeEvent& ev) { UIWidget::onResize(ev); +#ifndef DISTRHO_PLUGIN_TARGET_VST3 + if (uiData->initializing) + return; + const uint width = ev.size.getWidth(); const uint height = ev.size.getHeight(); uiData->setSizeCallback(width, height); +#endif +} + +// NOTE: only used for VST3 +void UI::requestSizeChange(const uint width, const uint height) +{ + uiData->setSizeCallback(width, height); } #endif diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 669d2427..742ea537 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -98,6 +98,7 @@ public: DISTRHO_SAFE_ASSERT_RETURN(uiPtr != nullptr,); ui = uiPtr; + uiData->initializing = false; #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI // unused diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 1f1192a8..d03ab16e 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -33,7 +33,13 @@ # define DISTRHO_UI_IS_STANDALONE 0 #endif -#if defined(DISTRHO_PLUGIN_TARGET_VST2) +#ifdef DISTRHO_PLUGIN_TARGET_VST3 +# define DISTRHO_UI_IS_VST3 1 +#else +# define DISTRHO_UI_IS_VST3 0 +#endif + +#ifdef DISTRHO_PLUGIN_TARGET_VST2 # undef DISTRHO_UI_USER_RESIZABLE # define DISTRHO_UI_USER_RESIZABLE 0 #endif @@ -175,7 +181,8 @@ public: const uint width, const uint height, const double scaleFactor) - : Window(app, parentWindowHandle, width, height, scaleFactor, DISTRHO_UI_USER_RESIZABLE, false), + : Window(app, parentWindowHandle, width, height, scaleFactor, + DISTRHO_UI_USER_RESIZABLE, DISTRHO_UI_IS_VST3, false), ui(uiPtr), initializing(true), receivedReshapeDuringInit(false) @@ -332,6 +339,9 @@ struct UI::PrivateData { #endif char* bundlePath; + // Ignore initial resize events while initializing + bool initializing; + // Callbacks void* callbacksPtr; editParamFunc editParamCallbackFunc; @@ -355,6 +365,7 @@ struct UI::PrivateData { uiStateFileKeyRequest(nullptr), #endif bundlePath(nullptr), + initializing(true), callbacksPtr(nullptr), editParamCallbackFunc(nullptr), setParamCallbackFunc(nullptr), From 431d330d879ad48e8d4869f97c3e222d631bc003 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 02:04:29 +0000 Subject: [PATCH 268/504] Move common vst3 code to new file; Skip params for shared controller Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 26 +- distrho/src/DistrhoPluginVST3.cpp | 336 +++++--------------------- distrho/src/DistrhoPluginVST3.hpp | 275 +++++++++++++++++++++ distrho/src/DistrhoUIPrivateData.hpp | 26 +- distrho/src/DistrhoUIVST3.cpp | 23 -- examples/CVPort/Makefile | 3 +- 6 files changed, 340 insertions(+), 349 deletions(-) create mode 100644 distrho/src/DistrhoPluginVST3.hpp diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index a48b20c5..017eb234 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -19,6 +19,10 @@ #include "../DistrhoPlugin.hpp" +#ifdef DISTRHO_PLUGIN_TARGET_VST3 +# include "DistrhoPluginVST3.hpp" +#endif + #include START_NAMESPACE_DISTRHO @@ -81,28 +85,6 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por } } -// #ifdef DISTRHO_PLUGIN_TARGET_VST3 -enum Vst3InternalParameters { - kVst3InternalParameterActive = 0, - kVst3InternalParameterBufferSize, - kVst3InternalParameterSampleRate, -# if DISTRHO_PLUGIN_WANT_LATENCY - kVst3InternalParameterLatency, -# endif -# if DISTRHO_PLUGIN_WANT_PROGRAMS - kVst3InternalParameterProgram, -# endif - kVst3InternalParameterBaseCount, -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, - kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, - kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end -# else - kVst3InternalParameterCount = kVst3InternalParameterBaseCount -# endif -}; -// #endif - // ----------------------------------------------------------------------- // Plugin private data diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 7464e1a6..924429d0 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -18,20 +18,6 @@ #include "../DistrhoPluginUtils.hpp" #include "../extra/ScopedPointer.hpp" -#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI == 1 && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS == 0 -# define DPF_VST3_USES_SEPARATE_CONTROLLER -#endif - #define DPF_VST3_MAX_BUFFER_SIZE 32768 #define DPF_VST3_MAX_SAMPLE_RATE 384000 #define DPF_VST3_MAX_LATENCY DPF_VST3_MAX_SAMPLE_RATE * 10 @@ -46,21 +32,6 @@ #include "travesty/factory.h" #include "travesty/host.h" -#ifdef DISTRHO_PROPER_CPP11_SUPPORT -# include -#else -// quick and dirty std::atomic replacement for the things we need -namespace std { - struct atomic_int { - volatile int value; - explicit atomic_int(volatile int v) noexcept : value(v) {} - int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } - int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } - operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); } - }; -} -#endif - #include #include #include @@ -71,8 +42,6 @@ namespace std { * - have parameter outputs host-provided UI working in at least 1 host * - parameter groups via unit ids * - test parameter changes from DSP (aka requestParameterValueChange) - * - how to hide parameter outputs? - * - how to hide program parameter? * - test receiving midi CC * - implement getParameterNormalized/setParameterNormalized for MIDI CC params ? * - fully implemented parameter stuff and verify @@ -241,190 +210,6 @@ const char* tuid2str(const v3_tuid iid) return buf; } -// -------------------------------------------------------------------------------------------------------------------- - -static bool strcmp_utf16(const int16_t* const str16, const char* const str8) -{ - size_t i = 0; - for (; str8[i] != '\0'; ++i) - { - const uint8_t char8 = static_cast(str8[i]); - - // skip non-ascii chars, unsupported - if (char8 >= 0x80) - return false; - - if (str16[i] != char8) - return false; - } - - return str16[i] == str8[i]; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static size_t strlen_utf16(const int16_t* const str) -{ - size_t i = 0; - - while (str[i] != 0) - ++i; - - return i; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static 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'; - } -} - -void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length) -{ - DISTRHO_SAFE_ASSERT_RETURN(length > 0,); - - if (const size_t len = std::min(strlen_utf16(src), length-1U)) - { - for (size_t i=0; i= 0x80) - continue; - - dst[i] = src[i]; - } - dst[len] = 0; - } - else - { - dst[0] = 0; - } -} - -void strncpy_utf16(int16_t* 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)) - { - for (size_t i=0; i= 0x80) - continue; - - dst[i] = src[i]; - } - dst[len] = 0; - } - else - { - dst[0] = 0; - } -} - -// -------------------------------------------------------------------------------------------------------------------- - -template -static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - std::snprintf(dst, size-1, format, value); - dst[size-1] = '\0'; -} - -template -static void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - - char* const tmpbuf = (char*)std::malloc(size); - DISTRHO_SAFE_ASSERT_RETURN(tmpbuf != nullptr,); - - std::snprintf(tmpbuf, size-1, format, value); - tmpbuf[size-1] = '\0'; - - strncpy_utf16(dst, tmpbuf, size); - std::free(tmpbuf); -} - -static inline -void snprintf_u32(char* const dst, const uint32_t value, const size_t size) -{ - return snprintf_t(dst, value, "%u", size); -} - -static inline -void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size) -{ - return snprintf_utf16_t(dst, value, "%f", size); -} - -static inline -void snprintf_i32_utf16(int16_t* const dst, const int value, const size_t size) -{ - return snprintf_utf16_t(dst, value, "%d", size); -} - -// -------------------------------------------------------------------------------------------------------------------- -// handy way to create a utf16 string from a utf8 one on the current function scope, used for message strings - -struct ScopedUTF16String { - int16_t* str; - ScopedUTF16String(const char* const s) noexcept; - ~ScopedUTF16String() noexcept; - operator const int16_t*() const noexcept; -}; - -// -------------------------------------------------------------------------------------------------------------------- - -ScopedUTF16String::ScopedUTF16String(const char* const s) noexcept - : str(nullptr) -{ - const size_t len = std::strlen(s); - str = static_cast(std::malloc(sizeof(int16_t) * (len + 1))); - DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,); - strncpy_utf16(str, s, len + 1); -} - -ScopedUTF16String::~ScopedUTF16String() noexcept -{ - std::free(str); -} - -ScopedUTF16String::operator const int16_t*() const noexcept -{ - return str; -} - -// -------------------------------------------------------------------------------------------------------------------- -// handy way to create a utf8 string from a utf16 one on the current function scope - -struct ScopedUTF8String { - char str[128]; - - ScopedUTF8String(const int16_t* const s) noexcept - { - strncpy_utf8(str, s, 128); - } - - operator const char*() const noexcept - { - return str; - } -}; - // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_create (implemented on UI side) @@ -469,7 +254,7 @@ public: : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), fComponentHandler(nullptr), #if DISTRHO_PLUGIN_HAS_UI -# ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +# if DPF_VST3_USES_SEPARATE_CONTROLLER fConnectionFromCompToCtrl(nullptr), # endif fConnectionFromCtrlToView(nullptr), @@ -563,9 +348,10 @@ public: { fCachedParameterValues = new float[extraParameterCount]; - fCachedParameterValues[kVst3InternalParameterActive] = 0.0f; + #if DPF_VST3_USES_SEPARATE_CONTROLLER fCachedParameterValues[kVst3InternalParameterBufferSize] = fPlugin.getBufferSize(); fCachedParameterValues[kVst3InternalParameterSampleRate] = fPlugin.getSampleRate(); + #endif #if DISTRHO_PLUGIN_WANT_LATENCY fCachedParameterValues[kVst3InternalParameterLatency] = fLastKnownLatency; #endif @@ -1243,11 +1029,13 @@ public: fPlugin.setSampleRate(setup->sample_rate, true); fPlugin.setBufferSize(setup->max_block_size, true); + #if DPF_VST3_USES_SEPARATE_CONTROLLER fCachedParameterValues[kVst3InternalParameterBufferSize] = setup->max_block_size; fParameterValuesChangedDuringProcessing[kVst3InternalParameterBufferSize] = true; fCachedParameterValues[kVst3InternalParameterSampleRate] = setup->sample_rate; fParameterValuesChangedDuringProcessing[kVst3InternalParameterSampleRate] = true; + #endif #if DISTRHO_PLUGIN_HAS_UI fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = true; #endif @@ -1272,8 +1060,6 @@ public: fPlugin.deactivateIfNeeded(); } - fCachedParameterValues[kVst3InternalParameterActive] = processing ? 1.0f : 0.0f; - fParameterValuesChangedDuringProcessing[kVst3InternalParameterActive] = true; return V3_OK; } @@ -1608,12 +1394,7 @@ public: switch (rindex) { - case kVst3InternalParameterActive: - info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; - info->step_count = 1; - strncpy_utf16(info->title, "Active", 128); - strncpy_utf16(info->short_title, "Active", 128); - return V3_OK; + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterBufferSize: info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; info->step_count = DPF_VST3_MAX_BUFFER_SIZE - 1; @@ -1627,6 +1408,7 @@ public: strncpy_utf16(info->short_title, "Sample Rate", 128); strncpy_utf16(info->units, "frames", 128); return V3_OK; + #endif #if DISTRHO_PLUGIN_WANT_LATENCY case kVst3InternalParameterLatency: info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; @@ -1714,15 +1496,14 @@ public: switch (rindex) { - case kVst3InternalParameterActive: - strncpy_utf16(output, normalized > 0.5 ? "On" : "Off", 128); - return V3_OK; + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterBufferSize: snprintf_i32_utf16(output, static_cast(normalized * DPF_VST3_MAX_BUFFER_SIZE + 0.5), 128); return V3_OK; case kVst3InternalParameterSampleRate: snprintf_f32_utf16(output, std::round(normalized * DPF_VST3_MAX_SAMPLE_RATE), 128); return V3_OK; + #endif #if DISTRHO_PLUGIN_WANT_LATENCY case kVst3InternalParameterLatency: snprintf_f32_utf16(output, std::round(normalized * DPF_VST3_MAX_LATENCY), 128); @@ -1783,15 +1564,14 @@ public: { switch (rindex) { - case kVst3InternalParameterActive: - *output = strcmp_utf16(input, "On") ? 1.0 : 0.0; - return V3_OK; + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterBufferSize: *output = static_cast(std::atoi(ScopedUTF8String(input))) / DPF_VST3_MAX_BUFFER_SIZE; return V3_OK; case kVst3InternalParameterSampleRate: *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_SAMPLE_RATE; return V3_OK; + #endif #if DISTRHO_PLUGIN_WANT_LATENCY case kVst3InternalParameterLatency: *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_LATENCY; @@ -1852,12 +1632,12 @@ public: switch (rindex) { - case kVst3InternalParameterActive: - return normalized > 0.5 ? 1.0 : 0.0; + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterBufferSize: return std::round(normalized * DPF_VST3_MAX_BUFFER_SIZE); case kVst3InternalParameterSampleRate: return normalized * DPF_VST3_MAX_SAMPLE_RATE; + #endif #if DISTRHO_PLUGIN_WANT_LATENCY case kVst3InternalParameterLatency: return normalized * DPF_VST3_MAX_LATENCY; @@ -1897,12 +1677,12 @@ public: { switch (rindex) { - case kVst3InternalParameterActive: - return plain; + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterBufferSize: return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_BUFFER_SIZE)); case kVst3InternalParameterSampleRate: return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_SAMPLE_RATE)); + #endif #if DISTRHO_PLUGIN_WANT_LATENCY case kVst3InternalParameterLatency: return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_LATENCY)); @@ -1933,11 +1713,13 @@ public: return 0.0; #endif + #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS switch (rindex) { - case kVst3InternalParameterActive: + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterBufferSize: case kVst3InternalParameterSampleRate: + #endif #if DISTRHO_PLUGIN_WANT_LATENCY case kVst3InternalParameterLatency: #endif @@ -1946,6 +1728,7 @@ public: #endif return plainParameterToNormalized(rindex, fCachedParameterValues[rindex]); } + #endif const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0); @@ -1971,23 +1754,14 @@ public: switch (rindex) { - case kVst3InternalParameterActive: - if (fCachedParameterValues[rindex] > 0.5f) - { - if (! fPlugin.isActive()) - fPlugin.activate(); - } - else - { - fPlugin.deactivateIfNeeded(); - } - break; + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterBufferSize: fPlugin.setBufferSize(fCachedParameterValues[rindex], true); break; case kVst3InternalParameterSampleRate: fPlugin.setSampleRate(fCachedParameterValues[rindex], true); break; + #endif #if DISTRHO_PLUGIN_WANT_LATENCY case kVst3InternalParameterLatency: flags = V3_RESTART_LATENCY_CHANGED; @@ -2019,10 +1793,12 @@ public: return V3_OK; } + #if DPF_VST3_USES_SEPARATE_CONTROLLER const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, V3_INVALID_ARG); setNormalizedPluginParameterValue(index, normalized); + #endif return V3_OK; } @@ -2036,7 +1812,7 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_connection_point interface calls - #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER void comp2ctrl_connect(v3_connection_point** const other) { fConnectionFromCompToCtrl = other; @@ -2230,7 +2006,7 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (std::strcmp(msgid, "midi") == 0) { - #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR); return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message); #else @@ -2242,7 +2018,7 @@ public: #if DISTRHO_PLUGIN_WANT_STATE if (std::strcmp(msgid, "state-set") == 0) { - #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR); return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message); #else @@ -2352,7 +2128,7 @@ private: // VST3 stuff v3_component_handler** fComponentHandler; #if DISTRHO_PLUGIN_HAS_UI - #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER v3_connection_point** fConnectionFromCompToCtrl; #endif v3_connection_point** fConnectionFromCtrlToView; @@ -2401,7 +2177,8 @@ private: v3_param_id paramId; float curValue; - for (v3_param_id i=kVst3InternalParameterActive; i<=kVst3InternalParameterSampleRate; ++i) + #if DPF_VST3_USES_SEPARATE_CONTROLLER + for (v3_param_id i=kVst3InternalParameterBufferSize; i<=kVst3InternalParameterSampleRate; ++i) { if (! fParameterValuesChangedDuringProcessing[i]) continue; @@ -2410,6 +2187,7 @@ private: fParameterValuesChangedDuringProcessing[i] = false; addParameterDataToHostOutputEvents(outparamsptr, i, curValue); } + #endif for (uint32_t i=0; i connectionCtrl2View; #endif -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER ScopedPointer connectionComp2Ctrl; ScopedPointer vst3; #else @@ -2961,12 +2739,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // cached values v3_component_handler** handler; v3_host_application** const hostApplicationFromFactory; -#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER +#if !DPF_VST3_USES_SEPARATE_CONTROLLER v3_host_application** const hostApplicationFromComponent; #endif v3_host_application** hostApplicationFromInitialize; -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller(v3_host_application** const hostApp) : refcounter(1), vst3(nullptr), @@ -2978,7 +2756,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { #endif handler(nullptr), hostApplicationFromFactory(hostApp), -#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER +#if !DPF_VST3_USES_SEPARATE_CONTROLLER hostApplicationFromComponent(hostComp), #endif hostApplicationFromInitialize(nullptr) @@ -2988,7 +2766,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // make sure host application is valid through out this controller lifetime if (hostApplicationFromFactory != nullptr) v3_cpp_obj_ref(hostApplicationFromFactory); -#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER +#if !DPF_VST3_USES_SEPARATE_CONTROLLER if (hostApplicationFromComponent != nullptr) v3_cpp_obj_ref(hostApplicationFromComponent); #endif @@ -3024,12 +2802,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { #if DISTRHO_PLUGIN_HAS_UI connectionCtrl2View = nullptr; #endif -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER connectionComp2Ctrl = nullptr; vst3 = nullptr; #endif -#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER +#if !DPF_VST3_USES_SEPARATE_CONTROLLER if (hostApplicationFromComponent != nullptr) v3_cpp_obj_unref(hostApplicationFromComponent); #endif @@ -3065,7 +2843,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } #endif -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER if (v3_tuid_match(iid, v3_connection_point_iid)) { d_stdout("query_interface_edit_controller => %p %s %p | OK convert %p", @@ -3105,7 +2883,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return refcount; } -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER /** * Some hosts will have unclean instances of a few of the controller child classes at this point. * We check for those here, going through the whole possible chain to see if it is safe to delete. @@ -3144,7 +2922,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *static_cast(self); // check if already initialized -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 == nullptr, V3_INVALID_ARG); #else DISTRHO_SAFE_ASSERT_RETURN(! controller->initialized, V3_INVALID_ARG); @@ -3160,7 +2938,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // save it for later so we can unref it controller->hostApplicationFromInitialize = hostApplication; -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER // provide the factory application to the plugin if this new one is missing if (hostApplication == nullptr) hostApplication = controller->hostApplicationFromFactory; @@ -3195,7 +2973,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { d_stdout("dpf_edit_controller::terminate => %p", self); dpf_edit_controller* const controller = *static_cast(self); -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER // check if already terminated DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_INVALID_ARG); @@ -3226,7 +3004,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; @@ -3246,7 +3024,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED); #endif @@ -3262,7 +3040,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED); #endif @@ -3396,7 +3174,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // we require a host application for message creation v3_host_application** const host = controller->hostApplicationFromInitialize != nullptr ? controller->hostApplicationFromInitialize -#ifndef DPF_VST3_USES_SEPARATE_CONTROLLER +#if !DPF_VST3_USES_SEPARATE_CONTROLLER : controller->hostApplicationFromComponent != nullptr ? controller->hostApplicationFromComponent #endif @@ -3646,7 +3424,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { struct dpf_component : v3_component_cpp { std::atomic_int refcounter; ScopedPointer processor; -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER ScopedPointer connectionComp2Ctrl; #else ScopedPointer controller; @@ -3691,7 +3469,7 @@ struct dpf_component : v3_component_cpp { { d_stdout("~dpf_component()"); processor = nullptr; -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER connectionComp2Ctrl = nullptr; #else controller = nullptr; @@ -3743,7 +3521,7 @@ struct dpf_component : v3_component_cpp { return V3_OK; } -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER if (v3_tuid_match(iid, v3_connection_point_iid)) { d_stdout("query_interface_component => %p %s %p | OK convert %p", @@ -3815,7 +3593,7 @@ struct dpf_component : v3_component_cpp { } } -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl) { if (const int refcount = point->refcounter) @@ -3880,7 +3658,7 @@ struct dpf_component : v3_component_cpp { // create the actual plugin component->vst3 = new PluginVst3(hostApplication); - #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER // set connection point if needed if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl) { @@ -4133,7 +3911,7 @@ struct dpf_factory : v3_plugin_factory_cpp { if (hostContext != nullptr) v3_cpp_obj_unref(hostContext); - #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER if (gControllerGarbage.size() != 0) { d_stdout("DPF notice: cleaning up previously undeleted controllers now"); @@ -4271,7 +4049,7 @@ struct dpf_factory : v3_plugin_factory_cpp { return V3_OK; } - #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER // create edit controller if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_controller) && v3_tuid_match(iid, v3_edit_controller_iid)) { @@ -4299,8 +4077,7 @@ struct dpf_factory : v3_plugin_factory_cpp { DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); info->cardinality = 0x7FFFFFFF; - // TODO FIXME -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI info->class_flags = V3_DISTRIBUTABLE; #endif std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); @@ -4323,8 +4100,7 @@ struct dpf_factory : v3_plugin_factory_cpp { DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); info->cardinality = 0x7FFFFFFF; - // TODO FIXME -#ifdef DPF_VST3_USES_SEPARATE_CONTROLLER +#if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI info->class_flags = V3_DISTRIBUTABLE; #endif std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); diff --git a/distrho/src/DistrhoPluginVST3.hpp b/distrho/src/DistrhoPluginVST3.hpp new file mode 100644 index 00000000..ca0b6735 --- /dev/null +++ b/distrho/src/DistrhoPluginVST3.hpp @@ -0,0 +1,275 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_PLUGIN_VST3_HPP_INCLUDED +#define DISTRHO_PLUGIN_VST3_HPP_INCLUDED + +#include "DistrhoPluginChecks.h" +#include "../DistrhoUtils.hpp" + +#include +#include + +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI == 1 && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS == 0 +# define DPF_VST3_USES_SEPARATE_CONTROLLER 1 +#else +# define DPF_VST3_USES_SEPARATE_CONTROLLER 0 +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#ifdef DISTRHO_PROPER_CPP11_SUPPORT +# include +#else +// quick and dirty std::atomic replacement for the things we need +namespace std { + struct atomic_int { + volatile int value; + explicit atomic_int(volatile int v) noexcept : value(v) {} + int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } + int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } + operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); } + }; +}; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +enum Vst3InternalParameters { +#if DPF_VST3_USES_SEPARATE_CONTROLLER + kVst3InternalParameterBufferSize, + kVst3InternalParameterSampleRate, +#endif +#if DISTRHO_PLUGIN_WANT_LATENCY + kVst3InternalParameterLatency, +#endif +#if DISTRHO_PLUGIN_WANT_PROGRAMS + kVst3InternalParameterProgram, +#endif + kVst3InternalParameterBaseCount, +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, + kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, + kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end +#else + kVst3InternalParameterCount = kVst3InternalParameterBaseCount +#endif +}; + +// -------------------------------------------------------------------------------------------------------------------- + +static inline +bool strcmp_utf16(const int16_t* const str16, const char* const str8) +{ + size_t i = 0; + for (; str8[i] != '\0'; ++i) + { + const uint8_t char8 = static_cast(str8[i]); + + // skip non-ascii chars, unsupported + if (char8 >= 0x80) + return false; + + if (str16[i] != char8) + return false; + } + + return str16[i] == str8[i]; +} + +// -------------------------------------------------------------------------------------------------------------------- + +static inline +size_t strlen_utf16(const int16_t* const str) +{ + size_t i = 0; + + while (str[i] != 0) + ++i; + + return i; +} + +// -------------------------------------------------------------------------------------------------------------------- + +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) +{ + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); + + if (const size_t len = std::min(strlen_utf16(src), length-1U)) + { + for (size_t i=0; i= 0x80) + continue; + + dst[i] = src[i]; + } + dst[len] = 0; + } + else + { + dst[0] = 0; + } +} + +static inline +void strncpy_utf16(int16_t* 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)) + { + for (size_t i=0; i= 0x80) + continue; + + dst[i] = src[i]; + } + dst[len] = 0; + } + else + { + dst[0] = 0; + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +template +static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + std::snprintf(dst, size-1, format, value); + dst[size-1] = '\0'; +} + +template +static void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + + char* const tmpbuf = (char*)std::malloc(size); + DISTRHO_SAFE_ASSERT_RETURN(tmpbuf != nullptr,); + + std::snprintf(tmpbuf, size-1, format, value); + tmpbuf[size-1] = '\0'; + + strncpy_utf16(dst, tmpbuf, size); + std::free(tmpbuf); +} + +static inline +void snprintf_u32(char* const dst, const uint32_t value, const size_t size) +{ + return snprintf_t(dst, value, "%u", size); +} + +static inline +void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size) +{ + return snprintf_utf16_t(dst, value, "%f", size); +} + +static inline +void snprintf_i32_utf16(int16_t* const dst, const int value, const size_t size) +{ + return snprintf_utf16_t(dst, value, "%d", size); +} + +// -------------------------------------------------------------------------------------------------------------------- +// handy way to create a utf16 string from a utf8 one on the current function scope, used for message strings + +struct ScopedUTF16String { + int16_t* str; + ScopedUTF16String(const char* const s) noexcept + : str(nullptr) + { + const size_t len = std::strlen(s); + str = static_cast(std::malloc(sizeof(int16_t) * (len + 1))); + DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,); + strncpy_utf16(str, s, len + 1); + } + + ~ScopedUTF16String() noexcept + { + std::free(str); + } + + operator const int16_t*() const noexcept + { + return str; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// handy way to create a utf8 string from a utf16 one on the current function scope (limited to 128 chars) + +struct ScopedUTF8String { + char str[128]; + + ScopedUTF8String(const int16_t* const s) noexcept + { + strncpy_utf8(str, s, 128); + } + + operator const char*() const noexcept + { + return str; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +#endif // DISTRHO_PLUGIN_VST3_HPP_INCLUDED diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index d03ab16e..378c15d4 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -19,6 +19,10 @@ #include "../DistrhoUI.hpp" +#ifdef DISTRHO_PLUGIN_TARGET_VST3 +# include "DistrhoPluginVST3.hpp" +#endif + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # include "../extra/Sleep.hpp" #else @@ -294,28 +298,6 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8 typedef void (*setSizeFunc) (void* ptr, uint width, uint height); typedef bool (*fileRequestFunc) (void* ptr, const char* key); -#if defined(DISTRHO_PLUGIN_TARGET_VST3) && !defined(DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED) -enum Vst3InternalParameters { - kVst3InternalParameterActive = 0, - kVst3InternalParameterBufferSize, - kVst3InternalParameterSampleRate, -# if DISTRHO_PLUGIN_WANT_LATENCY - kVst3InternalParameterLatency, -# endif -# if DISTRHO_PLUGIN_WANT_PROGRAMS - kVst3InternalParameterProgram, -# endif - kVst3InternalParameterBaseCount, -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT - kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, - kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, - kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end -# else - kVst3InternalParameterCount = kVst3InternalParameterBaseCount -# endif -}; -#endif - // ----------------------------------------------------------------------- // UI private data diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index c7de0a69..53873d47 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -21,21 +21,6 @@ #include "travesty/host.h" #include "travesty/view.h" -#ifdef DISTRHO_PROPER_CPP11_SUPPORT -# include -#else -// quick and dirty std::atomic replacement for the things we need -namespace std { - struct atomic_int { - volatile int value; - explicit atomic_int(volatile int v) noexcept : value(v) {} - int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } - int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } - operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); } - }; -}; -#endif - /* TODO items: * - mousewheel event * - key down/up events @@ -66,14 +51,6 @@ static constexpr const setStateFunc setStateCallback = nullptr; // Utility functions (defined on plugin side) const char* tuid2str(const v3_tuid iid); -void strncpy_utf16(int16_t* dst, const char* src, size_t length); - -struct ScopedUTF16String { - int16_t* str; - ScopedUTF16String(const char* const s) noexcept; - ~ScopedUTF16String() noexcept; - operator const int16_t*() const noexcept; -}; // -------------------------------------------------------------------------------------------------------------------- diff --git a/examples/CVPort/Makefile b/examples/CVPort/Makefile index 5833735e..c41d7cdc 100644 --- a/examples/CVPort/Makefile +++ b/examples/CVPort/Makefile @@ -23,8 +23,7 @@ include ../../Makefile.plugins.mk # -------------------------------------------------------------- # Enable all possible plugin types -TARGETS += jack -TARGETS += lv2_dsp +TARGETS = jack lv2_dsp vst3 all: $(TARGETS) From 4bcc59a2ab623660df063bc7ce4c191cafe5290d Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 02:13:19 +0000 Subject: [PATCH 269/504] Silent a compiler warning Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 924429d0..6714ac5b 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1427,20 +1427,20 @@ public: #endif } -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (rindex < kVst3InternalParameterCount) { const uint32_t index = static_cast(rindex - kVst3InternalParameterMidiCC_start); info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN; info->step_count = 127; char ccstr[24]; - snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", index / 130 + 1, index % 130); + snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", static_cast(index / 130) + 1, index % 130); strncpy_utf16(info->title, ccstr, 128); snprintf(ccstr, sizeof(ccstr)-1, "Ch.%d CC%d", index / 130 + 1, index % 130); strncpy_utf16(info->short_title, ccstr+5, 128); return V3_OK; } -#endif + #endif const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG); From 0452b25ff051d8a6ebbbf18a5e0cab64d2395a37 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 02:41:49 +0000 Subject: [PATCH 270/504] Fix vst3 build with instance access Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 8 ++++++-- distrho/src/DistrhoUIVST3.cpp | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 6714ac5b..689af271 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1029,16 +1029,16 @@ public: fPlugin.setSampleRate(setup->sample_rate, true); fPlugin.setBufferSize(setup->max_block_size, true); - #if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER fCachedParameterValues[kVst3InternalParameterBufferSize] = setup->max_block_size; fParameterValuesChangedDuringProcessing[kVst3InternalParameterBufferSize] = true; fCachedParameterValues[kVst3InternalParameterSampleRate] = setup->sample_rate; fParameterValuesChangedDuringProcessing[kVst3InternalParameterSampleRate] = true; - #endif #if DISTRHO_PLUGIN_HAS_UI fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = true; #endif + #endif if (active) fPlugin.activate(); @@ -1874,9 +1874,11 @@ public: { fConnectedToUI = true; + #if DPF_VST3_USES_SEPARATE_CONTROLLER fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = false; sendParameterSetToUI(kVst3InternalParameterSampleRate, fCachedParameterValues[kVst3InternalParameterSampleRate]); + #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS fParameterValueChangesForUI[kVst3InternalParameterProgram] = false; @@ -1921,12 +1923,14 @@ public: if (std::strcmp(msgid, "idle") == 0) { + #if DPF_VST3_USES_SEPARATE_CONTROLLER if (fParameterValueChangesForUI[kVst3InternalParameterSampleRate]) { fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = false; sendParameterSetToUI(kVst3InternalParameterSampleRate, fCachedParameterValues[kVst3InternalParameterSampleRate]); } + #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS if (fParameterValueChangesForUI[kVst3InternalParameterProgram]) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 53873d47..b6c97a01 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -379,10 +379,12 @@ public: { switch (rindex) { + #if DPF_VST3_USES_SEPARATE_CONTROLLER case kVst3InternalParameterSampleRate: DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG); fUI.setSampleRate(value, true); break; + #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS case kVst3InternalParameterProgram: DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG); From 4c5b77f2e37893d9efa69599136c495d70a79083 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 02:42:53 +0000 Subject: [PATCH 271/504] Do not build vst3 version of CVPort example, not usable yet Signed-off-by: falkTX --- examples/CVPort/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CVPort/Makefile b/examples/CVPort/Makefile index c41d7cdc..e1f01ff0 100644 --- a/examples/CVPort/Makefile +++ b/examples/CVPort/Makefile @@ -23,7 +23,7 @@ include ../../Makefile.plugins.mk # -------------------------------------------------------------- # Enable all possible plugin types -TARGETS = jack lv2_dsp vst3 +TARGETS = jack lv2_dsp all: $(TARGETS) From d48c7ffc0fe7fe1d0515b9737a3af64c58acdaf1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 03:07:39 +0000 Subject: [PATCH 272/504] Fix vst3 host-side resize after last commits Signed-off-by: falkTX --- distrho/src/DistrhoUIInternal.hpp | 6 +++--- distrho/src/DistrhoUIPrivateData.hpp | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 742ea537..041981d5 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -286,10 +286,10 @@ public: #ifdef DISTRHO_PLUGIN_TARGET_VST3 void setWindowSizeForVST3(const uint width, const uint height) { +# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI ui->setSize(width, height); -# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - // NOTE in external uis, the ui and window refer to the same object - uiData->window->setSize(width, height); +# else + uiData->window->setSizeForVST3(width, height); # endif } #endif diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 378c15d4..e3e846d4 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -231,6 +231,13 @@ public: puglBackendEnter(pData->view); } + #ifdef DISTRHO_PLUGIN_TARGET_VST3 + void setSizeForVST3(const uint width, const uint height) + { + puglSetWindowSize(pData->view, width, height); + } + #endif + protected: void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override { From d0413dfbd6580fcb89dcbee11cad9319975474cc Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 04:02:53 +0000 Subject: [PATCH 273/504] Fix vst3 initial size on macOS; More geometry constraint fixing --- dgl/src/Window.cpp | 10 ++++++++-- distrho/src/DistrhoUI.cpp | 13 +++++++++++-- distrho/src/DistrhoUIVST3.cpp | 14 ++++++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 146fffc7..d67ab2b4 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -201,8 +201,14 @@ void Window::setSize(uint width, uint height) if (pData->isEmbed) { const double scaleFactor = pData->scaleFactor; - const uint minWidth = static_cast(pData->minWidth * scaleFactor + 0.5); - const uint minHeight = static_cast(pData->minHeight * scaleFactor + 0.5); + uint minWidth = pData->minWidth; + uint minHeight = pData->minHeight; + + if (pData->autoScaling && scaleFactor != 1.0) + { + minWidth *= scaleFactor; + minHeight *= scaleFactor; + } // handle geometry constraints here if (width < minWidth) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 7f26308c..7366be34 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -199,7 +199,7 @@ UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetA Widget::setSize(width, height); if (automaticallyScaleAndSetAsMinimumSize) - setGeometryConstraints(width, height, true, true); + setGeometryConstraints(width, height, true, true, true); } #else // unused @@ -383,7 +383,16 @@ void UI::onResize(const ResizeEvent& ev) // NOTE: only used for VST3 void UI::requestSizeChange(const uint width, const uint height) { - uiData->setSizeCallback(width, height); +# ifdef DISTRHO_PLUGIN_TARGET_VST3 + if (uiData->initializing) + uiData->window->setSizeForVST3(width, height); + else + uiData->setSizeCallback(width, height); +# else + // unused + (void)width; + (void)height; +# endif } #endif diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index b6c97a01..00df1209 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -257,6 +257,7 @@ public: rect->right /= scaleFactor; rect->bottom /= scaleFactor; #endif + d_stdout("getSize request returning %i %i", rect->right, rect->bottom); return V3_OK; } @@ -300,6 +301,11 @@ public: uint minimumWidth, minimumHeight; bool keepAspectRatio; fUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); +#ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + minimumWidth /= scaleFactor; + minimumHeight /= scaleFactor; +#endif applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); return V3_TRUE; } @@ -1298,7 +1304,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { #endif rect->right = view->nextWidth; rect->bottom = view->nextHeight; - tmpUI.quit(); + d_stdout("dpf_plugin_view::get_size => %p | next size %u %u with scale factor %f", self, view->nextWidth, view->nextHeight, lastScaleFactor); return V3_OK; } @@ -1377,7 +1383,11 @@ struct dpf_plugin_view : v3_plugin_view_cpp { uint minimumWidth, minimumHeight; bool keepAspectRatio; tmpUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); - tmpUI.quit(); +#ifdef DISTRHO_OS_MAC + const double scaleFactor = tmpUI.getScaleFactor(); + minimumWidth /= scaleFactor; + minimumHeight /= scaleFactor; +#endif applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); return V3_TRUE; } From 33967164d989851b41964fee915f29dcdbaf1423 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 13:52:42 +0000 Subject: [PATCH 274/504] Give focus to VST3 UI when instructed by the host Signed-off-by: falkTX --- distrho/src/DistrhoUIVST3.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 00df1209..2f6225e2 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -242,6 +242,8 @@ public: v3_result onFocus(const bool state) { + if (state) + fUI.focus(); fUI.notifyFocusChanged(state); return V3_OK; } From 2342feadc0d3e13e8aecd749f142e75709555c48 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 14:02:49 +0000 Subject: [PATCH 275/504] Fix loading empty VST3 states Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 689af271..603e9678 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -684,6 +684,7 @@ public: const bool connectedToUI = fConnectionFromCtrlToView != nullptr && fConnectedToUI; #endif String key, value; + bool hasValue = false; bool fillingKey = true; // if filling key or value char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters) @@ -721,9 +722,14 @@ public: // append to temporary vars if (fillingKey) + { key += buffer + i; + } else + { value += buffer + i; + hasValue = true; + } // increase buffer offset by length of string i += std::strlen(buffer + i); @@ -742,6 +748,7 @@ public: queryingType = 's'; key.clear(); value.clear(); + hasValue = false; continue; } if (key == "__dpf_state_end__") @@ -750,6 +757,7 @@ public: queryingType = 'n'; key.clear(); value.clear(); + hasValue = false; continue; } if (key == "__dpf_parameters_begin__") @@ -759,6 +767,7 @@ public: queryingType = 'p'; key.clear(); value.clear(); + hasValue = false; continue; } if (key == "__dpf_parameters_end__") @@ -767,14 +776,15 @@ public: queryingType = 'x'; key.clear(); value.clear(); + hasValue = false; continue; } // no special key, swap between reading real key and value fillingKey = !fillingKey; - // if there is no value yet keep reading until we have one (TODO check empty values on purpose) - if (value.isEmpty()) + // if there is no value yet keep reading until we have one + if (! hasValue) continue; if (key == "__dpf_program__") @@ -847,11 +857,11 @@ public: fPlugin.setParameterValue(j, fvalue); break; } - } key.clear(); value.clear(); + hasValue = false; } } } From 5835cd7918ea65fe030e881b4836ab8e0545a357 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 14:15:42 +0000 Subject: [PATCH 276/504] Save vst3 parameters as integers as needed Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 603e9678..eb1e91ea 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -845,7 +845,6 @@ public: else fvalue = std::atof(value.buffer()); - // TODO atoi if integer fCachedParameterValues[kVst3InternalParameterBaseCount + j] = fvalue; #if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) @@ -895,8 +894,8 @@ public: const uint32_t stateCount = 0; #endif - // int64_t ignore = 0; - // v3_cpp_obj(stream)->seek(stream, 0, V3_SEEK_SET, &ignore); + int64_t ignore = 0; + v3_cpp_obj(stream)->seek(stream, 0, V3_SEEK_SET, &ignore); if (stateCount == 0 && paramCount == 0) { @@ -963,8 +962,10 @@ public: String tmpStr; tmpStr = fPlugin.getParameterSymbol(i); tmpStr += "\xff"; - // TODO cast to integer if needed - tmpStr += String(fPlugin.getParameterValue(i)); + if (fPlugin.getParameterHints(i) & kParameterIsInteger) + tmpStr += String(static_cast(std::round(fPlugin.getParameterValue(i)))); + else + tmpStr += String(fPlugin.getParameterValue(i)); tmpStr += "\xff"; state += tmpStr; From fb11675884a0fe166708e8887e9cdfda22d2f56e Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 14:40:42 +0000 Subject: [PATCH 277/504] Partially revert last commit, state seeking is not wanted Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index eb1e91ea..eab69554 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -692,9 +692,6 @@ public: buffer[sizeof(buffer)-1] = '\xff'; v3_result res; - // int64_t ignore = 0; - // v3_cpp_obj(stream)->seek(stream, 0, V3_SEEK_SET, &ignore); - for (int32_t pos = 0, term = 0, read; term == 0; pos += read) { res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); @@ -894,9 +891,6 @@ public: const uint32_t stateCount = 0; #endif - int64_t ignore = 0; - v3_cpp_obj(stream)->seek(stream, 0, V3_SEEK_SET, &ignore); - if (stateCount == 0 && paramCount == 0) { char buffer = '\0'; From 09ad9bb5701d892b71469ad809a0fd2596b20797 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 17:09:35 +0000 Subject: [PATCH 278/504] Allow JACK/Standalone to have resources/bundle path too Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 30 ++++++++++++++++++++++ distrho/src/DistrhoUtils.cpp | 42 +++++++++++++++---------------- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 8329d327..40446937 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -15,6 +15,7 @@ */ #include "DistrhoPluginInternal.hpp" +#include "../DistrhoPluginUtils.hpp" #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" @@ -32,6 +33,7 @@ #ifndef DISTRHO_OS_WINDOWS # include +# include #endif #ifndef JACK_METADATA_ORDER @@ -949,6 +951,34 @@ int main(int argc, char* argv[]) d_nextSampleRate = jackbridge_get_sample_rate(client); d_nextCanRequestParameterValueChanges = true; + #ifndef DISTRHO_OS_WINDOWS + // 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 + const PluginJack p(client); return 0; diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index eb39fdc5..772f4b67 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -94,40 +94,40 @@ const char* getResourcePath(const char* const bundlePath) noexcept { DISTRHO_SAFE_ASSERT_RETURN(bundlePath != nullptr, nullptr); -#if defined(DISTRHO_PLUGIN_TARGET_LV2) - static String bundlePathLV2; +#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_VST2) + static String resourcePath; - if (bundlePathLV2.isEmpty()) + if (resourcePath.isEmpty()) { - bundlePathLV2 = bundlePath; - bundlePathLV2 += DISTRHO_OS_SEP_STR "resources"; + resourcePath = bundlePath; +# ifdef DISTRHO_OS_MAC + resourcePath += "/Contents/Resources"; +# else + resourcePath += DISTRHO_OS_SEP_STR "resources"; +# endif } - return bundlePathLV2.buffer(); -#elif defined(DISTRHO_PLUGIN_TARGET_VST2) - static String bundlePathVST2; + return resourcePath.buffer(); +#elif defined(DISTRHO_PLUGIN_TARGET_LV2) + static String resourcePath; - if (bundlePathVST2.isEmpty()) + if (resourcePath.isEmpty()) { - bundlePathVST2 = bundlePath; -# ifdef DISTRHO_OS_MAC - bundlePathVST2 += "/Contents/Resources"; -# else - bundlePathVST2 += DISTRHO_OS_SEP_STR "resources"; -# endif + resourcePath = bundlePath; + resourcePath += DISTRHO_OS_SEP_STR "resources"; } - return bundlePathVST2.buffer(); + return resourcePath.buffer(); #elif defined(DISTRHO_PLUGIN_TARGET_VST3) - static String bundlePathVST3; + static String resourcePath; - if (bundlePathVST3.isEmpty()) + if (resourcePath.isEmpty()) { - bundlePathVST3 = bundlePath; - bundlePathVST3 += "/Contents/Resources"; + resourcePath = bundlePath; + resourcePath += "/Contents/Resources"; } - return bundlePathVST3.buffer(); + return resourcePath.buffer(); #endif return nullptr; From dabfd5d884035b67b9438a01274ec505f0c9389a Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 18:08:24 +0000 Subject: [PATCH 279/504] Allow to skip RtAudio fallback in JACK/Standalone mode Signed-off-by: falkTX --- Makefile.base.mk | 4 ++++ distrho/src/jackbridge/JackBridge.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index c4b0e05c..9f4c3f6e 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -262,6 +262,9 @@ endif HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) +ifeq ($(SKIP_RTAUDIO_FALLBACK),true) +CXXFLAGS += DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK +else ifeq ($(MACOS),true) HAVE_RTAUDIO = true else ifeq ($(WINDOWS),true) @@ -275,6 +278,7 @@ else ifeq ($(HAVE_PULSEAUDIO),true) HAVE_RTAUDIO = true endif endif +endif # backwards compat HAVE_JACK = true diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 64f50a39..02adf0f4 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -36,7 +36,7 @@ #include "../../extra/LibraryUtils.hpp" // in case JACK fails, we fallback to RtAudio's native API -#ifdef DISTRHO_PROPER_CPP11_SUPPORT +#if defined(DISTRHO_PROPER_CPP11_SUPPORT) && !defined(DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK) # include "RtAudioBridge.hpp" # ifdef RTAUDIO_API_TYPE # include "rtaudio/RtAudio.cpp" From 501dcafaa3d53074340c324c69f86ddcedd80506 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Dec 2021 18:19:26 +0000 Subject: [PATCH 280/504] Fix a typo Signed-off-by: falkTX --- Makefile.base.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 9f4c3f6e..b0aaee81 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -263,7 +263,7 @@ endif HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) ifeq ($(SKIP_RTAUDIO_FALLBACK),true) -CXXFLAGS += DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK +CXXFLAGS += -DDPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK else ifeq ($(MACOS),true) HAVE_RTAUDIO = true From 4a4f54531a0dc20073272c34390cde0a248410d2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 3 Jan 2022 23:16:27 +0000 Subject: [PATCH 281/504] Fix compiler warnings --- distrho/src/DistrhoPluginVST3.cpp | 11 +++++++++++ distrho/src/DistrhoPluginVST3.hpp | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index eab69554..aaaa9de1 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1312,8 +1312,10 @@ public: const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex); + #ifdef DPF_VST3_HAS_INTERNAL_PARAMETERS if (rindex < kVst3InternalParameterCount) continue; + #endif if (v3_cpp_obj(queue)->get_point_count(queue) <= 0) continue; @@ -1353,8 +1355,10 @@ public: const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex); + #ifdef DPF_VST3_HAS_INTERNAL_PARAMETERS if (rindex < kVst3InternalParameterCount) continue; + #endif const int32_t pcount = v3_cpp_obj(queue)->get_point_count(queue); @@ -1752,6 +1756,7 @@ public: return V3_INVALID_ARG; #endif + #ifdef DPF_VST3_HAS_INTERNAL_PARAMETERS if (rindex < kVst3InternalParameterBaseCount) { fCachedParameterValues[rindex] = normalizedParameterToPlain(rindex, normalized); @@ -1797,6 +1802,7 @@ public: return V3_OK; } + #endif #if DPF_VST3_USES_SEPARATE_CONTROLLER const uint32_t index = static_cast(rindex - kVst3InternalParameterCount); @@ -1805,6 +1811,11 @@ public: setNormalizedPluginParameterValue(index, normalized); #endif return V3_OK; + + #ifndef DPF_VST3_HAS_INTERNAL_PARAMETERS + // unused + (void)rindex; + #endif } v3_result setComponentHandler(v3_component_handler** const handler) noexcept diff --git a/distrho/src/DistrhoPluginVST3.hpp b/distrho/src/DistrhoPluginVST3.hpp index ca0b6735..90036588 100644 --- a/distrho/src/DistrhoPluginVST3.hpp +++ b/distrho/src/DistrhoPluginVST3.hpp @@ -83,6 +83,10 @@ enum Vst3InternalParameters { #endif }; +#if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT +# define DPF_VST3_HAS_INTERNAL_PARAMETERS +#endif + // -------------------------------------------------------------------------------------------------------------------- static inline From d8521efcdd36317421bd4cd3fccf58e1b9bcecf6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 3 Jan 2022 23:31:48 +0000 Subject: [PATCH 282/504] More compiler warning fixes --- distrho/src/DistrhoPluginVST3.cpp | 30 +++++++++++++++++++----------- distrho/src/DistrhoPluginVST3.hpp | 11 ++++++++++- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index aaaa9de1..68644c63 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1312,7 +1312,7 @@ public: const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex); - #ifdef DPF_VST3_HAS_INTERNAL_PARAMETERS + #if DPF_VST3_HAS_INTERNAL_PARAMETERS if (rindex < kVst3InternalParameterCount) continue; #endif @@ -1331,15 +1331,15 @@ public: } } -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount); -#else + #else fPlugin.run(inputs, outputs, data->nframes); -#endif + #endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT fHostEventOutputHandle = nullptr; -#endif + #endif // if there are any parameter changes after frame 0, set them here if (v3_param_changes** const inparamsptr = data->input_params) @@ -1355,7 +1355,7 @@ public: const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex); - #ifdef DPF_VST3_HAS_INTERNAL_PARAMETERS + #if DPF_VST3_HAS_INTERNAL_PARAMETERS if (rindex < kVst3InternalParameterCount) continue; #endif @@ -1718,7 +1718,11 @@ public: { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT // TODO something to do here? - if (rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end) + if ( + #if !DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS + rindex >= kVst3InternalParameterMidiCC_start && + #endif + rindex <= kVst3InternalParameterMidiCC_end) return 0.0; #endif @@ -1752,11 +1756,15 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT // TODO something to do here? - if (rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end) + if ( + #if !DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS + rindex >= kVst3InternalParameterMidiCC_start && + #endif + rindex <= kVst3InternalParameterMidiCC_end) return V3_INVALID_ARG; #endif - #ifdef DPF_VST3_HAS_INTERNAL_PARAMETERS + #if DPF_VST3_HAS_INTERNAL_PARAMETERS && !DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS if (rindex < kVst3InternalParameterBaseCount) { fCachedParameterValues[rindex] = normalizedParameterToPlain(rindex, normalized); @@ -1812,7 +1820,7 @@ public: #endif return V3_OK; - #ifndef DPF_VST3_HAS_INTERNAL_PARAMETERS + #if !DPF_VST3_HAS_INTERNAL_PARAMETERS // unused (void)rindex; #endif diff --git a/distrho/src/DistrhoPluginVST3.hpp b/distrho/src/DistrhoPluginVST3.hpp index 90036588..a67c2218 100644 --- a/distrho/src/DistrhoPluginVST3.hpp +++ b/distrho/src/DistrhoPluginVST3.hpp @@ -84,7 +84,16 @@ enum Vst3InternalParameters { }; #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT -# define DPF_VST3_HAS_INTERNAL_PARAMETERS +# define DPF_VST3_HAS_INTERNAL_PARAMETERS 1 +#else +# define DPF_VST3_HAS_INTERNAL_PARAMETERS 0 +#endif + +#if DPF_VST3_HAS_INTERNAL_PARAMETERS && DISTRHO_PLUGIN_WANT_MIDI_INPUT && \ + !(DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS) +# define DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS 1 +#else +# define DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS 0 #endif // -------------------------------------------------------------------------------------------------------------------- From 6d983cbfe7ae99cda177831b8376f6f8d8a459fa Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 4 Jan 2022 13:59:27 +0000 Subject: [PATCH 283/504] Fix pure opengl3 build --- dgl/OpenGL.hpp | 8 ++++++++ dgl/src/NanoVG.cpp | 2 +- dgl/src/OpenGL.cpp | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 67a0ddd2..629d831f 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -129,7 +129,11 @@ ImageFormat asDISTRHOImageFormat(const GLenum format) { switch (format) { +#ifdef DGL_USE_OPENGL3 + case GL_RED: +#else case GL_LUMINANCE: +#endif return kImageFormatGrayscale; case GL_BGR: return kImageFormatBGR; @@ -152,7 +156,11 @@ GLenum asOpenGLImageFormat(const ImageFormat format) case kImageFormatNull: break; case kImageFormatGrayscale: +#ifdef DGL_USE_OPENGL3 + return GL_RED; +#else return GL_LUMINANCE; +#endif case kImageFormatBGR: return GL_BGR; case kImageFormatBGRA: diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 772391b0..cb78fade 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -88,7 +88,7 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) # define NANOVG_GL2_IMPLEMENTATION #endif -#if defined(DISTRHO_OS_MAC) && defined(NANOVG_GL3_IMPLEMENTATION) +#if defined(DISTRHO_OS_MAC) && defined(NANOVG_GL2_IMPLEMENTATION) # define glBindVertexArray glBindVertexArrayAPPLE # define glDeleteVertexArrays glDeleteVertexArraysAPPLE # define glGenVertexArrays glGenVertexArraysAPPLE diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index eb619c3a..145c315c 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -38,10 +38,12 @@ START_NAMESPACE_DGL void Color::setFor(const GraphicsContext&, const bool includeAlpha) { +#ifndef DGL_USE_OPENGL3 if (includeAlpha) glColor4f(red, green, blue, alpha); else glColor3f(red, green, blue); +#endif } // ----------------------------------------------------------------------- @@ -52,6 +54,7 @@ static void drawLine(const Point& posStart, const Point& posEnd) { DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); +#ifndef DGL_USE_OPENGL3 glBegin(GL_LINES); { @@ -60,6 +63,7 @@ static void drawLine(const Point& posStart, const Point& posEnd) } glEnd(); +#endif } template @@ -102,6 +106,7 @@ static void drawCircle(const Point& pos, const T origy = pos.getY(); double t, x = size, y = 0.0; +#ifndef DGL_USE_OPENGL3 glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); for (uint i=0; i& pos, } glEnd(); +#endif } template @@ -162,6 +168,7 @@ static void drawTriangle(const Point& pos1, { DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); +#ifndef DGL_USE_OPENGL3 glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); { @@ -171,6 +178,7 @@ static void drawTriangle(const Point& pos1, } glEnd(); +#endif } template @@ -216,6 +224,7 @@ static void drawRectangle(const Rectangle& rect, const bool outline) { DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); +#ifndef DGL_USE_OPENGL3 glBegin(outline ? GL_LINE_LOOP : GL_QUADS); { @@ -238,6 +247,7 @@ static void drawRectangle(const Rectangle& rect, const bool outline) } glEnd(); +#endif } template @@ -316,11 +326,14 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point& pos, con setupCalled = true; } +#ifndef DGL_USE_OPENGL3 glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +#endif glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, textureId); +#ifndef DGL_USE_OPENGL3 glBegin(GL_QUADS); { @@ -343,6 +356,7 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point& pos, con } glEnd(); +#endif glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); @@ -528,6 +542,7 @@ void ImageBaseKnob::onDisplay() pData->isReady = true; } +#ifndef DGL_USE_OPENGL3 const int w = static_cast(getWidth()); const int h = static_cast(getHeight()); @@ -549,6 +564,7 @@ void ImageBaseKnob::onDisplay() { Rectangle(0, 0, w, h).draw(context); } +#endif glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); From 7b3b7bacf5b57afd294603642811e72452847f17 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 4 Jan 2022 14:44:18 +0000 Subject: [PATCH 284/504] Fix UI::getBundlePath() for VST2 --- distrho/src/DistrhoPluginVST2.cpp | 4 ++-- distrho/src/DistrhoUIInternal.hpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 6d55f4ff..fc2ef318 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -189,7 +189,7 @@ public: sendNoteCallback, setSizeCallback, nullptr, // TODO file request - nullptr, + d_nextBundlePath, plugin->getInstancePointer(), scaleFactor) # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -705,7 +705,7 @@ public: else { UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, fPlugin.getInstancePointer(), fLastScaleFactor); fVstRect.right = tmpUI.getWidth(); fVstRect.bottom = tmpUI.getHeight(); diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 041981d5..31d97f0d 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -62,6 +62,7 @@ public: uiData(new UI::PrivateData()) { uiData->sampleRate = sampleRate; + uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; uiData->dspPtr = dspPtr; uiData->bgColor = bgColor; From 01af6735cccfa25ce2d0f9c449900975a273176f Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 7 Jan 2022 17:48:55 +0000 Subject: [PATCH 285/504] Update to latest fontstash, allow nanovg freetype builds Signed-off-by: falkTX --- dgl/Makefile | 3 + dgl/src/nanovg/fontstash.h | 282 +++++++++++++++++++------------------ 2 files changed, 147 insertions(+), 138 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 04494eba..590fc1a8 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -25,6 +25,9 @@ endif ifeq ($(USE_NANOVG_FBO),true) BUILD_CXX_FLAGS += -DDGL_USE_NANOVG_FBO endif +ifeq ($(USE_NANOVG_FREETYPE),true) +BUILD_CXX_FLAGS += -DFONS_USE_FREETYPE $(shell $(PKG_CONFIG) --cflags freetype2) +endif ifeq ($(USE_RGBA),true) BUILD_CXX_FLAGS += -DDGL_USE_RGBA endif diff --git a/dgl/src/nanovg/fontstash.h b/dgl/src/nanovg/fontstash.h index cfe1072f..5941b104 100644 --- a/dgl/src/nanovg/fontstash.h +++ b/dgl/src/nanovg/fontstash.h @@ -157,31 +157,167 @@ struct FONSttFontImpl { }; typedef struct FONSttFontImpl FONSttFontImpl; -static FT_Library ftLibrary; +#else + +#define STB_TRUETYPE_IMPLEMENTATION + +#include "stb_truetype.h" + +struct FONSttFontImpl { + stbtt_fontinfo font; +}; +typedef struct FONSttFontImpl FONSttFontImpl; + +#endif + +#ifndef FONS_SCRATCH_BUF_SIZE +# define FONS_SCRATCH_BUF_SIZE 96000 +#endif +#ifndef FONS_HASH_LUT_SIZE +# define FONS_HASH_LUT_SIZE 256 +#endif +#ifndef FONS_INIT_FONTS +# define FONS_INIT_FONTS 4 +#endif +#ifndef FONS_INIT_GLYPHS +# define FONS_INIT_GLYPHS 256 +#endif +#ifndef FONS_INIT_ATLAS_NODES +# define FONS_INIT_ATLAS_NODES 256 +#endif +#ifndef FONS_VERTEX_COUNT +# define FONS_VERTEX_COUNT 1024 +#endif +#ifndef FONS_MAX_STATES +# define FONS_MAX_STATES 20 +#endif +#ifndef FONS_MAX_FALLBACKS +# define FONS_MAX_FALLBACKS 20 +#endif + +static unsigned int fons__hashint(unsigned int a) +{ + a += ~(a<<15); + a ^= (a>>10); + a += (a<<3); + a ^= (a>>6); + a += ~(a<<11); + a ^= (a>>16); + return a; +} + +static int fons__mini(int a, int b) +{ + return a < b ? a : b; +} + +static int fons__maxi(int a, int b) +{ + return a > b ? a : b; +} + +struct FONSglyph +{ + unsigned int codepoint; + int index; + int next; + short size, blur; + short x0,y0,x1,y1; + short xadv,xoff,yoff; +}; +typedef struct FONSglyph FONSglyph; + +struct FONSfont +{ + FONSttFontImpl font; + char name[64]; + unsigned char* data; + int dataSize; + unsigned char freeData; + float ascender; + float descender; + float lineh; + FONSglyph* glyphs; + int cglyphs; + int nglyphs; + int lut[FONS_HASH_LUT_SIZE]; + int fallbacks[FONS_MAX_FALLBACKS]; + int nfallbacks; +}; +typedef struct FONSfont FONSfont; + +struct FONSstate +{ + int font; + int align; + float size; + unsigned int color; + float blur; + float spacing; +}; +typedef struct FONSstate FONSstate; + +struct FONSatlasNode { + short x, y, width; +}; +typedef struct FONSatlasNode FONSatlasNode; + +struct FONSatlas +{ + int width, height; + FONSatlasNode* nodes; + int nnodes; + int cnodes; +}; +typedef struct FONSatlas FONSatlas; + +struct FONScontext +{ + FONSparams params; + float itw,ith; + unsigned char* texData; + int dirtyRect[4]; + FONSfont** fonts; + FONSatlas* atlas; + int cfonts; + int nfonts; + float verts[FONS_VERTEX_COUNT*2]; + float tcoords[FONS_VERTEX_COUNT*2]; + unsigned int colors[FONS_VERTEX_COUNT]; + int nverts; + unsigned char* scratch; + int nscratch; + FONSstate states[FONS_MAX_STATES]; + int nstates; + void (*handleError)(void* uptr, int error, int val); + void* errorUptr; +#ifdef FONS_USE_FREETYPE + FT_Library ftLibrary; +#endif +}; + +#ifdef FONS_USE_FREETYPE int fons__tt_init(FONScontext *context) { FT_Error ftError; - FONS_NOTUSED(context); - ftError = FT_Init_FreeType(&ftLibrary); + ftError = FT_Init_FreeType(&context->ftLibrary); return ftError == 0; } int fons__tt_done(FONScontext *context) { FT_Error ftError; - FONS_NOTUSED(context); - ftError = FT_Done_FreeType(ftLibrary); + ftError = FT_Done_FreeType(context->ftLibrary); return ftError == 0; } int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) { FT_Error ftError; - FONS_NOTUSED(context); //font->font.userdata = stash; - ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); + ftError = FT_New_Memory_Face(context->ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); return ftError == 0; } @@ -269,17 +405,10 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) #else -#define STB_TRUETYPE_IMPLEMENTATION static void* fons__tmpalloc(size_t size, void* up); static void fons__tmpfree(void* ptr, void* up); #define STBTT_malloc(x,u) fons__tmpalloc(x,u) #define STBTT_free(x,u) fons__tmpfree(x,u) -#include "stb_truetype.h" - -struct FONSttFontImpl { - stbtt_fontinfo font; -}; -typedef struct FONSttFontImpl FONSttFontImpl; int fons__tt_init(FONScontext *context) { @@ -350,129 +479,6 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) #endif -#ifndef FONS_SCRATCH_BUF_SIZE -# define FONS_SCRATCH_BUF_SIZE 96000 -#endif -#ifndef FONS_HASH_LUT_SIZE -# define FONS_HASH_LUT_SIZE 256 -#endif -#ifndef FONS_INIT_FONTS -# define FONS_INIT_FONTS 4 -#endif -#ifndef FONS_INIT_GLYPHS -# define FONS_INIT_GLYPHS 256 -#endif -#ifndef FONS_INIT_ATLAS_NODES -# define FONS_INIT_ATLAS_NODES 256 -#endif -#ifndef FONS_VERTEX_COUNT -# define FONS_VERTEX_COUNT 1024 -#endif -#ifndef FONS_MAX_STATES -# define FONS_MAX_STATES 20 -#endif -#ifndef FONS_MAX_FALLBACKS -# define FONS_MAX_FALLBACKS 20 -#endif - -static unsigned int fons__hashint(unsigned int a) -{ - a += ~(a<<15); - a ^= (a>>10); - a += (a<<3); - a ^= (a>>6); - a += ~(a<<11); - a ^= (a>>16); - return a; -} - -static int fons__mini(int a, int b) -{ - return a < b ? a : b; -} - -static int fons__maxi(int a, int b) -{ - return a > b ? a : b; -} - -struct FONSglyph -{ - unsigned int codepoint; - int index; - int next; - short size, blur; - short x0,y0,x1,y1; - short xadv,xoff,yoff; -}; -typedef struct FONSglyph FONSglyph; - -struct FONSfont -{ - FONSttFontImpl font; - char name[64]; - unsigned char* data; - int dataSize; - unsigned char freeData; - float ascender; - float descender; - float lineh; - FONSglyph* glyphs; - int cglyphs; - int nglyphs; - int lut[FONS_HASH_LUT_SIZE]; - int fallbacks[FONS_MAX_FALLBACKS]; - int nfallbacks; -}; -typedef struct FONSfont FONSfont; - -struct FONSstate -{ - int font; - int align; - float size; - unsigned int color; - float blur; - float spacing; -}; -typedef struct FONSstate FONSstate; - -struct FONSatlasNode { - short x, y, width; -}; -typedef struct FONSatlasNode FONSatlasNode; - -struct FONSatlas -{ - int width, height; - FONSatlasNode* nodes; - int nnodes; - int cnodes; -}; -typedef struct FONSatlas FONSatlas; - -struct FONScontext -{ - FONSparams params; - float itw,ith; - unsigned char* texData; - int dirtyRect[4]; - FONSfont** fonts; - FONSatlas* atlas; - int cfonts; - int nfonts; - float verts[FONS_VERTEX_COUNT*2]; - float tcoords[FONS_VERTEX_COUNT*2]; - unsigned int colors[FONS_VERTEX_COUNT]; - int nverts; - unsigned char* scratch; - int nscratch; - FONSstate states[FONS_MAX_STATES]; - int nstates; - void (*handleError)(void* uptr, int error, int val); - void* errorUptr; -}; - #ifdef STB_TRUETYPE_IMPLEMENTATION static void* fons__tmpalloc(size_t size, void* up) @@ -1684,8 +1690,8 @@ void fonsDeleteInternal(FONScontext* stash) if (stash->fonts) free(stash->fonts); if (stash->texData) free(stash->texData); if (stash->scratch) free(stash->scratch); - free(stash); fons__tt_done(stash); + free(stash); } void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) From 7f206b99dcccadbf26376ea6db0b9557800dd97d Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 7 Jan 2022 18:02:56 +0000 Subject: [PATCH 286/504] Fix previous commit, upstream is slightly borked Signed-off-by: falkTX --- dgl/src/nanovg/fontstash.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dgl/src/nanovg/fontstash.h b/dgl/src/nanovg/fontstash.h index 5941b104..4a28c6bb 100644 --- a/dgl/src/nanovg/fontstash.h +++ b/dgl/src/nanovg/fontstash.h @@ -160,7 +160,10 @@ typedef struct FONSttFontImpl FONSttFontImpl; #else #define STB_TRUETYPE_IMPLEMENTATION - +static void* fons__tmpalloc(size_t size, void* up); +static void fons__tmpfree(void* ptr, void* up); +#define STBTT_malloc(x,u) fons__tmpalloc(x,u) +#define STBTT_free(x,u) fons__tmpfree(x,u) #include "stb_truetype.h" struct FONSttFontImpl { @@ -405,11 +408,6 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) #else -static void* fons__tmpalloc(size_t size, void* up); -static void fons__tmpfree(void* ptr, void* up); -#define STBTT_malloc(x,u) fons__tmpalloc(x,u) -#define STBTT_free(x,u) fons__tmpfree(x,u) - int fons__tt_init(FONScontext *context) { FONS_NOTUSED(context); From 4ff990126dcef93090e2e92b9cb544d1f28972e3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 Jan 2022 02:50:07 +0000 Subject: [PATCH 287/504] Move DGL flags to Makefile.base.mk Signed-off-by: falkTX --- Makefile.base.mk | 32 ++++++++++++++++++++++++++++++++ dgl/Makefile | 19 ------------------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index b0aaee81..efccd0e3 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -426,6 +426,38 @@ HAVE_DGL = $(HAVE_X11) endif endif +# --------------------------------------------------------------------------------------------------------------------- +# Optional flags + +ifeq ($(NVG_DISABLE_SKIPPING_WHITESPACE),true) +BUILD_CXX_FLAGS += -DNVG_DISABLE_SKIPPING_WHITESPACE +endif + +ifneq ($(NVG_FONT_TEXTURE_FLAGS),) +BUILD_CXX_FLAGS += -DNVG_FONT_TEXTURE_FLAGS=$(NVG_FONT_TEXTURE_FLAGS) +endif + +ifeq ($(FILE_BROWSER_DISABLED),true) +BUILD_CXX_FLAGS += -DDGL_FILE_BROWSER_DISABLED +endif + +ifeq ($(USE_OPENGL3),true) +BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 +endif + +ifeq ($(USE_NANOVG_FBO),true) +BUILD_CXX_FLAGS += -DDGL_USE_NANOVG_FBO +endif + +ifeq ($(USE_NANOVG_FREETYPE),true) +BUILD_CXX_FLAGS += -DFONS_USE_FREETYPE $(shell $(PKG_CONFIG) --cflags freetype2) +endif + +ifeq ($(USE_RGBA),true) +BUILD_CXX_FLAGS += -DDGL_USE_RGBA +endif + + # --------------------------------------------------------------------------------------------------------------------- # Set app extension diff --git a/dgl/Makefile b/dgl/Makefile index 590fc1a8..6eb1d192 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -13,25 +13,6 @@ BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-un BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include LINK_FLAGS += $(DGL_LIBS) -ifeq ($(NVG_DISABLE_SKIPPING_WHITESPACE),true) -BUILD_CXX_FLAGS += -DNVG_DISABLE_SKIPPING_WHITESPACE -endif -ifneq ($(NVG_FONT_TEXTURE_FLAGS),) -BUILD_CXX_FLAGS += -DNVG_FONT_TEXTURE_FLAGS=$(NVG_FONT_TEXTURE_FLAGS) -endif -ifeq ($(USE_OPENGL3),true) -BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 -endif -ifeq ($(USE_NANOVG_FBO),true) -BUILD_CXX_FLAGS += -DDGL_USE_NANOVG_FBO -endif -ifeq ($(USE_NANOVG_FREETYPE),true) -BUILD_CXX_FLAGS += -DFONS_USE_FREETYPE $(shell $(PKG_CONFIG) --cflags freetype2) -endif -ifeq ($(USE_RGBA),true) -BUILD_CXX_FLAGS += -DDGL_USE_RGBA -endif - # TODO fix these after pugl-upstream is done BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers ifneq ($(MACOS),true) From 923664009361100bba378c6cfb94d68e39575476 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 Jan 2022 03:07:28 +0000 Subject: [PATCH 288/504] Add opengl3 as a possible build type Signed-off-by: falkTX --- Makefile.plugins.mk | 15 ++++++++ dgl/Makefile | 40 +++++++++++++++++-- dgl/src/OpenGL.cpp | 93 ++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 134 insertions(+), 14 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index ffa3d015..3bc341b9 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -213,6 +213,18 @@ HAVE_DGL = false endif endif +ifeq ($(UI_TYPE),opengl3) +ifeq ($(HAVE_OPENGL),true) +DGL_FLAGS += -DDGL_OPENGL -DDGL_USE_OPENGL3 -DHAVE_DGL +DGL_FLAGS += $(OPENGL_FLAGS) +DGL_LIBS += $(OPENGL_LIBS) +DGL_LIB = $(DPF_PATH)/build/libdgl-opengl3.a +HAVE_DGL = true +else +HAVE_DGL = false +endif +endif + ifeq ($(UI_TYPE),vulkan) ifeq ($(HAVE_VULKAN),true) DGL_FLAGS += -DDGL_VULKAN -DHAVE_DGL @@ -313,6 +325,9 @@ $(DPF_PATH)/build/libdgl-cairo.a: $(DPF_PATH)/build/libdgl-opengl.a: $(MAKE) -C $(DPF_PATH)/dgl opengl +$(DPF_PATH)/build/libdgl-opengl3.a: + $(MAKE) -C $(DPF_PATH)/dgl opengl3 + $(DPF_PATH)/build/libdgl-stub.a: $(MAKE) -C $(DPF_PATH)/dgl stub diff --git a/dgl/Makefile b/dgl/Makefile index 6eb1d192..893e34ee 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -69,6 +69,18 @@ endif # --------------------------------------------------------------------------------------------------------------------- +OBJS_opengl3 = $(OBJS_common) \ + ../build/dgl/OpenGL.cpp.opengl3.o \ + ../build/dgl/NanoVG.cpp.opengl3.o + +ifeq ($(MACOS),true) +OBJS_opengl3 += ../build/dgl/pugl.mm.opengl3.o +else +OBJS_opengl3 += ../build/dgl/pugl.cpp.opengl3.o +endif + +# --------------------------------------------------------------------------------------------------------------------- + OBJS_stub = $(OBJS_common) ifeq ($(MACOS),true) @@ -112,10 +124,11 @@ endif all: $(TARGETS) -cairo: ../build/libdgl-cairo.a -opengl: ../build/libdgl-opengl.a -stub: ../build/libdgl-stub.a -vulkan: ../build/libdgl-vulkan.a +cairo: ../build/libdgl-cairo.a +opengl: ../build/libdgl-opengl.a +opengl3: ../build/libdgl-opengl3.a +stub: ../build/libdgl-stub.a +vulkan: ../build/libdgl-vulkan.a # --------------------------------------------------------------------------------------------------------------------- @@ -131,6 +144,12 @@ vulkan: ../build/libdgl-vulkan.a $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ +../build/libdgl-opengl3.a: $(OBJS_opengl3) + -@mkdir -p ../build + @echo "Creating libdgl-opengl3.a" + $(SILENT)rm -f $@ + $(SILENT)$(AR) crs $@ $^ + ../build/libdgl-stub.a: $(OBJS_stub) -@mkdir -p ../build @echo "Creating libdgl-stub.a" @@ -191,6 +210,18 @@ vulkan: ../build/libdgl-vulkan.a # --------------------------------------------------------------------------------------------------------------------- +../build/dgl/%.cpp.opengl3.o: src/%.cpp + -@mkdir -p ../build/dgl + @echo "Compiling $< (OpenGL variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ + +../build/dgl/%.mm.opengl3.o: src/%.mm + -@mkdir -p ../build/dgl + @echo "Compiling $< (OpenGL variant)" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ + +# --------------------------------------------------------------------------------------------------------------------- + ../build/dgl/%.cpp.vulkan.o: src/%.cpp -@mkdir -p ../build/dgl @echo "Compiling $< (Vulkan variant)" @@ -214,6 +245,7 @@ debug: -include $(OBJS_common:%.o=%.d) -include $(OBJS_cairo:%.o=%.d) -include $(OBJS_opengl:%.o=%.d) +-include $(OBJS_opengl3:%.o=%.d) -include $(OBJS_stub:%.o=%.d) -include $(OBJS_vulkan:%.o=%.d) diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 145c315c..0992cc32 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -33,6 +33,15 @@ START_NAMESPACE_DGL +// ----------------------------------------------------------------------- + +#ifdef DGL_USE_OPENGL3 +static void notImplemented(const char* const name) +{ + d_stderr2("OpenGL3 function not implemented: %s", name); +} +#endif + // ----------------------------------------------------------------------- // Color @@ -43,18 +52,22 @@ void Color::setFor(const GraphicsContext&, const bool includeAlpha) glColor4f(red, green, blue, alpha); else glColor3f(red, green, blue); +#else + notImplemented("Color::setFor"); + // unused + (void)includeAlpha; #endif } // ----------------------------------------------------------------------- // Line +#ifndef DGL_USE_OPENGL3 template static void drawLine(const Point& posStart, const Point& posEnd) { DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); -#ifndef DGL_USE_OPENGL3 glBegin(GL_LINES); { @@ -63,23 +76,31 @@ static void drawLine(const Point& posStart, const Point& posEnd) } glEnd(); -#endif } +#endif template void Line::draw(const GraphicsContext&, const T width) { +#ifndef DGL_USE_OPENGL3 DISTRHO_SAFE_ASSERT_RETURN(width != 0,); glLineWidth(static_cast(width)); drawLine(posStart, posEnd); +#else + notImplemented("Line::draw"); +#endif } // deprecated calls template void Line::draw() { +#ifndef DGL_USE_OPENGL3 drawLine(posStart, posEnd); +#else + notImplemented("Line::draw"); +#endif } template class Line; @@ -92,6 +113,7 @@ template class Line; // ----------------------------------------------------------------------- // Circle +#ifndef DGL_USE_OPENGL3 template static void drawCircle(const Point& pos, const uint numSegments, @@ -106,7 +128,6 @@ static void drawCircle(const Point& pos, const T origy = pos.getY(); double t, x = size, y = 0.0; -#ifndef DGL_USE_OPENGL3 glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); for (uint i=0; i& pos, } glEnd(); -#endif } +#endif template void Circle::draw(const GraphicsContext&) { +#ifndef DGL_USE_OPENGL3 drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); +#else + notImplemented("Circle::draw"); +#endif } template @@ -134,20 +159,32 @@ void Circle::drawOutline(const GraphicsContext&, const T lineWidth) DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); +#ifndef DGL_USE_OPENGL3 drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); +#else + notImplemented("Circle::drawOutline"); +#endif } // deprecated calls template void Circle::draw() { +#ifndef DGL_USE_OPENGL3 drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); +#else + notImplemented("Circle::draw"); +#endif } template void Circle::drawOutline() { +#ifndef DGL_USE_OPENGL3 drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); +#else + notImplemented("Circle::drawOutline"); +#endif } template class Circle; @@ -160,6 +197,7 @@ template class Circle; // ----------------------------------------------------------------------- // Triangle +#ifndef DGL_USE_OPENGL3 template static void drawTriangle(const Point& pos1, const Point& pos2, @@ -168,7 +206,6 @@ static void drawTriangle(const Point& pos1, { DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); -#ifndef DGL_USE_OPENGL3 glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES); { @@ -178,13 +215,17 @@ static void drawTriangle(const Point& pos1, } glEnd(); -#endif } +#endif template void Triangle::draw(const GraphicsContext&) { +#ifndef DGL_USE_OPENGL3 drawTriangle(pos1, pos2, pos3, false); +#else + notImplemented("Triangle::draw"); +#endif } template @@ -193,20 +234,32 @@ void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); +#ifndef DGL_USE_OPENGL3 drawTriangle(pos1, pos2, pos3, true); +#else + notImplemented("Triangle::drawOutline"); +#endif } // deprecated calls template void Triangle::draw() { +#ifndef DGL_USE_OPENGL3 drawTriangle(pos1, pos2, pos3, false); +#else + notImplemented("Triangle::draw"); +#endif } template void Triangle::drawOutline() { +#ifndef DGL_USE_OPENGL3 drawTriangle(pos1, pos2, pos3, true); +#else + notImplemented("Triangle::drawOutline"); +#endif } template class Triangle; @@ -219,12 +272,12 @@ template class Triangle; // ----------------------------------------------------------------------- // Rectangle +#ifndef DGL_USE_OPENGL3 template static void drawRectangle(const Rectangle& rect, const bool outline) { DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),); -#ifndef DGL_USE_OPENGL3 glBegin(outline ? GL_LINE_LOOP : GL_QUADS); { @@ -247,13 +300,17 @@ static void drawRectangle(const Rectangle& rect, const bool outline) } glEnd(); -#endif } +#endif template void Rectangle::draw(const GraphicsContext&) { +#ifndef DGL_USE_OPENGL3 drawRectangle(*this, false); +#else + notImplemented("Rectangle::draw"); +#endif } template @@ -262,20 +319,32 @@ void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); +#ifndef DGL_USE_OPENGL3 drawRectangle(*this, true); +#else + notImplemented("Rectangle::drawOutline"); +#endif } // deprecated calls template void Rectangle::draw() { +#ifndef DGL_USE_OPENGL3 drawRectangle(*this, false); +#else + notImplemented("Rectangle::draw"); +#endif } template void Rectangle::drawOutline() { +#ifndef DGL_USE_OPENGL3 drawRectangle(*this, true); +#else + notImplemented("Rectangle::drawOutline"); +#endif } template class Rectangle; @@ -542,29 +611,33 @@ void ImageBaseKnob::onDisplay() pData->isReady = true; } -#ifndef DGL_USE_OPENGL3 const int w = static_cast(getWidth()); const int h = static_cast(getHeight()); if (pData->rotationAngle != 0) { +#ifndef DGL_USE_OPENGL3 glPushMatrix(); +#endif const int w2 = w/2; const int h2 = h/2; +#ifndef DGL_USE_OPENGL3 glTranslatef(static_cast(w2), static_cast(h2), 0.0f); glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); +#endif Rectangle(-w2, -h2, w, h).draw(context); +#ifndef DGL_USE_OPENGL3 glPopMatrix(); +#endif } else { Rectangle(0, 0, w, h).draw(context); } -#endif glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); From 5d7fd17f6a634ccd648a264743319c145928a27c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 Jan 2022 03:24:10 +0000 Subject: [PATCH 289/504] Fix UI::getBundlePath() for VST3 Signed-off-by: falkTX --- distrho/src/DistrhoUIVST3.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 2f6225e2..d0e8422c 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -47,6 +47,11 @@ static constexpr const sendNoteFunc sendNoteCallback = nullptr; static constexpr const setStateFunc setStateCallback = nullptr; #endif +// -------------------------------------------------------------------------------------------------------------------- +// Static data, see DistrhoPlugin.cpp + +extern const char* d_nextBundlePath; + // -------------------------------------------------------------------------------------------------------------------- // Utility functions (defined on plugin side) @@ -129,7 +134,7 @@ public: sendNoteCallback, setSizeCallback, nullptr, // TODO file request - nullptr, // bundlePath + d_nextBundlePath, instancePointer, scaleFactor) { From ad2b5b3d6e604dede370722ab04d89e2acaeb2fa Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 9 Jan 2022 09:31:17 +0000 Subject: [PATCH 290/504] Print OpenGL3 variant name during build Signed-off-by: falkTX --- dgl/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 893e34ee..867d36de 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -212,12 +212,12 @@ vulkan: ../build/libdgl-vulkan.a ../build/dgl/%.cpp.opengl3.o: src/%.cpp -@mkdir -p ../build/dgl - @echo "Compiling $< (OpenGL variant)" + @echo "Compiling $< (OpenGL3 variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ ../build/dgl/%.mm.opengl3.o: src/%.mm -@mkdir -p ../build/dgl - @echo "Compiling $< (OpenGL variant)" + @echo "Compiling $< (OpenGL3 variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- From 96d5def58d97dc4e68c0682b7d2cf718a7c393b0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 12 Jan 2022 14:04:21 +0000 Subject: [PATCH 291/504] Fix LTO link flags, experiment with static builds Signed-off-by: falkTX --- Makefile.base.mk | 7 ++++++- Makefile.plugins.mk | 2 ++ distrho/src/DistrhoUtils.cpp | 6 ++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index efccd0e3..762efee1 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -190,9 +190,14 @@ 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_FLAGS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch +LINK_OPTS += -fno-strict-aliasing -flto -Werror=odr -Werror=lto-type-mismatch endif BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 3bc341b9..2e39991c 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -73,8 +73,10 @@ BASE_FLAGS += -DHAVE_JACK # always needed ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifneq ($(STATIC_BUILD),true) LINK_FLAGS += -ldl endif +endif # --------------------------------------------------------------------------------------------------------------------- # Set files to build diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index 772f4b67..d19343fa 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -23,7 +23,9 @@ #ifdef DISTRHO_OS_WINDOWS # include #else -# include +# ifndef STATIC_BUILD +# include +# endif # include # include #endif @@ -59,7 +61,7 @@ const char* getBinaryFilename() filenameBuf[0] = '\0'; GetModuleFileName(hInstance, filenameBuf, sizeof(filenameBuf)); filename = filenameBuf; -#else +#elif !defined(STATIC_BUILD) Dl_info info; dladdr((void*)getBinaryFilename, &info); char filenameBuf[PATH_MAX]; From bdf07598d8fa50f6ccaeae3ce1cf8a303c867fe3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 26 Jan 2022 18:32:27 +0000 Subject: [PATCH 292/504] VST3: Create dummy buffer, setup for unused buses Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 91 +++++++++++++++++-------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 68644c63..13eca4bf 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -54,7 +54,7 @@ * - append MIDI input events in a sorted way * == BUSES * - bus arrangements - * - optional audio buses, create dummy buffer of max_block_size length for them + * - optional audio buses * - routing info, do we care? * - set sidechain bus name from port group * == CV @@ -263,6 +263,7 @@ public: fParameterCount(fPlugin.getParameterCount()), fVst3ParameterCount(fParameterCount + kVst3InternalParameterCount), fCachedParameterValues(nullptr), + fDummyAudioBuffer(nullptr), fParameterValuesChangedDuringProcessing(nullptr) #if DISTRHO_PLUGIN_HAS_UI , fParameterValueChangesForUI(nullptr) @@ -371,18 +372,18 @@ public: #endif } -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE for (uint32_t i=0, count=fPlugin.getStateCount(); i= 0); fCurrentProgram = static_cast(program); fPlugin.loadProgram(fCurrentProgram); -# if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) { fParameterValueChangesForUI[kVst3InternalParameterProgram] = false; sendParameterSetToUI(kVst3InternalParameterProgram, program); } -# endif -#endif + #endif + #endif } else if (queryingType == 's') { d_stdout("found state '%s' '%s'", key.buffer(), value.buffer()); -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE if (fPlugin.wantStateKey(key)) { fStateMap[key] = value; fPlugin.setState(key, value); -# if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) sendStateSetToUI(key, value); -# endif + #endif } -#endif + #endif } else if (queryingType == 'p') { @@ -843,13 +844,13 @@ public: fvalue = std::atof(value.buffer()); fCachedParameterValues[kVst3InternalParameterBaseCount + j] = fvalue; -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) { // UI parameter updates are handled outside the read loop (after host param restart) fParameterValueChangesForUI[kVst3InternalParameterBaseCount + j] = true; } -#endif + #endif fPlugin.setParameterValue(j, fvalue); break; } @@ -865,7 +866,7 @@ public: if (fComponentHandler != nullptr) v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED); -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI if (connectedToUI) { for (uint32_t i=0; iwrite(stream, &buffer, 1, &ignored); } -#if DISTRHO_PLUGIN_WANT_FULL_STATE + #if DISTRHO_PLUGIN_WANT_FULL_STATE // Update current state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; fStateMap[key] = fPlugin.getState(key); } -#endif + #endif String state; -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #if DISTRHO_PLUGIN_WANT_PROGRAMS { String tmpStr("__dpf_program__\xff"); tmpStr += String(fCurrentProgram); @@ -917,9 +918,9 @@ public: state += tmpStr; } -#endif + #endif -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE if (stateCount != 0) { state += "__dpf_state_begin__\xff"; @@ -941,7 +942,7 @@ public: state += "__dpf_state_end__\xff"; } -#endif + #endif if (paramCount != 0) { @@ -1015,11 +1016,11 @@ public: uint32_t getLatencySamples() const noexcept { -#if DISTRHO_PLUGIN_WANT_LATENCY + #if DISTRHO_PLUGIN_WANT_LATENCY return fPlugin.getLatency(); -#else + #else return 0; -#endif + #endif } v3_result setupProcessing(v3_process_setup* const setup) @@ -1048,7 +1049,7 @@ public: if (active) fPlugin.activate(); - // TODO create dummy buffer of max_block_size length, to use for disabled buses + fDummyAudioBuffer = new float[setup->max_block_size]; return V3_OK; } @@ -1063,6 +1064,9 @@ public: else { fPlugin.deactivateIfNeeded(); + + delete[] fDummyAudioBuffer; + fDummyAudioBuffer = nullptr; } return V3_OK; @@ -1142,6 +1146,8 @@ public: const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS != 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1]; /* */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1]; + std::memset(fDummyAudioBuffer, 0, sizeof(float)*data->nframes); + { int32_t i = 0; if (data->inputs != nullptr) @@ -1153,7 +1159,7 @@ public: } } for (; i < std::max(1, DISTRHO_PLUGIN_NUM_INPUTS); ++i) - inputs[i] = nullptr; // TODO use dummy buffer + inputs[i] = fDummyAudioBuffer; } { @@ -1167,17 +1173,17 @@ public: } } for (; i < std::max(1, DISTRHO_PLUGIN_NUM_OUTPUTS); ++i) - outputs[i] = nullptr; // TODO use dummy buffer + outputs[i] = fDummyAudioBuffer; } -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT fHostEventOutputHandle = data->output_events; -#endif + #endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t midiEventCount = 0; -# if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI while (fNotesRingBuffer.isDataAvailableForReading()) { uint8_t midiData[3]; @@ -1192,7 +1198,7 @@ public: if (midiEventCount == kMaxMidiEvents) break; } -# endif + #endif if (v3_event_list** const eventptr = data->input_events) { @@ -1296,7 +1302,7 @@ public: } #endif */ -#endif + #endif // if there are any parameter changes at frame 0, set them here if (v3_param_changes** const inparamsptr = data->input_params) @@ -2167,6 +2173,7 @@ private: const uint32_t fParameterCount; const uint32_t fVst3ParameterCount; // full offset + real float* fCachedParameterValues; // basic offset + real + float* fDummyAudioBuffer; bool* fParameterValuesChangedDuringProcessing; // basic offset + real #if DISTRHO_PLUGIN_HAS_UI bool* fParameterValueChangesForUI; // basic offset + real @@ -2248,9 +2255,9 @@ private: } fCachedParameterValues[kVst3InternalParameterBaseCount + i] = curValue; -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = true; -#endif + #endif paramId = kVst3InternalParameterCount + i; curValue = fPlugin.getParameterRanges(i).getNormalizedValue(curValue); @@ -2259,7 +2266,7 @@ private: break; } -#if DISTRHO_PLUGIN_WANT_LATENCY + #if DISTRHO_PLUGIN_WANT_LATENCY const uint32_t latency = fPlugin.getLatency(); if (fLastKnownLatency != latency) @@ -2270,7 +2277,7 @@ private: fCachedParameterValues[kVst3InternalParameterLatency]); addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterLatency, curValue); } -#endif + #endif } bool addParameterDataToHostOutputEvents(v3_param_changes** const outparamsptr, From dd90f7b3e3d57d5bed66ac3e7af1391338cdb349 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 26 Jan 2022 18:49:03 +0000 Subject: [PATCH 293/504] VST3: Fix memory leak for hosts that dont deactivate before unload Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 13eca4bf..81c16519 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -394,6 +394,12 @@ public: fCachedParameterValues = nullptr; } + if (fDummyAudioBuffer != nullptr) + { + delete[] fDummyAudioBuffer; + fDummyAudioBuffer = nullptr; + } + if (fParameterValuesChangedDuringProcessing != nullptr) { delete[] fParameterValuesChangedDuringProcessing; From a349e82a719d2e6de9394e211f355ca1632b97a7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 26 Jan 2022 18:59:58 +0000 Subject: [PATCH 294/504] Also fix in case of calling VST3 setupProcessing twice Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 81c16519..033d4e7d 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1055,6 +1055,7 @@ public: if (active) fPlugin.activate(); + delete[] fDummyAudioBuffer; fDummyAudioBuffer = new float[setup->max_block_size]; return V3_OK; @@ -3383,7 +3384,7 @@ struct dpf_audio_processor : v3_audio_processor_cpp { static v3_result V3_API get_bus_arrangement(void* const self, const int32_t bus_direction, const int32_t idx, v3_speaker_arrangement* const arr) { - d_stdout("dpf_audio_processor::get_bus_arrangement => %p %s %p", + d_stdout("dpf_audio_processor::get_bus_arrangement => %p %s %i %p", self, v3_bus_direction_str(bus_direction), idx, arr); dpf_audio_processor* const processor = *static_cast(self); From f56d13280baee304ebcb6eb92aaaa0409f8674a9 Mon Sep 17 00:00:00 2001 From: Luciano Iam Date: Sun, 30 Jan 2022 11:42:02 +0100 Subject: [PATCH 295/504] VST3: safer dealloc of dummy audio buffer Fix crash described at https://github.com/DISTRHO/DPF/issues/364 --- distrho/src/DistrhoPluginVST3.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 033d4e7d..0b53a117 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1071,9 +1071,6 @@ public: else { fPlugin.deactivateIfNeeded(); - - delete[] fDummyAudioBuffer; - fDummyAudioBuffer = nullptr; } return V3_OK; From e9f41ad9979d35d7f747ec25652e5ff25da8e853 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 31 Jan 2022 19:32:56 +0000 Subject: [PATCH 296/504] Fix lv2 ttl generation for static builds Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index a857140c..202b6335 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -42,6 +42,10 @@ # include "mod-license.h" #endif +#ifndef DISTRHO_OS_WINDOWS +# include +#endif + #include #include @@ -225,7 +229,17 @@ void lv2_generate_ttl(const char* const basename) USE_NAMESPACE_DISTRHO String bundlePath(getBinaryFilename()); - bundlePath.truncate(bundlePath.rfind(DISTRHO_OS_SEP)); + if (bundlePath.isNotEmpty()) + { + bundlePath.truncate(bundlePath.rfind(DISTRHO_OS_SEP)); + } +#ifndef DISTRHO_OS_WINDOWS + else if (char* const cwd = ::getcwd(nullptr, 0)) + { + bundlePath = cwd; + std::free(cwd); + } +#endif d_nextBundlePath = bundlePath.buffer(); // Dummy plugin to get data from From 7e6e3cc84b60727599120a8756396caa2919d46e Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 1 Feb 2022 12:32:31 +0000 Subject: [PATCH 297/504] Initial work for host-visible state Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 37 +++++- distrho/src/DistrhoPlugin.cpp | 12 +- distrho/src/DistrhoPluginInternal.hpp | 16 ++- distrho/src/DistrhoPluginLV2.cpp | 157 ++++++++++++++++--------- distrho/src/DistrhoPluginLV2export.cpp | 31 ++--- distrho/src/lv2/atom-forge.h | 2 +- 6 files changed, 167 insertions(+), 88 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 80de8c8c..3bd4b9e2 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -135,6 +135,29 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; /** @} */ +/* ------------------------------------------------------------------------------------------------------------ + * State Hints */ + +/** + @defgroup StateHints State Hints + + Various state hints. + @see Plugin::getStateHints + @{ + */ + +/** + State is available for the host to see and change. + */ +static const uint32_t kStateIsHostVisible = 0x01; + +/** + State is a filename path. + */ +static const uint32_t kStateIsFilenamePath = 0x02 | kStateIsHostVisible; + +/** @} */ + /* ------------------------------------------------------------------------------------------------------------ * Base Plugin structs */ @@ -993,13 +1016,17 @@ protected: Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. */ virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue); -#endif -#if DISTRHO_PLUGIN_WANT_STATEFILES /** - TODO API under construction + TODO API under construction, should likely be put in a new State struct with key, name defValue and hints + Returns StateHints mask. */ - virtual bool isStateFile(uint32_t index) = 0; + virtual uint32_t getStateHints(uint32_t index); +#endif + +#if DISTRHO_PLUGIN_WANT_STATEFILES + DISTRHO_DEPRECATED_BY("getStateHints") + virtual bool isStateFile(uint32_t index) { return false; } #endif /* -------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 2242b16c..e9f9810c 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -200,6 +200,16 @@ String Plugin::getState(const char*) const { return String(); } #endif #if DISTRHO_PLUGIN_WANT_STATE +uint32_t Plugin::getStateHints(const uint32_t index) +{ + #if DISTRHO_PLUGIN_WANT_STATEFILES + if isStateFile(index) + return kStateIsFilenamePath; + #endif + + return 0x0; +} + void Plugin::setState(const char*, const char*) {} #endif diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 017eb234..52185510 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -727,6 +727,13 @@ public: return fData->stateCount; } + uint32_t getStateHints(const uint32_t index) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, 0x0); + + return fPlugin->getStateHints(index); + } + const String& getStateKey(const uint32_t index) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); @@ -741,15 +748,6 @@ public: return fData->stateDefValues[index]; } -# if DISTRHO_PLUGIN_WANT_STATEFILES - bool isStateFile(const uint32_t index) const - { - DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, false); - - return fPlugin->isStateFile(index); - } -# endif - # if DISTRHO_PLUGIN_WANT_FULL_STATE String getState(const char* key) const { diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 60378f2a..92ba3309 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -17,6 +17,7 @@ #include "DistrhoPluginInternal.hpp" #include "lv2/atom.h" +#include "lv2/atom-forge.h" #include "lv2/atom-util.h" #include "lv2/buf-size.h" #include "lv2/data-access.h" @@ -47,8 +48,8 @@ # define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:" #endif -#define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) || DISTRHO_PLUGIN_WANT_STATEFILES) -#define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) +#define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) +#define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) START_NAMESPACE_DISTRHO @@ -126,29 +127,29 @@ public: #endif #if DISTRHO_PLUGIN_WANT_STATE + std::memset(&fAtomForge, 0, sizeof(fAtomForge)); + lv2_atom_forge_init(&fAtomForge, uridMap); + if (const uint32_t count = fPlugin.getStateCount()) { + fUrids = new LV2_URID[count]; fNeededUiSends = new bool[count]; for (uint32_t i=0; i < count; ++i) { fNeededUiSends[i] = false; - const String& dkey(fPlugin.getStateKey(i)); - fStateMap[dkey] = fPlugin.getStateDefaultValue(i); + const String& statekey(fPlugin.getStateKey(i)); + fStateMap[statekey] = fPlugin.getStateDefaultValue(i); -# if DISTRHO_PLUGIN_WANT_STATEFILES - if (fPlugin.isStateFile(i)) - { - const String dpf_lv2_key(DISTRHO_PLUGIN_URI "#" + dkey); - const LV2_URID urid = uridMap->map(uridMap->handle, dpf_lv2_key.buffer()); - fUridStateFileMap[urid] = dkey; - } -# endif + const String lv2key(DISTRHO_PLUGIN_URI "#" + statekey); + const LV2_URID urid = fUrids[i] = uridMap->map(uridMap->handle, lv2key.buffer()); + fUridStateMap[urid] = statekey; } } else { + fUrids = nullptr; fNeededUiSends = nullptr; } #else @@ -544,13 +545,14 @@ public: } #endif - // check for messages from UI or files -#if DISTRHO_PLUGIN_WANT_STATE && (DISTRHO_PLUGIN_HAS_UI || DISTRHO_PLUGIN_WANT_STATEFILES) + // check for messages from UI or host +#if DISTRHO_PLUGIN_WANT_STATE LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) { if (event == nullptr) break; + #if DISTRHO_PLUGIN_HAS_UI if (event->body.type == fURIDs.dpfKeyValue) { const void* const data = (const void*)(event + 1); @@ -567,8 +569,9 @@ public: fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); } } -# if DISTRHO_PLUGIN_WANT_STATEFILES - else if (event->body.type == fURIDs.atomObject && fWorker != nullptr) + else + #endif + if (event->body.type == fURIDs.atomObject && fWorker != nullptr) { const LV2_Atom_Object* const object = (const LV2_Atom_Object*)&event->body; @@ -576,13 +579,12 @@ public: const LV2_Atom* value = nullptr; lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); - if (property != nullptr && property->type == fURIDs.atomURID && - value != nullptr && value->type == fURIDs.atomPath) + if (property != nullptr && property->type == fURIDs.atomURID && value != nullptr && + (value->type == fURIDs.atomPath || value->type == fURIDs.atomString)) { fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); } } -# endif } #endif @@ -681,7 +683,7 @@ public: updateParameterOutputsAndTriggers(); -#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI +#if DISTRHO_PLUGIN_WANT_STATE fEventsOutData.initIfNeeded(fURIDs.atomSequence); LV2_Atom_Event* aev; @@ -692,6 +694,16 @@ public: if (! fNeededUiSends[i]) continue; + const uint32_t hints = fPlugin.getStateHints(i); + + #if ! DISTRHO_PLUGIN_HAS_UI + if ((hints & kStateIsHostVisible) == 0x0) + { + fNeededUiSends[i] = false; + continue; + } + #endif + const String& curKey(fPlugin.getStateKey(i)); for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) @@ -703,8 +715,19 @@ public: const String& value(cit->second); - // set msg size (key + value + separator + 2x null terminator) - const uint32_t msgSize = static_cast(key.length()+value.length())+3U; + // set msg size + uint32_t msgSize; + + if (hints & kStateIsHostVisible) + { + // object, prop key, prop urid, value key, value + msgSize = sizeof(LV2_Atom_Object) + sizeof(LV2_URID) * 3 + value.length() + 1; + } + else + { + // key + value + 2x null terminator + separator + msgSize = static_cast(key.length()+value.length())+3U; + } if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) { @@ -715,18 +738,38 @@ public: // put data aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); aev->time.frames = 0; - aev->body.type = fURIDs.dpfKeyValue; - aev->body.size = msgSize; uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); - std::memset(msgBuf, 0, msgSize); - // write key and value in atom buffer - std::memcpy(msgBuf, key.buffer(), key.length()+1); - std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); + if (hints & kStateIsHostVisible) + { + LV2_Atom_Forge atomForge = fAtomForge; + lv2_atom_forge_set_buffer(&atomForge, msgBuf, msgSize); - fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); + LV2_Atom_Forge_Frame forgeFrame; + lv2_atom_forge_object(&atomForge, &forgeFrame, 0, fURIDs.patchSet); + + lv2_atom_forge_key(&atomForge, fURIDs.patchProperty); + lv2_atom_forge_urid(&atomForge, fUrids[i]); + + lv2_atom_forge_key(&atomForge, fURIDs.patchValue); + lv2_atom_forge_path(&atomForge, value.buffer(), static_cast(value.length()+1)); + + lv2_atom_forge_pop(&atomForge, &forgeFrame); + } + else + { + aev->body.type = fURIDs.dpfKeyValue; + aev->body.size = msgSize; + std::memset(msgBuf, 0, msgSize); + + // write key and value in atom buffer + std::memcpy(msgBuf, key.buffer(), key.length()+1); + std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); + } + + fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); fNeededUiSends[i] = false; break; } @@ -854,7 +897,7 @@ public: } # endif - String dpf_lv2_key; + String lv2key; LV2_URID urid; for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) @@ -870,24 +913,24 @@ public: const String& value(cit->second); -# if DISTRHO_PLUGIN_WANT_STATEFILES - if (fPlugin.isStateFile(i)) + if (const uint32_t hints = fPlugin.getStateHints(i)) { - dpf_lv2_key = DISTRHO_PLUGIN_URI "#"; - urid = fURIDs.atomPath; + lv2key = DISTRHO_PLUGIN_URI "#"; + urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath + ? fURIDs.atomPath + : fURIDs.atomString; } else -# endif { - dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; + lv2key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; urid = fURIDs.atomString; } - dpf_lv2_key += key; + lv2key += key; // some hosts need +1 for the null terminator, even though the type is string store(handle, - fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), + fUridMap->map(fUridMap->handle, lv2key.buffer()), value.buffer(), value.length()+1, urid, @@ -903,33 +946,33 @@ public: size_t size; uint32_t type, flags; - String dpf_lv2_key; + String lv2key; LV2_URID urid; for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) { const String& key(fPlugin.getStateKey(i)); -# if DISTRHO_PLUGIN_WANT_STATEFILES - if (fPlugin.isStateFile(i)) + if (const uint32_t hints = fPlugin.getStateHints(i)) { - dpf_lv2_key = DISTRHO_PLUGIN_URI "#"; - urid = fURIDs.atomPath; + lv2key = DISTRHO_PLUGIN_URI "#"; + urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath + ? fURIDs.atomPath + : fURIDs.atomString; } else -# endif { - dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; + lv2key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; urid = fURIDs.atomString; } - dpf_lv2_key += key; + lv2key += key; size = 0; type = 0; flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; const void* data = retrieve(handle, - fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), + fUridMap->map(fUridMap->handle, lv2key.buffer()), &size, &type, &flags); if (data == nullptr || size == 0) @@ -967,7 +1010,6 @@ public: return LV2_WORKER_SUCCESS; } -# if DISTRHO_PLUGIN_WANT_STATEFILES if (eventBody->type == fURIDs.atomObject) { const LV2_Atom_Object* const object = (const LV2_Atom_Object*)eventBody; @@ -978,7 +1020,8 @@ public: DISTRHO_SAFE_ASSERT_RETURN(property != nullptr, LV2_WORKER_ERR_UNKNOWN); DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID, LV2_WORKER_ERR_UNKNOWN); DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, LV2_WORKER_ERR_UNKNOWN); - DISTRHO_SAFE_ASSERT_RETURN(value->type == fURIDs.atomPath, LV2_WORKER_ERR_UNKNOWN); + DISTRHO_SAFE_ASSERT_RETURN(value->type == fURIDs.atomPath || + value->type == fURIDs.atomString, LV2_WORKER_ERR_UNKNOWN); const LV2_URID urid = ((const LV2_Atom_URID*)property)->body; const char* const filename = (const char*)(value + 1); @@ -986,8 +1029,8 @@ public: String key; try { - key = fUridStateFileMap[urid]; - } DISTRHO_SAFE_EXCEPTION_RETURN("lv2_work fUridStateFileMap[urid]", LV2_WORKER_ERR_UNKNOWN); + key = fUridStateMap[urid]; + } DISTRHO_SAFE_EXCEPTION_RETURN("lv2_work fUridStateMap[urid]", LV2_WORKER_ERR_UNKNOWN); setState(key, filename); @@ -1002,7 +1045,6 @@ public: return LV2_WORKER_SUCCESS; } -# endif return LV2_WORKER_ERR_UNKNOWN; } @@ -1136,6 +1178,7 @@ private: LV2_URID atomURID; LV2_URID dpfKeyValue; LV2_URID midiEvent; + LV2_URID patchSet; LV2_URID patchProperty; LV2_URID patchValue; LV2_URID timePosition; @@ -1162,6 +1205,7 @@ private: atomURID(map(LV2_ATOM__URID)), dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), midiEvent(map(LV2_MIDI__MidiEvent)), + patchSet(map(LV2_PATCH__Set)), patchProperty(map(LV2_PATCH__property)), patchValue(map(LV2_PATCH__value)), timePosition(map(LV2_TIME__Position)), @@ -1188,7 +1232,10 @@ private: const LV2_Worker_Schedule* const fWorker; #if DISTRHO_PLUGIN_WANT_STATE + LV2_Atom_Forge fAtomForge; StringToStringMap fStateMap; + UridToStringMap fUridStateMap; + LV2_URID* fUrids; bool* fNeededUiSends; void setState(const char* const key, const char* const newValue) @@ -1213,10 +1260,6 @@ private: d_stderr("Failed to find plugin state with key \"%s\"", key); } - -# if DISTRHO_PLUGIN_WANT_STATEFILES - UridToStringMap fUridStateFileMap; -# endif #endif void updateParameterOutputsAndTriggers() diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 202b6335..15623e1d 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -79,8 +79,8 @@ # define DISTRHO_LV2_UI_TYPE "UI" #endif -#define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) || DISTRHO_PLUGIN_WANT_STATEFILES) -#define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) +#define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) +#define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) #define DISTRHO_BYPASS_PARAMETER_NAME "lv2_enabled" @@ -152,9 +152,7 @@ static const char* const lv2ManifestUiOptionalFeatures[] = "ui:parent", "ui:touch", #endif -#if DISTRHO_PLUGIN_WANT_STATEFILES "ui:requestValue", -#endif nullptr }; @@ -370,23 +368,28 @@ void lv2_generate_ttl(const char* const basename) pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; pluginString += "\n"; -#if DISTRHO_PLUGIN_WANT_STATEFILES // define writable states as lv2 parameters - bool hasStateFiles = false; + bool hasHostVisibleState = false; for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) { - if (! plugin.isStateFile(i)) + const uint32_t hints = plugin.getStateHints(i); + + if ((hints & kStateIsHostVisible) == 0x0) continue; const String& key(plugin.getStateKey(i)); pluginString += "<" DISTRHO_PLUGIN_URI "#" + key + ">\n"; pluginString += " a lv2:Parameter ;\n"; pluginString += " rdfs:label \"" + key + "\" ;\n"; - pluginString += " rdfs:range atom:Path .\n\n"; - hasStateFiles = true; + + if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath) + pluginString += " rdfs:range atom:Path .\n\n"; + else + pluginString += " rdfs:range atom:String .\n\n"; + + hasHostVisibleState = true; } -#endif // plugin pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; @@ -404,12 +407,11 @@ void lv2_generate_ttl(const char* const basename) addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4); addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4); -#if DISTRHO_PLUGIN_WANT_STATEFILES - if (hasStateFiles) + if (hasHostVisibleState) { for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) { - if (! plugin.isStateFile(i)) + if ((plugin.getStateHints(i) & kStateIsHostVisible) == 0x0) continue; const String& key(plugin.getStateKey(i)); @@ -417,7 +419,6 @@ void lv2_generate_ttl(const char* const basename) } pluginString += "\n"; } -#endif // UI #if DISTRHO_PLUGIN_HAS_UI diff --git a/distrho/src/lv2/atom-forge.h b/distrho/src/lv2/atom-forge.h index d803d23f..d808f0d9 100644 --- a/distrho/src/lv2/atom-forge.h +++ b/distrho/src/lv2/atom-forge.h @@ -125,7 +125,7 @@ lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); not held. */ static inline void -lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) +lv2_atom_forge_init(LV2_Atom_Forge* forge, const LV2_URID_Map* map) { #if defined(__clang__) # pragma clang diagnostic push From 4363f9347e9bbf8fdfaa4d61f8b4a01c66abb50e Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 1 Feb 2022 15:18:50 +0000 Subject: [PATCH 298/504] Fix build Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 15623e1d..40abb09c 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -368,6 +368,7 @@ void lv2_generate_ttl(const char* const basename) pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; pluginString += "\n"; +#if DISTRHO_PLUGIN_WANT_STATE // define writable states as lv2 parameters bool hasHostVisibleState = false; @@ -390,6 +391,7 @@ void lv2_generate_ttl(const char* const basename) hasHostVisibleState = true; } +#endif // plugin pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; @@ -407,6 +409,7 @@ void lv2_generate_ttl(const char* const basename) addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4); addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4); +#if DISTRHO_PLUGIN_WANT_STATE if (hasHostVisibleState) { for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) @@ -419,6 +422,7 @@ void lv2_generate_ttl(const char* const basename) } pluginString += "\n"; } +#endif // UI #if DISTRHO_PLUGIN_HAS_UI From 60b9b76b466ad62a9f64f2eb7557a2563bbbf683 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 7 Feb 2022 22:23:09 +0000 Subject: [PATCH 299/504] Only use on Linux Signed-off-by: falkTX --- distrho/extra/sofd/libsofd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/distrho/extra/sofd/libsofd.c b/distrho/extra/sofd/libsofd.c index ad8eca48..1d903840 100644 --- a/distrho/extra/sofd/libsofd.c +++ b/distrho/extra/sofd/libsofd.c @@ -338,7 +338,6 @@ const char *x_fib_recent_file(const char *appname) { } #ifdef HAVE_X11 -#include #include #include @@ -347,6 +346,11 @@ const char *x_fib_recent_file(const char *appname) { #include #include +#if defined(__linux__) || defined(__linux) +#define HAVE_MNTENT +#include +#endif + #ifndef MIN #define MIN(A,B) ( (A) < (B) ? (A) : (B) ) #endif @@ -1766,6 +1770,7 @@ static int parse_gtk_bookmarks (Display *dpy, const char *fn) { return found; } +#ifdef HAVE_MNTENT static const char *ignore_mountpoints[] = { "/bin", "/boot", "/dev", "/etc", "/lib", "/live", "/mnt", "/opt", @@ -1840,6 +1845,7 @@ static int read_mtab (Display *dpy, const char *mtab) { fclose (mt); return found; } +#endif static void populate_places (Display *dpy) { char tmp[1024]; @@ -1868,9 +1874,11 @@ static void populate_places (Display *dpy) { parse_gtk_bookmarks (dpy, _fib_cfg_custom_places); } +#ifdef HAVE_MNTENT if (read_mtab (dpy, "/proc/mounts") < 1) { read_mtab (dpy, "/etc/mtab"); } +#endif int parsed_bookmarks = 0; if (!parsed_bookmarks && getenv ("HOME")) { From 49b4f6fd370510a1cc07ef0ffc807826264a2044 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 8 Feb 2022 22:22:26 +0000 Subject: [PATCH 300/504] Use unlikely macro (built-in expect) to speed up safe asserts Signed-off-by: falkTX --- distrho/src/DistrhoDefines.h | 75 ++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index b3d2de6b..32499a62 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -71,6 +71,13 @@ # define nullptr NULL #endif +/* Define unlikely */ +#ifdef __GNUC__ +# define unlikely(x) __builtin_expect(x,0) +#else +# define unlikely(x) x +#endif + /* Define DISTRHO_DEPRECATED */ #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 480 # define DISTRHO_DEPRECATED __attribute__((deprecated)) @@ -90,40 +97,40 @@ #endif /* Define DISTRHO_SAFE_ASSERT* */ -#define DISTRHO_SAFE_ASSERT(cond) if (! (cond)) d_safe_assert (#cond, __FILE__, __LINE__); -#define DISTRHO_SAFE_ASSERT_INT(cond, value) if (! (cond)) d_safe_assert_int (#cond, __FILE__, __LINE__, static_cast(value)); -#define DISTRHO_SAFE_ASSERT_INT2(cond, v1, v2) if (! (cond)) d_safe_assert_int2 (#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); -#define DISTRHO_SAFE_ASSERT_UINT(cond, value) if (! (cond)) d_safe_assert_uint (#cond, __FILE__, __LINE__, static_cast(value)); -#define DISTRHO_SAFE_ASSERT_UINT2(cond, v1, v2) if (! (cond)) d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); - -#define DISTRHO_SAFE_ASSERT_BREAK(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); break; } -#define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); continue; } -#define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (! (cond)) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; } - -#define DISTRHO_CUSTOM_SAFE_ASSERT(msg, cond) if (! (cond)) d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); -#define DISTRHO_CUSTOM_SAFE_ASSERT_BREAK(msg, cond) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); break; } -#define DISTRHO_CUSTOM_SAFE_ASSERT_CONTINUE(msg, cond) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); continue; } -#define DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(msg, cond, ret) if (! (cond)) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); return ret; } - -#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_BREAK(msg, cond) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } break; } -#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_CONTINUE(msg, cond) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } continue; } -#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN(msg, cond, ret) if (! (cond)) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } return ret; } - -#define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); break; } -#define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); continue; } -#define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); return ret; } - -#define DISTRHO_SAFE_ASSERT_INT2_BREAK(cond, v1, v2) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); break; } -#define DISTRHO_SAFE_ASSERT_INT2_CONTINUE(cond, v1, v2) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); continue; } -#define DISTRHO_SAFE_ASSERT_INT2_RETURN(cond, v1, v2, ret) if (! (cond)) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); return ret; } - -#define DISTRHO_SAFE_ASSERT_UINT_BREAK(cond, value) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); break; } -#define DISTRHO_SAFE_ASSERT_UINT_CONTINUE(cond, value) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); continue; } -#define DISTRHO_SAFE_ASSERT_UINT_RETURN(cond, value, ret) if (! (cond)) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); return ret; } - -#define DISTRHO_SAFE_ASSERT_UINT2_BREAK(cond, v1, v2) if (! (cond)) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); break; } -#define DISTRHO_SAFE_ASSERT_UINT2_CONTINUE(cond, v1, v2) if (! (cond)) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); continue; } -#define DISTRHO_SAFE_ASSERT_UINT2_RETURN(cond, v1, v2, ret) if (! (cond)) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); return ret; } +#define DISTRHO_SAFE_ASSERT(cond) if (unlikely(!(cond))) d_safe_assert (#cond, __FILE__, __LINE__); +#define DISTRHO_SAFE_ASSERT_INT(cond, value) if (unlikely(!(cond))) d_safe_assert_int (#cond, __FILE__, __LINE__, static_cast(value)); +#define DISTRHO_SAFE_ASSERT_INT2(cond, v1, v2) if (unlikely(!(cond))) d_safe_assert_int2 (#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); +#define DISTRHO_SAFE_ASSERT_UINT(cond, value) if (unlikely(!(cond))) d_safe_assert_uint (#cond, __FILE__, __LINE__, static_cast(value)); +#define DISTRHO_SAFE_ASSERT_UINT2(cond, v1, v2) if (unlikely(!(cond))) d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); + +#define DISTRHO_SAFE_ASSERT_BREAK(cond) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); break; } +#define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); continue; } +#define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (unlikely(!(cond))) { d_safe_assert(#cond, __FILE__, __LINE__); return ret; } + +#define DISTRHO_CUSTOM_SAFE_ASSERT(msg, cond) if (unlikely(!(cond))) d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); +#define DISTRHO_CUSTOM_SAFE_ASSERT_BREAK(msg, cond) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); break; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_CONTINUE(msg, cond) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); continue; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(msg, cond, ret) if (unlikely(!(cond))) { d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); return ret; } + +#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_BREAK(msg, cond) if (unlikely(!(cond))) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } break; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_CONTINUE(msg, cond) if (unlikely(!(cond))) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } continue; } +#define DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN(msg, cond, ret) if (unlikely(!(cond))) { static bool _p; if (!_p) { _p = true; d_custom_safe_assert(msg, #cond, __FILE__, __LINE__); } return ret; } + +#define DISTRHO_SAFE_ASSERT_INT_BREAK(cond, value) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); break; } +#define DISTRHO_SAFE_ASSERT_INT_CONTINUE(cond, value) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); continue; } +#define DISTRHO_SAFE_ASSERT_INT_RETURN(cond, value, ret) if (unlikely(!(cond))) { d_safe_assert_int(#cond, __FILE__, __LINE__, static_cast(value)); return ret; } + +#define DISTRHO_SAFE_ASSERT_INT2_BREAK(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); break; } +#define DISTRHO_SAFE_ASSERT_INT2_CONTINUE(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); continue; } +#define DISTRHO_SAFE_ASSERT_INT2_RETURN(cond, v1, v2, ret) if (unlikely(!(cond))) { d_safe_assert_int2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); return ret; } + +#define DISTRHO_SAFE_ASSERT_UINT_BREAK(cond, value) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); break; } +#define DISTRHO_SAFE_ASSERT_UINT_CONTINUE(cond, value) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); continue; } +#define DISTRHO_SAFE_ASSERT_UINT_RETURN(cond, value, ret) if (unlikely(!(cond))) { d_safe_assert_uint(#cond, __FILE__, __LINE__, static_cast(value)); return ret; } + +#define DISTRHO_SAFE_ASSERT_UINT2_BREAK(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); break; } +#define DISTRHO_SAFE_ASSERT_UINT2_CONTINUE(cond, v1, v2) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); continue; } +#define DISTRHO_SAFE_ASSERT_UINT2_RETURN(cond, v1, v2, ret) if (unlikely(!(cond))) { d_safe_assert_uint2(#cond, __FILE__, __LINE__, static_cast(v1), static_cast(v2)); return ret; } /* Define DISTRHO_SAFE_EXCEPTION */ #define DISTRHO_SAFE_EXCEPTION(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); } From 07de97ba7201ae22705a4f1101afa0f853c845ae Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 8 Feb 2022 22:36:43 +0000 Subject: [PATCH 301/504] Fix crash when cancelling file dialog Closes #358 Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index ccaa4f1b..a40fd3b4 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -87,8 +87,8 @@ struct FileBrowserData { ~FileBrowserData() { - if (cancelAndStop() && selectedFile != nullptr && selectedFile != kSelectedFileCancelled) - std::free(const_cast(selectedFile)); + if (cancelAndStop()) + free(); } void setupAndStart(const bool embed, @@ -238,10 +238,24 @@ struct FileBrowserData { XCloseDisplay(x11display); #endif - if (selectedFile != nullptr && selectedFile != kSelectedFileCancelled) - std::free(const_cast(selectedFile)); + free(); } #endif + + void free() + { + if (selectedFile == nullptr) + return; + + if (selectedFile == kSelectedFileCancelled || std::strcmp(selectedFile, kSelectedFileCancelled) == 0) + { + selectedFile = nullptr; + return; + } + + std::free(const_cast(selectedFile)); + selectedFile = nullptr; + } }; // -------------------------------------------------------------------------------------------------------------------- From 249c2e047060e105122698dfabfc979dcb8cbcb7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 8 Feb 2022 22:47:28 +0000 Subject: [PATCH 302/504] Fix build Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 2 +- distrho/src/DistrhoPlugin.cpp | 27 +++++++++++++++----- examples/FileHandling/FileHandlingPlugin.cpp | 6 ++--- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 3bd4b9e2..64a2fd74 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -1026,7 +1026,7 @@ protected: #if DISTRHO_PLUGIN_WANT_STATEFILES DISTRHO_DEPRECATED_BY("getStateHints") - virtual bool isStateFile(uint32_t index) { return false; } + virtual bool isStateFile(uint32_t) { return false; } #endif /* -------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index e9f9810c..7ed9cf73 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -181,10 +181,6 @@ void Plugin::initProgramName(uint32_t, String&) {} void Plugin::initState(uint32_t, String&, String&) {} #endif -#if DISTRHO_PLUGIN_WANT_STATEFILES -bool Plugin::isStateFile(uint32_t) { return false; } -#endif - /* ------------------------------------------------------------------------------------------------------------ * Init */ @@ -202,12 +198,29 @@ String Plugin::getState(const char*) const { return String(); } #if DISTRHO_PLUGIN_WANT_STATE uint32_t Plugin::getStateHints(const uint32_t index) { - #if DISTRHO_PLUGIN_WANT_STATEFILES - if isStateFile(index) - return kStateIsFilenamePath; + #if DISTRHO_PLUGIN_WANT_STATEFILES + #if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif + if (isStateFile(index)) + #if defined(__clang__) + #pragma clang diagnostic pop + #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic pop #endif + return kStateIsFilenamePath; + #endif return 0x0; + + #if !DISTRHO_PLUGIN_WANT_STATEFILES + // unused + (void)index; + #endif } void Plugin::setState(const char*, const char*) {} diff --git a/examples/FileHandling/FileHandlingPlugin.cpp b/examples/FileHandling/FileHandlingPlugin.cpp index db134ce3..0e1f28f6 100644 --- a/examples/FileHandling/FileHandlingPlugin.cpp +++ b/examples/FileHandling/FileHandlingPlugin.cpp @@ -149,17 +149,17 @@ protected: /** TODO API under construction */ - bool isStateFile(uint32_t index) override + uint32_t getStateHints(uint32_t index) override { switch (index) { case kStateFile1: case kStateFile2: case kStateFile3: - return true; + return kStateIsFilenamePath; } - return false; + return 0x0; } /* -------------------------------------------------------------------------------------------------------- From 65440847faef769cfcb07a94009642341820dd63 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 8 Feb 2022 23:07:46 +0000 Subject: [PATCH 303/504] Fix a leak Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 92ba3309..2da88f12 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -184,6 +184,12 @@ public: fNeededUiSends = nullptr; } + if (fUrids != nullptr) + { + delete[] fUrids; + fUrids = nullptr; + } + fStateMap.clear(); #endif } From fb947e978669c3f214c072db6fcba1b75ffa69f2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Feb 2022 21:59:28 +0000 Subject: [PATCH 304/504] Use X11 on HaikuOS for now, until a real backend is done --- Makefile.base.mk | 14 ++++---------- dgl/src/pugl.cpp | 15 +++++---------- distrho/extra/sofd/libsofd.c | 2 ++ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 762efee1..af7aaf48 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -252,15 +252,13 @@ HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) ifeq ($(MACOS_OR_WINDOWS),true) HAVE_OPENGL = true else -HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) -ifneq ($(HAIKU),true) +HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) HAVE_DBUS = $(shell $(PKG_CONFIG) --exists dbus-1 && echo true) HAVE_X11 = $(shell $(PKG_CONFIG) --exists x11 && echo true) HAVE_XCURSOR = $(shell $(PKG_CONFIG) --exists xcursor && echo true) HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true) endif -endif # --------------------------------------------------------------------------------------------------------------------- # Check for optional libraries @@ -304,7 +302,7 @@ DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 # -lole32 endif -ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifneq ($(MACOS_OR_WINDOWS),true) ifeq ($(HAVE_DBUS),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) @@ -362,7 +360,7 @@ ifeq ($(WINDOWS),true) OPENGL_LIBS = -lopengl32 endif -ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifneq ($(MACOS_OR_WINDOWS),true) OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl x11) OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl x11) endif @@ -374,7 +372,7 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Set Stub specific stuff -ifeq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifeq ($(MACOS_OR_WINDOWS),true) HAVE_STUB = true else HAVE_STUB = $(HAVE_X11) @@ -424,12 +422,8 @@ endif ifeq ($(MACOS_OR_WINDOWS),true) HAVE_DGL = true else ifeq ($(HAVE_OPENGL),true) -ifeq ($(HAIKU),true) -HAVE_DGL = true -else HAVE_DGL = $(HAVE_X11) endif -endif # --------------------------------------------------------------------------------------------------------------------- # Optional flags diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index f18dcb28..a570c37f 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -23,8 +23,7 @@ #include #include -#if defined(DISTRHO_OS_HAIKU) -#elif defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_MAC) # import # include # include @@ -106,8 +105,7 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- -#if defined(DISTRHO_OS_HAIKU) -#elif defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_MAC) # ifndef DISTRHO_MACOS_NAMESPACE_MACRO # define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE # define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE) @@ -278,9 +276,7 @@ double puglGetDesktopScaleFactor(const PuglView* const view) void puglRaiseWindow(PuglView* const view) { -#if defined(DISTRHO_OS_HAIKU) - // nothing here yet -#elif defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_MAC) if (NSWindow* const window = view->impl->window ? view->impl->window : [view->impl->wrapperView window]) [window orderFrontRegardless]; @@ -325,7 +321,7 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co view->maxAspectY = (int)height; } -#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_MAC) puglSetMinSize(view, width, height); if (aspect) { @@ -353,8 +349,7 @@ PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint view->frame.width = width; view->frame.height = height; -#if defined(DISTRHO_OS_HAIKU) -#elif defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_MAC) // replace setFrame with setFrameSize PuglInternals* const impl = view->impl; diff --git a/distrho/extra/sofd/libsofd.c b/distrho/extra/sofd/libsofd.c index 1d903840..6aaeb689 100644 --- a/distrho/extra/sofd/libsofd.c +++ b/distrho/extra/sofd/libsofd.c @@ -499,7 +499,9 @@ static int query_font_geometry (Display *dpy, GC gc, const char *txt, int *w, in if (h) *h = text_structure.ascent + text_structure.descent; if (a) *a = text_structure.ascent; if (d) *d = text_structure.descent; +#ifndef DISTRHO_OS_HAIKU // FIXME XFreeFontInfo (NULL, fontinfo, 1); +#endif return 0; } From 53fd80aa9cefa527aaf32333a534114039b4c07f Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Feb 2022 02:53:27 +0000 Subject: [PATCH 305/504] Start API for changing state from DSP Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 9 ++++ distrho/src/DistrhoPlugin.cpp | 7 ++++ distrho/src/DistrhoPluginInternal.hpp | 18 +++++++- distrho/src/DistrhoPluginLV2.cpp | 57 +++++++++++++++++++++----- distrho/src/DistrhoPluginLV2export.cpp | 2 +- 5 files changed, 80 insertions(+), 13 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 64a2fd74..9c3727aa 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -926,6 +926,15 @@ public: bool requestParameterValueChange(uint32_t index, float value) noexcept; #endif +#if DISTRHO_PLUGIN_WANT_STATE + /** + Notify the host about a state value change. + This function will automatically trigger a state update on the UI side. + It must not be called during run. + */ + bool updateStateValue(const char* key, const char* value) noexcept; +#endif + protected: /* -------------------------------------------------------------------------------------------------------- * Information */ diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 7ed9cf73..58db96aa 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -145,6 +145,13 @@ bool Plugin::requestParameterValueChange(const uint32_t index, const float value } #endif +#if DISTRHO_PLUGIN_WANT_STATE +bool Plugin::updateStateValue(const char* const key, const char* const value) noexcept +{ + return pData->updateStateValueCallback(key, value); +} +#endif + /* ------------------------------------------------------------------------------------------------------------ * Init */ diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 52185510..5a229d11 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -46,6 +46,7 @@ extern bool d_nextCanRequestParameterValueChanges; typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent); typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, float value); +typedef bool (*updateStateValueFunc) (void* ptr, const char* key, const char* value); // ----------------------------------------------------------------------- // Helpers @@ -127,6 +128,7 @@ struct Plugin::PrivateData { void* callbacksPtr; writeMidiFunc writeMidiCallbackFunc; requestParameterValueChangeFunc requestParameterValueChangeCallbackFunc; + updateStateValueFunc updateStateValueCallbackFunc; uint32_t bufferSize; double sampleRate; @@ -159,6 +161,7 @@ struct Plugin::PrivateData { callbacksPtr(nullptr), writeMidiCallbackFunc(nullptr), requestParameterValueChangeCallbackFunc(nullptr), + updateStateValueCallbackFunc(nullptr), bufferSize(d_nextBufferSize), sampleRate(d_nextSampleRate), bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr) @@ -257,6 +260,17 @@ struct Plugin::PrivateData { return false; } #endif + +#if DISTRHO_PLUGIN_WANT_STATE + bool updateStateValueCallback(const char* const key, const char* const value) + { + d_stdout("updateStateValueCallback %p", updateStateValueCallbackFunc); + if (updateStateValueCallbackFunc != nullptr) + return updateStateValueCallbackFunc(callbacksPtr, key, value); + + return false; + } +#endif }; // ----------------------------------------------------------------------- @@ -267,7 +281,8 @@ class PluginExporter public: PluginExporter(void* const callbacksPtr, const writeMidiFunc writeMidiCall, - const requestParameterValueChangeFunc requestParameterValueChangeCall) + const requestParameterValueChangeFunc requestParameterValueChangeCall, + const updateStateValueFunc updateStateValueCall) : fPlugin(createPlugin()), fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), fIsActive(false) @@ -409,6 +424,7 @@ public: fData->callbacksPtr = callbacksPtr; fData->writeMidiCallbackFunc = writeMidiCall; fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall; + fData->updateStateValueCallbackFunc = updateStateValueCall; } ~PluginExporter() diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 2da88f12..1ba24631 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -62,6 +62,9 @@ static const writeMidiFunc writeMidiCallback = nullptr; #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif +#if ! DISTRHO_PLUGIN_WANT_STATE +static const updateStateValueFunc updateStateValueCallback = nullptr; +#endif // ----------------------------------------------------------------------- @@ -73,7 +76,7 @@ public: const LV2_Worker_Schedule* const worker, const LV2_ControlInputPort_Change_Request* const ctrlInPortChangeReq, const bool usingNominal) - : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), + : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), fUsingNominal(usingNominal), #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD fRunCount(0), @@ -727,7 +730,11 @@ public: if (hints & kStateIsHostVisible) { // object, prop key, prop urid, value key, value - msgSize = sizeof(LV2_Atom_Object) + sizeof(LV2_URID) * 3 + value.length() + 1; + msgSize = sizeof(LV2_Atom_Object) + + sizeof(LV2_Atom_Property_Body) * 4 + + sizeof(LV2_Atom_URID) * 3 + + sizeof(LV2_Atom_String) + + value.length() + 1; } else { @@ -737,7 +744,8 @@ public: if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) { - d_stdout("Sending key '%s' to UI failed, out of space", key.buffer()); + d_stdout("Sending key '%s' to UI failed, out of space (needs %u bytes)", + key.buffer(), msgSize); break; } @@ -745,10 +753,9 @@ public: aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); aev->time.frames = 0; - uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); - if (hints & kStateIsHostVisible) { + uint8_t* const msgBuf = (uint8_t*)&aev->body; LV2_Atom_Forge atomForge = fAtomForge; lv2_atom_forge_set_buffer(&atomForge, msgBuf, msgSize); @@ -759,7 +766,10 @@ public: lv2_atom_forge_urid(&atomForge, fUrids[i]); lv2_atom_forge_key(&atomForge, fURIDs.patchValue); - lv2_atom_forge_path(&atomForge, value.buffer(), static_cast(value.length()+1)); + if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath) + lv2_atom_forge_path(&atomForge, value.buffer(), static_cast(value.length()+1)); + else + lv2_atom_forge_string(&atomForge, value.buffer(), static_cast(value.length()+1)); lv2_atom_forge_pop(&atomForge, &forgeFrame); } @@ -768,6 +778,7 @@ public: aev->body.type = fURIDs.dpfKeyValue; aev->body.size = msgSize; + uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); std::memset(msgBuf, 0, msgSize); // write key and value in atom buffer @@ -1248,11 +1259,14 @@ private: { fPlugin.setState(key, newValue); - // check if we want to save this key - if (! fPlugin.wantStateKey(key)) - return; + // save this key if necessary + if (fPlugin.wantStateKey(key)) + updateState(key, newValue, false); + } - // check if key already exists + bool updateState(const char* const key, const char* const newValue, const bool sendToUI) + { + // key must already exist for (StringToStringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) { const String& dkey(it->first); @@ -1260,11 +1274,25 @@ private: if (dkey == key) { it->second = newValue; - return; + + if (sendToUI) + { + for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) + { + if (fPlugin.getStateKey(i) == key) + { + fNeededUiSends[i] = true; + break; + } + } + } + + return true; } } d_stderr("Failed to find plugin state with key \"%s\"", key); + return false; } #endif @@ -1306,6 +1334,13 @@ private: } #endif +#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, true); + } +#endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT bool writeMidi(const MidiEvent& midiEvent) { diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 40abb09c..90fdca6b 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -244,7 +244,7 @@ void lv2_generate_ttl(const char* const basename) d_nextBufferSize = 512; d_nextSampleRate = 44100.0; d_nextPluginIsDummy = true; - PluginExporter plugin(nullptr, nullptr, nullptr); + PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; d_nextPluginIsDummy = false; From a256e8fba3bb6adf7464e569bfaab1667412a38c Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Feb 2022 12:35:03 +0000 Subject: [PATCH 306/504] Fix build Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 2 +- distrho/src/DistrhoPluginLADSPA+DSSI.cpp | 4 ++-- distrho/src/DistrhoPluginVST2.cpp | 4 ++-- distrho/src/DistrhoPluginVST3.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 40446937..de43684e 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -117,7 +117,7 @@ class PluginJack { public: PluginJack(jack_client_t* const client) - : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), + : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), #if DISTRHO_PLUGIN_HAS_UI fUI(this, 0, // winId diff --git a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp index 48b68eb0..07ce113f 100644 --- a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp +++ b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp @@ -50,7 +50,7 @@ class PluginLadspaDssi { public: PluginLadspaDssi() - : fPlugin(nullptr, nullptr, nullptr), + : fPlugin(nullptr, nullptr, nullptr, nullptr), fPortControls(nullptr), fLastControlValues(nullptr) { @@ -554,7 +554,7 @@ static const struct DescriptorInitializer d_nextBufferSize = 512; d_nextSampleRate = 44100.0; d_nextPluginIsDummy = true; - const PluginExporter plugin(nullptr, nullptr, nullptr); + const PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; d_nextPluginIsDummy = false; diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index fc2ef318..8e5bbad7 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -498,7 +498,7 @@ class PluginVst : public ParameterAndNotesHelper { public: PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) - : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), + : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), fAudioMaster(audioMaster), fEffect(effect) { @@ -1411,7 +1411,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t } // Create dummy plugin to get data from - static PluginExporter plugin(nullptr, nullptr, nullptr); + static PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); if (doInternalInit) { diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 0b53a117..a4355191 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -251,7 +251,7 @@ class PluginVst3 public: PluginVst3(v3_host_application** const host) - : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), + : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), fComponentHandler(nullptr), #if DISTRHO_PLUGIN_HAS_UI # if DPF_VST3_USES_SEPARATE_CONTROLLER @@ -3850,7 +3850,7 @@ static const PluginExporter& _getPluginInfo() d_nextSampleRate = 44100.0; d_nextPluginIsDummy = true; d_nextCanRequestParameterValueChanges = true; - static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr); + static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; d_nextPluginIsDummy = false; From cbc326aa3067262b91f781b35b10fea438df15aa Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Feb 2022 13:06:57 +0000 Subject: [PATCH 307/504] Fix leak when fonsAddFontMem fails Signed-off-by: falkTX --- dgl/src/nanovg/fontstash.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dgl/src/nanovg/fontstash.h b/dgl/src/nanovg/fontstash.h index 4a28c6bb..37ecafc0 100644 --- a/dgl/src/nanovg/fontstash.h +++ b/dgl/src/nanovg/fontstash.h @@ -967,7 +967,10 @@ int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, in int idx = fons__allocFont(stash); if (idx == FONS_INVALID) + { + if (freeData && data) free(data); return FONS_INVALID; + } font = stash->fonts[idx]; From e207aafcb289008881bca53df7effa3736f922ce Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Feb 2022 13:13:58 +0000 Subject: [PATCH 308/504] Fix a memory leak in the String class Signed-off-by: falkTX --- distrho/extra/String.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index 88a7501d..d9a91ca4 100644 --- a/distrho/extra/String.hpp +++ b/distrho/extra/String.hpp @@ -861,7 +861,7 @@ public: std::memcpy(newBuf, fBuffer, fBufferLen); std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); - return String(newBuf); + return String(newBuf, false); } String operator+(const String& str) noexcept From 34e55a3be4b348f567502cdeac81459b08df54ef Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Feb 2022 23:24:04 +0000 Subject: [PATCH 309/504] Fix reversed LV2 micro/minor versions Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 90fdca6b..9cf62c47 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -1109,8 +1109,8 @@ void lv2_generate_ttl(const char* const basename) const uint32_t version(plugin.getVersion()); const uint32_t majorVersion = (version & 0xFF0000) >> 16; - const uint32_t microVersion = (version & 0x00FF00) >> 8; - /* */ uint32_t minorVersion = (version & 0x0000FF) >> 0; + /* */ uint32_t minorVersion = (version & 0x00FF00) >> 8; + const uint32_t microVersion = (version & 0x0000FF) >> 0; // NOTE: LV2 ignores 'major' version and says 0 for minor is pre-release/unstable. if (majorVersion > 0) From 854a9c76490b84141b1fe3f84f5cd6ceb6ac5885 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 01:03:49 +0000 Subject: [PATCH 310/504] Revamp state API, make it a struct Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 96 +++++++++++++++++--- distrho/src/DistrhoPlugin.cpp | 61 ++++++------- distrho/src/DistrhoPluginInternal.hpp | 44 +++++---- distrho/src/DistrhoPluginLV2.cpp | 18 ++-- distrho/src/DistrhoPluginLV2export.cpp | 28 ++++-- distrho/src/DistrhoPluginVST2.cpp | 4 +- distrho/src/DistrhoPluginVST3.cpp | 4 +- examples/FileHandling/FileHandlingPlugin.cpp | 35 +++---- examples/States/ExamplePluginStates.cpp | 37 +++++--- 9 files changed, 207 insertions(+), 120 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 9c3727aa..ef7c228f 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -142,19 +142,42 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; @defgroup StateHints State Hints Various state hints. - @see Plugin::getStateHints + @see State::hints @{ */ /** - State is available for the host to see and change. + State is visible and readable by hosts that support string-type plugin parameters. */ -static const uint32_t kStateIsHostVisible = 0x01; +static const uint32_t kStateIsHostReadable = 0x01; /** - State is a filename path. + State is writable by the host, allowing users to arbitrarily change the state.@n + For obvious reasons a writable state is also readable by the host. */ -static const uint32_t kStateIsFilenamePath = 0x02 | kStateIsHostVisible; +static const uint32_t kStateIsHostWritable = 0x02 | kStateIsHostReadable; + +/** + State is a filename path instead of a regular string.@n + The readable and writable hints are required for filenames to work, and thus are automatically set. + */ +static const uint32_t kStateIsFilenamePath = 0x04 | kStateIsHostWritable; + +/** + State is a base64 encoded string. + */ +static const uint32_t kStateIsBase64Blob = 0x08; + +/** + State is for Plugin/DSP side only, meaning there is never a need to notify the UI when it changes. + */ +static const uint32_t kStateIsOnlyForDSP = 0x10; + +/** + State is for UI side only.@n + If the DSP and UI are separate and the UI is not available, this property won't be saved. + */ +static const uint32_t kStateIsOnlyForUI = 0x20; /** @} */ @@ -639,6 +662,48 @@ struct PortGroup { String symbol; }; +/** + State. + + In DPF states refer to key:value string pairs, used to store arbitrary non-parameter data.@n + By default states are completely internal to the plugin and not visible by the host.@n + Flags can be set to allow hosts to see and/or change them. + + TODO API under construction + */ +struct State { + /** + Hints describing this state. + @note Changing these hints can break compatibility with previously saved data. + @see StateHints + */ + uint32_t hints; + + /** + The key or "symbol" of this state.@n + A state key is a short restricted name used as a machine and human readable identifier. + @note State keys MUST be unique within a plugin instance. + TODO define rules for allowed characters, must be usable as URI non-encoded parameters + */ + String key; + + /** + The default value of this state. + */ + String defaultValue; + + /** + String representation of this state. + */ + String label; + + /** + An extensive description/comment about this state. + @note This value is optional and only used for LV2. + */ + String description; +}; + /** MIDI event. */ @@ -928,9 +993,13 @@ public: #if DISTRHO_PLUGIN_WANT_STATE /** - Notify the host about a state value change. - This function will automatically trigger a state update on the UI side. - It must not be called during run. + Notify the host about a state value change.@n + This function will automatically trigger a state update on the UI side.@n + It must not be called during run.@n + The state must be host readable. + @note this function does nothing on DSSI plugin format, as DSSI only supports UI->DSP messages. + + TODO API under construction */ bool updateStateValue(const char* key, const char* value) noexcept; #endif @@ -1020,17 +1089,14 @@ protected: #if DISTRHO_PLUGIN_WANT_STATE /** - Set the state key and default value of @a index.@n + Initialize the state @a index.@n This function will be called once, shortly after the plugin is created.@n Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. */ - virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue); + virtual void initState(uint32_t index, State& state); - /** - TODO API under construction, should likely be put in a new State struct with key, name defValue and hints - Returns StateHints mask. - */ - virtual uint32_t getStateHints(uint32_t index); + DISTRHO_DEPRECATED_BY("getStateHints(uint32_t,State&)") + virtual void initState(uint32_t, String&, String&) {} #endif #if DISTRHO_PLUGIN_WANT_STATEFILES diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 58db96aa..b0fa0ae4 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -72,9 +72,8 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou if (stateCount > 0) { #if DISTRHO_PLUGIN_WANT_STATE - pData->stateCount = stateCount; - pData->stateKeys = new String[stateCount]; - pData->stateDefValues = new String[stateCount]; + pData->stateCount = stateCount; + pData->states = new State[stateCount]; #else d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); DPF_ABORT @@ -185,27 +184,11 @@ void Plugin::initProgramName(uint32_t, String&) {} #endif #if DISTRHO_PLUGIN_WANT_STATE -void Plugin::initState(uint32_t, String&, String&) {} -#endif - -/* ------------------------------------------------------------------------------------------------------------ - * Init */ - -float Plugin::getParameterValue(uint32_t) const { return 0.0f; } -void Plugin::setParameterValue(uint32_t, float) {} - -#if DISTRHO_PLUGIN_WANT_PROGRAMS -void Plugin::loadProgram(uint32_t) {} -#endif - -#if DISTRHO_PLUGIN_WANT_FULL_STATE -String Plugin::getState(const char*) const { return String(); } -#endif - -#if DISTRHO_PLUGIN_WANT_STATE -uint32_t Plugin::getStateHints(const uint32_t index) +void Plugin::initState(const uint32_t index, State& state) { - #if DISTRHO_PLUGIN_WANT_STATEFILES + uint hints = 0x0; + String stateKey, defaultStateValue; + #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -213,23 +196,39 @@ uint32_t Plugin::getStateHints(const uint32_t index) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif + initState(index, stateKey, defaultStateValue); + #if DISTRHO_PLUGIN_WANT_STATEFILES if (isStateFile(index)) + hints = kStateIsFilenamePath; + #endif #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) #pragma GCC diagnostic pop #endif - return kStateIsFilenamePath; - #endif - return 0x0; - - #if !DISTRHO_PLUGIN_WANT_STATEFILES - // unused - (void)index; - #endif + state.hints = hints; + state.key = stateKey; + state.label = stateKey; + state.defaultValue = defaultStateValue; } +#endif + +/* ------------------------------------------------------------------------------------------------------------ + * Init */ + +float Plugin::getParameterValue(uint32_t) const { return 0.0f; } +void Plugin::setParameterValue(uint32_t, float) {} +#if DISTRHO_PLUGIN_WANT_PROGRAMS +void Plugin::loadProgram(uint32_t) {} +#endif + +#if DISTRHO_PLUGIN_WANT_FULL_STATE +String Plugin::getState(const char*) const { return String(); } +#endif + +#if DISTRHO_PLUGIN_WANT_STATE void Plugin::setState(const char*, const char*) {} #endif diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 5a229d11..00fed5d6 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -112,8 +112,7 @@ struct Plugin::PrivateData { #if DISTRHO_PLUGIN_WANT_STATE uint32_t stateCount; - String* stateKeys; - String* stateDefValues; + State* states; #endif #if DISTRHO_PLUGIN_WANT_LATENCY @@ -152,8 +151,7 @@ struct Plugin::PrivateData { #endif #if DISTRHO_PLUGIN_WANT_STATE stateCount(0), - stateKeys(nullptr), - stateDefValues(nullptr), + states(nullptr), #endif #if DISTRHO_PLUGIN_WANT_LATENCY latency(0), @@ -221,16 +219,10 @@ struct Plugin::PrivateData { #endif #if DISTRHO_PLUGIN_WANT_STATE - if (stateKeys != nullptr) + if (states != nullptr) { - delete[] stateKeys; - stateKeys = nullptr; - } - - if (stateDefValues != nullptr) - { - delete[] stateDefValues; - stateDefValues = nullptr; + delete[] states; + states = nullptr; } #endif @@ -418,7 +410,7 @@ public: #if DISTRHO_PLUGIN_WANT_STATE for (uint32_t i=0, count=fData->stateCount; i < count; ++i) - fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]); + fPlugin->initState(i, fData->states[i]); #endif fData->callbacksPtr = callbacksPtr; @@ -747,25 +739,39 @@ public: { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, 0x0); - return fPlugin->getStateHints(index); + return fData->states[index].hints; } const String& getStateKey(const uint32_t index) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); - return fData->stateKeys[index]; + return fData->states[index].key; } const String& getStateDefaultValue(const uint32_t index) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); - return fData->stateDefValues[index]; + return fData->states[index].defaultValue; + } + + const String& getStateLabel(const uint32_t index) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); + + return fData->states[index].label; + } + + const String& getStateDescription(const uint32_t index) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString); + + return fData->states[index].description; } # if DISTRHO_PLUGIN_WANT_FULL_STATE - String getState(const char* key) const + String getStateValue(const char* const key) const { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString); DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', sFallbackString); @@ -790,7 +796,7 @@ public: for (uint32_t i=0; i < fData->stateCount; ++i) { - if (fData->stateKeys[i] == key) + if (fData->states[i].key == key) return true; } diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 1ba24631..3d27fb59 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -706,7 +706,7 @@ public: const uint32_t hints = fPlugin.getStateHints(i); #if ! DISTRHO_PLUGIN_HAS_UI - if ((hints & kStateIsHostVisible) == 0x0) + if ((hints & kStateIsHostReadable) == 0x0) { fNeededUiSends[i] = false; continue; @@ -727,7 +727,7 @@ public: // set msg size uint32_t msgSize; - if (hints & kStateIsHostVisible) + if (hints & kStateIsHostReadable) { // object, prop key, prop urid, value key, value msgSize = sizeof(LV2_Atom_Object) @@ -753,7 +753,7 @@ public: aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); aev->time.frames = 0; - if (hints & kStateIsHostVisible) + if (hints & kStateIsHostReadable) { uint8_t* const msgBuf = (uint8_t*)&aev->body; LV2_Atom_Forge atomForge = fAtomForge; @@ -894,7 +894,7 @@ public: for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; - fStateMap[key] = fPlugin.getState(key); + fStateMap[key] = fPlugin.getStateValue(key); } # endif } @@ -910,7 +910,7 @@ public: for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; - fStateMap[key] = fPlugin.getState(key); + fStateMap[key] = fPlugin.getStateValue(key); } # endif @@ -930,7 +930,9 @@ public: const String& value(cit->second); - if (const uint32_t hints = fPlugin.getStateHints(i)) + const uint32_t hints = fPlugin.getStateHints(i); + + if (hints & kStateIsHostReadable) { lv2key = DISTRHO_PLUGIN_URI "#"; urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath @@ -970,7 +972,9 @@ public: { const String& key(fPlugin.getStateKey(i)); - if (const uint32_t hints = fPlugin.getStateHints(i)) + const uint32_t hints = fPlugin.getStateHints(i); + + if (hints & kStateIsHostReadable) { lv2key = DISTRHO_PLUGIN_URI "#"; urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 9cf62c47..9a8f74e4 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -376,13 +376,22 @@ void lv2_generate_ttl(const char* const basename) { const uint32_t hints = plugin.getStateHints(i); - if ((hints & kStateIsHostVisible) == 0x0) + if ((hints & kStateIsHostReadable) == 0x0) continue; - const String& key(plugin.getStateKey(i)); - pluginString += "<" DISTRHO_PLUGIN_URI "#" + key + ">\n"; + pluginString += "<" DISTRHO_PLUGIN_URI "#" + plugin.getStateKey(i) + ">\n"; pluginString += " a lv2:Parameter ;\n"; - pluginString += " rdfs:label \"" + key + "\" ;\n"; + pluginString += " rdfs:label \"" + plugin.getStateLabel(i) + "\" ;\n"; + + const String& comment(plugin.getStateDescription(i)); + + if (comment.isNotEmpty()) + { + if (comment.contains('"') || comment.contains('\n')) + pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n"; + else + pluginString += " rdfs:comment \"" + comment + "\" ;\n"; + } if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath) pluginString += " rdfs:range atom:Path .\n\n"; @@ -414,11 +423,16 @@ void lv2_generate_ttl(const char* const basename) { for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) { - if ((plugin.getStateHints(i) & kStateIsHostVisible) == 0x0) + const uint32_t hints = plugin.getStateHints(i); + + if ((hints & kStateIsHostReadable) == 0x0) continue; const String& key(plugin.getStateKey(i)); - pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n"; + pluginString += " patch:readable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n"; + + if ((hints & kStateIsHostWritable) == kStateIsHostWritable) + pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n"; } pluginString += "\n"; } @@ -1274,7 +1288,7 @@ void lv2_generate_ttl(const char* const basename) for (uint32_t j=0; j"; diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 8e5bbad7..95429c7c 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -737,7 +737,7 @@ public: for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; - fStateMap[key] = fPlugin.getState(key); + fStateMap[key] = fPlugin.getStateValue(key); } # endif @@ -813,7 +813,7 @@ public: for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; - fStateMap[key] = fPlugin.getState(key); + fStateMap[key] = fPlugin.getStateValue(key); } # endif diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index a4355191..9abeb858 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -910,7 +910,7 @@ public: for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; - fStateMap[key] = fPlugin.getState(key); + fStateMap[key] = fPlugin.getStateValue(key); } #endif @@ -1924,7 +1924,7 @@ public: for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key = cit->first; - fStateMap[key] = fPlugin.getState(key); + fStateMap[key] = fPlugin.getStateValue(key); } #endif diff --git a/examples/FileHandling/FileHandlingPlugin.cpp b/examples/FileHandling/FileHandlingPlugin.cpp index 0e1f28f6..196bc13f 100644 --- a/examples/FileHandling/FileHandlingPlugin.cpp +++ b/examples/FileHandling/FileHandlingPlugin.cpp @@ -124,42 +124,29 @@ protected: } /** - Set the state key and default value of @a index.@n - This function will be called once, shortly after the plugin is created.@n - Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. + Initialize the state @a index.@n + This function will be called once, shortly after the plugin is created. */ - void initState(uint32_t index, String& stateKey, String& defaultStateValue) override + void initState(uint32_t index, State& state) override { switch (index) { case kStateFile1: - stateKey = "file1"; + state.key = "file1"; + state.label = "File 1"; break; case kStateFile2: - stateKey = "file2"; + state.key = "file2"; + state.label = "File 2"; break; case kStateFile3: - stateKey = "file3"; + state.key = "file3"; + state.label = "File 3"; break; } - defaultStateValue = ""; - } - - /** - TODO API under construction - */ - uint32_t getStateHints(uint32_t index) override - { - switch (index) - { - case kStateFile1: - case kStateFile2: - case kStateFile3: - return kStateIsFilenamePath; - } - - return 0x0; + state.hints = kStateIsFilenamePath; + state.defaultValue = ""; } /* -------------------------------------------------------------------------------------------------------- diff --git a/examples/States/ExamplePluginStates.cpp b/examples/States/ExamplePluginStates.cpp index df52e07b..47a8e4dc 100644 --- a/examples/States/ExamplePluginStates.cpp +++ b/examples/States/ExamplePluginStates.cpp @@ -127,43 +127,54 @@ The plugin will be treated as an effect, but it will not change the host audio." } /** - Set the state key and default value of @a index. - This function will be called once, shortly after the plugin is created. + Initialize the state @a index.@n + This function will be called once, shortly after the plugin is created.@n + Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled. */ - void initState(uint32_t index, String& stateKey, String& defaultStateValue) override + void initState(uint32_t index, State& state) override { switch (index) { case 0: - stateKey = "top-left"; + state.key = "top-left"; + state.label = "Top Left"; break; case 1: - stateKey = "top-center"; + state.key = "top-center"; + state.label = "Top Center"; break; case 2: - stateKey = "top-right"; + state.key = "top-right"; + state.label = "Top Right"; break; case 3: - stateKey = "middle-left"; + state.key = "middle-left"; + state.label = "Middle Left"; break; case 4: - stateKey = "middle-center"; + state.key = "middle-center"; + state.label = "Middle Center"; break; case 5: - stateKey = "middle-right"; + state.key = "middle-right"; + state.label = "Middle Right"; break; case 6: - stateKey = "bottom-left"; + state.key = "bottom-left"; + state.label = "Bottom Left"; break; case 7: - stateKey = "bottom-center"; + state.key = "bottom-center"; + state.label = "Bottom Center"; break; case 8: - stateKey = "bottom-right"; + state.key = "bottom-right"; + state.label = "Bottom Right"; break; } - defaultStateValue = "false"; + state.hints = kStateIsHostWritable; + state.defaultValue = "false"; } /* -------------------------------------------------------------------------------------------------------- From b3c75c9a2a050057dbbe3f4bf085da19b1ba5e05 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 01:06:49 +0000 Subject: [PATCH 311/504] Clarify initial/default state Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 3 ++- examples/FileHandling/FileHandlingPlugin.cpp | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index ef7c228f..98912de6 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -688,7 +688,8 @@ struct State { String key; /** - The default value of this state. + The default value of this state.@n + Can be left empty if considered a valid initial state. */ String defaultValue; diff --git a/examples/FileHandling/FileHandlingPlugin.cpp b/examples/FileHandling/FileHandlingPlugin.cpp index 196bc13f..691a4557 100644 --- a/examples/FileHandling/FileHandlingPlugin.cpp +++ b/examples/FileHandling/FileHandlingPlugin.cpp @@ -146,7 +146,6 @@ protected: } state.hints = kStateIsFilenamePath; - state.defaultValue = ""; } /* -------------------------------------------------------------------------------------------------------- From 485832aff30132f2e8139719c96a97057aa0fe62 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 01:25:19 +0000 Subject: [PATCH 312/504] Get rid of the old DISTRHO_PLUGIN_WANT_STATEFILES Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 6 ++---- distrho/DistrhoUI.hpp | 4 +--- distrho/src/DistrhoPlugin.cpp | 2 -- distrho/src/DistrhoPluginChecks.h | 18 +++++++++--------- distrho/src/DistrhoPluginLV2export.cpp | 17 ++++++++++------- distrho/src/DistrhoUI.cpp | 2 +- distrho/src/DistrhoUIPrivateData.hpp | 4 ++-- examples/FileHandling/DistrhoPluginInfo.h | 1 - 8 files changed, 25 insertions(+), 29 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 98912de6..138caeaa 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -1096,12 +1096,10 @@ protected: */ virtual void initState(uint32_t index, State& state); - DISTRHO_DEPRECATED_BY("getStateHints(uint32_t,State&)") + DISTRHO_DEPRECATED_BY("initState(uint32_t,State&)") virtual void initState(uint32_t, String&, String&) {} -#endif -#if DISTRHO_PLUGIN_WANT_STATEFILES - DISTRHO_DEPRECATED_BY("getStateHints") + DISTRHO_DEPRECATED_BY("initState(uint32_t,State&)") virtual bool isStateFile(uint32_t) { return false; } #endif diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index dc09efda..e22a656b 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -164,9 +164,7 @@ public: @TODO Document this. */ void setState(const char* key, const char* value); -#endif -#if DISTRHO_PLUGIN_WANT_STATEFILES /** Request a new file from the host, matching the properties of a state key.@n This will use the native host file browser if available, otherwise a DPF built-in file browser is used.@n @@ -327,7 +325,7 @@ protected: This action happens after the user confirms the action, so the file browser dialog will be closed at this point. The default implementation does nothing. - If you need to use files as plugin state, please setup and use DISTRHO_PLUGIN_WANT_STATEFILES instead. + If you need to use files as plugin state, please setup and use states with kStateIsFilenamePath instead. */ virtual void uiFileBrowserSelected(const char* filename); #endif diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index b0fa0ae4..9c4c9b65 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -197,10 +197,8 @@ void Plugin::initState(const uint32_t index, State& state) #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif initState(index, stateKey, defaultStateValue); - #if DISTRHO_PLUGIN_WANT_STATEFILES if (isStateFile(index)) hints = kStateIsFilenamePath; - #endif #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index dfcda68b..d3d4b174 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -81,10 +81,6 @@ # define DISTRHO_PLUGIN_WANT_STATE 0 #endif -#ifndef DISTRHO_PLUGIN_WANT_STATEFILES -# define DISTRHO_PLUGIN_WANT_STATEFILES 0 -#endif - #ifndef DISTRHO_PLUGIN_WANT_FULL_STATE # define DISTRHO_PLUGIN_WANT_FULL_STATE 0 # define DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET @@ -137,11 +133,15 @@ #endif // ----------------------------------------------------------------------- -// Enable state if plugin wants state files - -#if DISTRHO_PLUGIN_WANT_STATEFILES && ! DISTRHO_PLUGIN_WANT_STATE -# undef DISTRHO_PLUGIN_WANT_STATE -# define DISTRHO_PLUGIN_WANT_STATE 1 +// Enable state if plugin wants state files (deprecated) + +#ifdef DISTRHO_PLUGIN_WANT_STATEFILES +# warning DISTRHO_PLUGIN_WANT_STATEFILES is deprecated +# undef DISTRHO_PLUGIN_WANT_STATEFILES +# if ! DISTRHO_PLUGIN_WANT_STATE +# undef DISTRHO_PLUGIN_WANT_STATE +# define DISTRHO_PLUGIN_WANT_STATE 1 +# endif #endif // ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 9a8f74e4..e1e0fc65 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -641,8 +641,9 @@ void lv2_generate_ttl(const char* const basename) # if DISTRHO_PLUGIN_WANT_MIDI_INPUT pluginString += " atom:supports midi:MidiEvent ;\n"; # endif -# if DISTRHO_PLUGIN_WANT_STATEFILES - pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; +# if DISTRHO_PLUGIN_WANT_STATE + if (hasHostVisibleState) + pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; # endif # if DISTRHO_PLUGIN_WANT_TIMEPOS pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; @@ -665,8 +666,9 @@ void lv2_generate_ttl(const char* const basename) # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT pluginString += " atom:supports midi:MidiEvent ;\n"; # endif -# if DISTRHO_PLUGIN_WANT_STATEFILES - pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; +# if DISTRHO_PLUGIN_WANT_STATE + if (hasHostVisibleState) + pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; # endif pluginString += " ] ;\n\n"; ++portIndex; @@ -1262,10 +1264,11 @@ void lv2_generate_ttl(const char* const basename) # 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"; diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 7366be34..5e98c289 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -264,7 +264,7 @@ void UI::setState(const char* key, const char* value) } #endif -#if DISTRHO_PLUGIN_WANT_STATEFILES +#if DISTRHO_PLUGIN_WANT_STATE bool UI::requestStateFile(const char* key) { return uiData->fileRequestCallback(key); diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index e3e846d4..5d56586b 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -441,7 +441,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) if (fileRequestCallbackFunc != nullptr) return fileRequestCallbackFunc(callbacksPtr, key); -#if DISTRHO_PLUGIN_WANT_STATEFILES && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) +#if DISTRHO_PLUGIN_WANT_STATE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) std::free(uiStateFileKeyRequest); uiStateFileKeyRequest = strdup(key); DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false); @@ -473,7 +473,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) if (initializing) return; -# if DISTRHO_PLUGIN_WANT_STATEFILES +# if DISTRHO_PLUGIN_WANT_STATE if (char* const key = ui->uiData->uiStateFileKeyRequest) { ui->uiData->uiStateFileKeyRequest = nullptr; diff --git a/examples/FileHandling/DistrhoPluginInfo.h b/examples/FileHandling/DistrhoPluginInfo.h index 4b840dfd..cf155408 100644 --- a/examples/FileHandling/DistrhoPluginInfo.h +++ b/examples/FileHandling/DistrhoPluginInfo.h @@ -26,7 +26,6 @@ #define DISTRHO_PLUGIN_NUM_INPUTS 1 #define DISTRHO_PLUGIN_NUM_OUTPUTS 1 #define DISTRHO_PLUGIN_WANT_STATE 1 -#define DISTRHO_PLUGIN_WANT_STATEFILES 1 #define DISTRHO_UI_USER_RESIZABLE 1 #define DISTRHO_UI_USE_NANOVG 1 From b214c2bd527843c95ce8bce6802df4c7e051eb05 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 01:36:55 +0000 Subject: [PATCH 313/504] Fix lv2 presets with host readable state Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index e1e0fc65..9878e5eb 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -429,10 +429,10 @@ void lv2_generate_ttl(const char* const basename) continue; const String& key(plugin.getStateKey(i)); - pluginString += " patch:readable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n"; + pluginString += " patch:readable <" DISTRHO_PLUGIN_URI "#" + key + "> ;\n"; if ((hints & kStateIsHostWritable) == kStateIsHostWritable) - pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n"; + pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + "> ;\n"; } pluginString += "\n"; } @@ -1293,7 +1293,14 @@ void lv2_generate_ttl(const char* const basename) const String key = plugin.getStateKey(j); const String value = plugin.getStateValue(key); - presetString += " <" DISTRHO_PLUGIN_LV2_STATE_PREFIX + key + ">"; + presetString += " <"; + + if (plugin.getStateHints(i) & kStateIsHostReadable) + presetString += DISTRHO_PLUGIN_URI "#"; + else + presetString += DISTRHO_PLUGIN_LV2_STATE_PREFIX; + + presetString += key + ">"; if (value.length() < 10) presetString += " \"" + value + "\" ;\n"; From 2d58e840beb47164b2765bb38c0801edaf2684c9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 02:23:49 +0000 Subject: [PATCH 314/504] Update lv2 headers a bit, same version as for carla Signed-off-by: falkTX --- distrho/src/lv2/atom-forge.h | 105 +++++---------- distrho/src/lv2/atom-util.h | 130 ++++++++++++++---- distrho/src/lv2/atom.h | 88 ++++++------ distrho/src/lv2/buf-size.h | 33 +++-- distrho/src/lv2/data-access.h | 19 ++- distrho/src/lv2/dynmanifest.h | 17 ++- distrho/src/lv2/event-helpers.h | 33 +++-- distrho/src/lv2/event.h | 54 ++++---- distrho/src/lv2/instance-access.h | 29 ++-- distrho/src/lv2/log.h | 36 +++-- distrho/src/lv2/logger.h | 54 +++++--- distrho/src/lv2/lv2.h | 214 ++++++++++++++++-------------- distrho/src/lv2/midi.h | 106 ++++++++------- distrho/src/lv2/morph.h | 34 +++-- distrho/src/lv2/options.h | 29 ++-- distrho/src/lv2/parameters.h | 71 ++++++---- distrho/src/lv2/patch.h | 74 ++++++----- distrho/src/lv2/port-groups.h | 87 ++++++------ distrho/src/lv2/port-props.h | 44 +++--- distrho/src/lv2/presets.h | 25 ++-- distrho/src/lv2/resize-port.h | 36 +++-- distrho/src/lv2/state.h | 148 +++++++++++++-------- distrho/src/lv2/time.h | 56 ++++---- distrho/src/lv2/units.h | 85 ++++++------ distrho/src/lv2/uri-map.h | 16 ++- distrho/src/lv2/urid.h | 31 +++-- distrho/src/lv2/worker.h | 55 +++++--- 27 files changed, 991 insertions(+), 718 deletions(-) diff --git a/distrho/src/lv2/atom-forge.h b/distrho/src/lv2/atom-forge.h index d808f0d9..25087b0d 100644 --- a/distrho/src/lv2/atom-forge.h +++ b/distrho/src/lv2/atom-forge.h @@ -1,5 +1,5 @@ /* - Copyright 2008-2013 David Robillard + Copyright 2008-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -39,6 +39,12 @@ This header is non-normative, it is provided for convenience. */ +/** + @defgroup forge Forge + @ingroup atom + @{ +*/ + #ifndef LV2_ATOM_FORGE_H #define LV2_ATOM_FORGE_H @@ -48,7 +54,7 @@ #include "atom-util.h" #include "urid.h" -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) # define LV2_ATOM_FORGE_DEPRECATED __attribute__((__deprecated__)) #else # define LV2_ATOM_FORGE_DEPRECATED @@ -60,6 +66,15 @@ extern "C" { # include #endif +// Disable deprecation warnings for Blank and Resource +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /** Handle for LV2_Atom_Forge_Sink. */ typedef void* LV2_Atom_Forge_Sink_Handle; @@ -119,21 +134,14 @@ static inline void lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); /** - Initialise @p forge. + Initialise `forge`. - URIs will be mapped using @p map and stored, a reference to @p map itself is + URIs will be mapped using `map` and stored, a reference to `map` itself is not held. */ static inline void lv2_atom_forge_init(LV2_Atom_Forge* forge, const LV2_URID_Map* map) { -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif lv2_atom_forge_set_buffer(forge, NULL, 0); forge->Blank = map->map(map->handle, LV2_ATOM__Blank); forge->Bool = map->map(map->handle, LV2_ATOM__Bool); @@ -153,13 +161,9 @@ lv2_atom_forge_init(LV2_Atom_Forge* forge, const LV2_URID_Map* map) forge->URI = map->map(map->handle, LV2_ATOM__URI); forge->URID = map->map(map->handle, LV2_ATOM__URID); forge->Vector = map->map(map->handle, LV2_ATOM__Vector); -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic pop -#endif } +/** Access the Atom pointed to by a reference. */ static inline LV2_Atom* lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref) { @@ -208,47 +212,23 @@ lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type) (lv2_atom_forge_deref(forge, forge->stack->ref)->type == type); } -/** Return true iff @p type is an atom:Object. */ +/** Return true iff `type` is an atom:Object. */ static inline bool lv2_atom_forge_is_object_type(const LV2_Atom_Forge* forge, uint32_t type) { -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif return (type == forge->Object || type == forge->Blank || type == forge->Resource); -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic pop -#endif } -/** Return true iff @p type is an atom:Object with a blank ID. */ +/** Return true iff `type` is an atom:Object with a blank ID. */ static inline bool lv2_atom_forge_is_blank(const LV2_Atom_Forge* forge, uint32_t type, const LV2_Atom_Object_Body* body) { -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif return (type == forge->Blank || (type == forge->Object && body->id == 0)); -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic pop -#endif } /** @@ -257,7 +237,7 @@ lv2_atom_forge_is_blank(const LV2_Atom_Forge* forge, @{ */ -/** Set the output buffer where @p forge will write atoms. */ +/** Set the output buffer where `forge` will write atoms. */ static inline void lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size) { @@ -271,7 +251,7 @@ lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size) } /** - Set the sink function where @p forge will write output. + Set the sink function where `forge` will write output. The return value of forge functions is an LV2_Atom_Forge_Ref which is an integer type safe to use as a pointer but is otherwise opaque. The sink @@ -456,7 +436,7 @@ lv2_atom_forge_typed_string(LV2_Atom_Forge* forge, return out; } -/** Write an atom:String. Note that @p str need not be NULL terminated. */ +/** Write an atom:String. Note that `str` need not be NULL terminated. */ static inline LV2_Atom_Forge_Ref lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len) { @@ -464,7 +444,7 @@ lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len) } /** - Write an atom:URI. Note that @p uri need not be NULL terminated. + Write an atom:URI. Note that `uri` need not be NULL terminated. This does not map the URI, but writes the complete URI string. To write a mapped URI, use lv2_atom_forge_urid(). */ @@ -474,7 +454,7 @@ lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len) return lv2_atom_forge_typed_string(forge, forge->URI, uri, len); } -/** Write an atom:Path. Note that @p path need not be NULL terminated. */ +/** Write an atom:Path. Note that `path` need not be NULL terminated. */ static inline LV2_Atom_Forge_Ref lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len) { @@ -617,24 +597,12 @@ lv2_atom_forge_resource(LV2_Atom_Forge* forge, LV2_URID id, LV2_URID otype) { -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif const LV2_Atom_Object a = { { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Resource }, { id, otype } }; return lv2_atom_forge_push( forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic pop -#endif } /** @@ -650,24 +618,12 @@ lv2_atom_forge_blank(LV2_Atom_Forge* forge, uint32_t id, LV2_URID otype) { -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif const LV2_Atom_Object a = { { (uint32_t)sizeof(LV2_Atom_Object_Body), forge->Blank }, { id, otype } }; return lv2_atom_forge_push( forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -# pragma GCC diagnostic pop -#endif } /** @@ -738,8 +694,15 @@ lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats) /** @} + @} */ +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +# pragma GCC diagnostic pop +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/distrho/src/lv2/atom-util.h b/distrho/src/lv2/atom-util.h index cad90f89..dcf23ac0 100644 --- a/distrho/src/lv2/atom-util.h +++ b/distrho/src/lv2/atom-util.h @@ -1,5 +1,5 @@ /* - Copyright 2008-2013 David Robillard + Copyright 2008-2015 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -22,6 +22,12 @@ This header is non-normative, it is provided for convenience. */ +/** + @defgroup util Utilities + @ingroup atom + @{ +*/ + #ifndef LV2_ATOM_UTIL_H #define LV2_ATOM_UTIL_H @@ -44,21 +50,21 @@ lv2_atom_pad_size(uint32_t size) return (size + 7U) & (~7U); } -/** Return the total size of @p atom, including the header. */ +/** Return the total size of `atom`, including the header. */ static inline uint32_t lv2_atom_total_size(const LV2_Atom* atom) { return (uint32_t)sizeof(LV2_Atom) + atom->size; } -/** Return true iff @p atom is null. */ +/** Return true iff `atom` is null. */ static inline bool lv2_atom_is_null(const LV2_Atom* atom) { return !atom || (atom->type == 0 && atom->size == 0); } -/** Return true iff @p a is equal to @p b. */ +/** Return true iff `a` is equal to `b`. */ static inline bool lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b) { @@ -79,14 +85,21 @@ lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body) return (const LV2_Atom_Event*)(body + 1); } +/** Get an iterator pointing to the end of a Sequence body. */ +static inline const LV2_Atom_Event* +lv2_atom_sequence_end(const LV2_Atom_Sequence_Body* body, uint32_t size) +{ + return (const LV2_Atom_Event*)((const uint8_t*)body + lv2_atom_pad_size(size)); +} + /** Get an iterator pointing to the end of a Sequence body. */ static inline LV2_Atom_Event* -lv2_atom_sequence_end(LV2_Atom_Sequence_Body* body, uint32_t size) +lv2_atom_sequence_end2(LV2_Atom_Sequence_Body* body, uint32_t size) { return (LV2_Atom_Event*)((uint8_t*)body + lv2_atom_pad_size(size)); } -/** Return true iff @p i has reached the end of @p body. */ +/** Return true iff `i` has reached the end of `body`. */ static inline bool lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, uint32_t size, @@ -95,7 +108,7 @@ lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, return (const uint8_t*)i >= ((const uint8_t*)body + size); } -/** Return an iterator to the element following @p i. */ +/** Return an iterator to the element following `i`. */ static inline const LV2_Atom_Event* lv2_atom_sequence_next(const LV2_Atom_Event* i) { @@ -119,13 +132,13 @@ lv2_atom_sequence_next(const LV2_Atom_Event* i) #define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ for (const LV2_Atom_Event* iter = lv2_atom_sequence_begin(&(seq)->body); \ !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ - (iter) = lv2_atom_sequence_next(iter)) + iter = lv2_atom_sequence_next(iter)) /** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ #define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ for (const LV2_Atom_Event* iter = lv2_atom_sequence_begin(body); \ !lv2_atom_sequence_is_end(body, size, (iter)); \ - (iter) = lv2_atom_sequence_next(iter)) + iter = lv2_atom_sequence_next(iter)) /** @} @@ -134,7 +147,7 @@ lv2_atom_sequence_next(const LV2_Atom_Event* i) */ /** - Clear all events from @p sequence. + Clear all events from `sequence`. This simply resets the size field, the other fields are left untouched. */ @@ -145,14 +158,14 @@ lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) } /** - Append an event at the end of @p sequence. + Append an event at the end of `sequence`. @param seq Sequence to append to. @param capacity Total capacity of the sequence atom (e.g. as set by the host for sequence output ports). @param event Event to write. - @return A pointer to the newly written event in @p seq, + @return A pointer to the newly written event in `seq`, or NULL on failure (insufficient space). */ static inline LV2_Atom_Event* @@ -165,7 +178,7 @@ lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, return NULL; } - LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size); + LV2_Atom_Event* e = lv2_atom_sequence_end2(&seq->body, seq->atom.size); memcpy(e, event, total_size); seq->atom.size += lv2_atom_pad_size(total_size); @@ -179,21 +192,21 @@ lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, @{ */ -/** Get an iterator pointing to the first element in @p tup. */ +/** Get an iterator pointing to the first element in `tup`. */ static inline const LV2_Atom* lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup) { return (const LV2_Atom*)(LV2_ATOM_BODY_CONST(tup)); } -/** Return true iff @p i has reached the end of @p body. */ +/** Return true iff `i` has reached the end of `body`. */ static inline bool lv2_atom_tuple_is_end(const void* body, uint32_t size, const LV2_Atom* i) { return (const uint8_t*)i >= ((const uint8_t*)body + size); } -/** Return an iterator to the element following @p i. */ +/** Return an iterator to the element following `i`. */ static inline const LV2_Atom* lv2_atom_tuple_next(const LV2_Atom* i) { @@ -208,21 +221,21 @@ lv2_atom_tuple_next(const LV2_Atom* i) This macro is used similarly to a for loop (which it expands to), e.g.: @code - LV2_ATOMO_TUPLE_FOREACH(tuple, elem) { + LV2_ATOM_TUPLE_FOREACH(tuple, elem) { // Do something with elem (an LV2_Atom*) here... } @endcode */ #define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ for (const LV2_Atom* iter = lv2_atom_tuple_begin(tuple); \ - !lv2_atom_tuple_is_end(LV2_ATOM_BODY_CONST(tuple), (tuple)->size, (iter)); \ - (iter) = lv2_atom_tuple_next(iter)) + !lv2_atom_tuple_is_end(LV2_ATOM_BODY_CONST(tuple), (tuple)->atom.size, (iter)); \ + iter = lv2_atom_tuple_next(iter)) /** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ #define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ for (const LV2_Atom* iter = (const LV2_Atom*)body; \ !lv2_atom_tuple_is_end(body, size, (iter)); \ - (iter) = lv2_atom_tuple_next(iter)) + iter = lv2_atom_tuple_next(iter)) /** @} @@ -230,14 +243,14 @@ lv2_atom_tuple_next(const LV2_Atom* i) @{ */ -/** Return a pointer to the first property in @p body. */ +/** Return a pointer to the first property in `body`. */ static inline const LV2_Atom_Property_Body* lv2_atom_object_begin(const LV2_Atom_Object_Body* body) { return (const LV2_Atom_Property_Body*)(body + 1); } -/** Return true iff @p i has reached the end of @p obj. */ +/** Return true iff `i` has reached the end of `obj`. */ static inline bool lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, uint32_t size, @@ -246,7 +259,7 @@ lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, return (const uint8_t*)i >= ((const uint8_t*)body + size); } -/** Return an iterator to the property following @p i. */ +/** Return an iterator to the property following `i`. */ static inline const LV2_Atom_Property_Body* lv2_atom_object_next(const LV2_Atom_Property_Body* i) { @@ -272,13 +285,13 @@ lv2_atom_object_next(const LV2_Atom_Property_Body* i) #define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ for (const LV2_Atom_Property_Body* iter = lv2_atom_object_begin(&(obj)->body); \ !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ - (iter) = lv2_atom_object_next(iter)) + iter = lv2_atom_object_next(iter)) /** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ #define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ for (const LV2_Atom_Property_Body* iter = lv2_atom_object_begin(body); \ !lv2_atom_object_is_end(body, size, (iter)); \ - (iter) = lv2_atom_object_next(iter)) + iter = lv2_atom_object_next(iter)) /** @} @@ -297,10 +310,10 @@ static const LV2_Atom_Object_Query LV2_ATOM_OBJECT_QUERY_END = { 0, NULL }; /** Get an object's values for various keys. - The value pointer of each item in @p query will be set to the location of - the corresponding value in @p object. Every value pointer in @p query MUST - be initialised to NULL. This function reads @p object in a single linear - sweep. By allocating @p query on the stack, objects can be "queried" + The value pointer of each item in `query` will be set to the location of + the corresponding value in `object`. Every value pointer in `query` MUST + be initialised to NULL. This function reads `object` in a single linear + sweep. By allocating `query` on the stack, objects can be "queried" quickly without allocating any memory. This function is realtime safe. This function can only do "flat" queries, it is not smart enough to match @@ -359,6 +372,7 @@ lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) va_start(args, body); for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { if (!va_arg(args, const LV2_Atom**)) { + va_end(args); return -1; } } @@ -372,6 +386,7 @@ lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) if (qkey == prop->key && !*qval) { *qval = &prop->value; if (++matches == n_queries) { + va_end(args); return matches; } break; @@ -436,6 +451,63 @@ lv2_atom_object_get(const LV2_Atom_Object* object, ...) } /** + Variable argument version of lv2_atom_object_query() with types. + + This is like lv2_atom_object_get(), but each entry has an additional + parameter to specify the required type. Only atoms with a matching type + will be selected. + + The arguments should be a series of uint32_t key, const LV2_Atom**, uint32_t + type triples, terminated by a zero key. The value pointers MUST be + initialized to NULL. For example: + + @code + const LV2_Atom_String* name = NULL; + const LV2_Atom_Int* age = NULL; + lv2_atom_object_get(obj, + uris.name_key, &name, uris.atom_String, + uris.age_key, &age, uris.atom_Int + 0); + @endcode +*/ +static inline int +lv2_atom_object_get_typed(const LV2_Atom_Object* object, ...) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of keys so we can short-circuit when done */ + va_list args; + va_start(args, object); + for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { + if (!va_arg(args, const LV2_Atom**) || + !va_arg(args, uint32_t)) { + return -1; + } + } + va_end(args); + + LV2_ATOM_OBJECT_FOREACH(object, prop) { + va_start(args, object); + for (int i = 0; i < n_queries; ++i) { + const uint32_t qkey = va_arg(args, uint32_t); + const LV2_Atom** qval = va_arg(args, const LV2_Atom**); + const uint32_t qtype = va_arg(args, uint32_t); + if (!*qval && qkey == prop->key && qtype == prop->value.type) { + *qval = &prop->value; + if (++matches == n_queries) { + return matches; + } + break; + } + } + va_end(args); + } + return matches; +} + +/** + @} @} */ diff --git a/distrho/src/lv2/atom.h b/distrho/src/lv2/atom.h index 91c4fa3f..2cb07c6c 100644 --- a/distrho/src/lv2/atom.h +++ b/distrho/src/lv2/atom.h @@ -1,5 +1,5 @@ /* - Copyright 2008-2012 David Robillard + Copyright 2008-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,8 +15,12 @@ */ /** - @file atom.h C header for the LV2 Atom extension - . + @defgroup atom Atom + + A generic value container and several data types, see + for details. + + @{ */ #ifndef LV2_ATOM_H @@ -25,50 +29,52 @@ #include #include -#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" -#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" - -#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" -#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" -#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" -#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" -#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" -#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" -#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" -#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" -#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" -#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" -#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" -#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" -#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" -#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" -#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" -#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" -#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" -#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" -#define LV2_ATOM__String LV2_ATOM_PREFIX "String" -#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" -#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" -#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" -#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" -#define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" -#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" -#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" -#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" -#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" -#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" -#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" -#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" - -#define LV2_ATOM_REFERENCE_TYPE 0 +#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" ///< http://lv2plug.in/ns/ext/atom +#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" ///< http://lv2plug.in/ns/ext/atom# + +#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" ///< http://lv2plug.in/ns/ext/atom#Atom +#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" ///< http://lv2plug.in/ns/ext/atom#AtomPort +#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" ///< http://lv2plug.in/ns/ext/atom#Blank +#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" ///< http://lv2plug.in/ns/ext/atom#Bool +#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" ///< http://lv2plug.in/ns/ext/atom#Chunk +#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" ///< http://lv2plug.in/ns/ext/atom#Double +#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" ///< http://lv2plug.in/ns/ext/atom#Event +#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" ///< http://lv2plug.in/ns/ext/atom#Float +#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" ///< http://lv2plug.in/ns/ext/atom#Int +#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" ///< http://lv2plug.in/ns/ext/atom#Literal +#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" ///< http://lv2plug.in/ns/ext/atom#Long +#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" ///< http://lv2plug.in/ns/ext/atom#Number +#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" ///< http://lv2plug.in/ns/ext/atom#Object +#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" ///< http://lv2plug.in/ns/ext/atom#Path +#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" ///< http://lv2plug.in/ns/ext/atom#Property +#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" ///< http://lv2plug.in/ns/ext/atom#Resource +#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" ///< http://lv2plug.in/ns/ext/atom#Sequence +#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" ///< http://lv2plug.in/ns/ext/atom#Sound +#define LV2_ATOM__String LV2_ATOM_PREFIX "String" ///< http://lv2plug.in/ns/ext/atom#String +#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" ///< http://lv2plug.in/ns/ext/atom#Tuple +#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" ///< http://lv2plug.in/ns/ext/atom#URI +#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" ///< http://lv2plug.in/ns/ext/atom#URID +#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" ///< http://lv2plug.in/ns/ext/atom#Vector +#define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" ///< http://lv2plug.in/ns/ext/atom#atomTransfer +#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" ///< http://lv2plug.in/ns/ext/atom#beatTime +#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" ///< http://lv2plug.in/ns/ext/atom#bufferType +#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" ///< http://lv2plug.in/ns/ext/atom#childType +#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" ///< http://lv2plug.in/ns/ext/atom#eventTransfer +#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" ///< http://lv2plug.in/ns/ext/atom#frameTime +#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" ///< http://lv2plug.in/ns/ext/atom#supports +#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" ///< http://lv2plug.in/ns/ext/atom#timeUnit + +#define LV2_ATOM_REFERENCE_TYPE 0 ///< The special type for a reference atom #ifdef __cplusplus extern "C" { #endif +/** @cond */ /** This expression will fail to compile if double does not fit in 64 bits. */ typedef char lv2_atom_assert_double_fits_in_64_bits[ ((sizeof(double) <= sizeof(uint64_t)) * 2) - 1]; +/** @endcond */ /** Return a pointer to the contents of an Atom. The "contents" of an atom @@ -239,6 +245,10 @@ typedef struct { LV2_Atom_Sequence_Body body; /**< Body. */ } LV2_Atom_Sequence; +/** + @} +*/ + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/distrho/src/lv2/buf-size.h b/distrho/src/lv2/buf-size.h index 3987e31b..900f8fa2 100644 --- a/distrho/src/lv2/buf-size.h +++ b/distrho/src/lv2/buf-size.h @@ -1,5 +1,5 @@ /* - Copyright 2007-2012 David Robillard + Copyright 2007-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -17,15 +17,28 @@ #ifndef LV2_BUF_SIZE_H #define LV2_BUF_SIZE_H -#define LV2_BUF_SIZE_URI "http://lv2plug.in/ns/ext/buf-size" -#define LV2_BUF_SIZE_PREFIX LV2_BUF_SIZE_URI "#" +/** + @defgroup buf-size Buffer Size -#define LV2_BUF_SIZE__boundedBlockLength LV2_BUF_SIZE_PREFIX "boundedBlockLength" -#define LV2_BUF_SIZE__fixedBlockLength LV2_BUF_SIZE_PREFIX "fixedBlockLength" -#define LV2_BUF_SIZE__maxBlockLength LV2_BUF_SIZE_PREFIX "maxBlockLength" -#define LV2_BUF_SIZE__minBlockLength LV2_BUF_SIZE_PREFIX "minBlockLength" -#define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength" -#define LV2_BUF_SIZE__powerOf2BlockLength LV2_BUF_SIZE_PREFIX "powerOf2BlockLength" -#define LV2_BUF_SIZE__sequenceSize LV2_BUF_SIZE_PREFIX "sequenceSize" + Access to, and restrictions on, buffer sizes; see + for details. + + @{ +*/ + +#define LV2_BUF_SIZE_URI "http://lv2plug.in/ns/ext/buf-size" ///< http://lv2plug.in/ns/ext/buf-size +#define LV2_BUF_SIZE_PREFIX LV2_BUF_SIZE_URI "#" ///< http://lv2plug.in/ns/ext/buf-size# + +#define LV2_BUF_SIZE__boundedBlockLength LV2_BUF_SIZE_PREFIX "boundedBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#boundedBlockLength +#define LV2_BUF_SIZE__fixedBlockLength LV2_BUF_SIZE_PREFIX "fixedBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#fixedBlockLength +#define LV2_BUF_SIZE__maxBlockLength LV2_BUF_SIZE_PREFIX "maxBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#maxBlockLength +#define LV2_BUF_SIZE__minBlockLength LV2_BUF_SIZE_PREFIX "minBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#minBlockLength +#define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#nominalBlockLength +#define LV2_BUF_SIZE__powerOf2BlockLength LV2_BUF_SIZE_PREFIX "powerOf2BlockLength" ///< http://lv2plug.in/ns/ext/buf-size#powerOf2BlockLength +#define LV2_BUF_SIZE__sequenceSize LV2_BUF_SIZE_PREFIX "sequenceSize" ///< http://lv2plug.in/ns/ext/buf-size#sequenceSize + +/** + @} +*/ #endif /* LV2_BUF_SIZE_H */ diff --git a/distrho/src/lv2/data-access.h b/distrho/src/lv2/data-access.h index 68aaf629..2f4fd28d 100644 --- a/distrho/src/lv2/data-access.h +++ b/distrho/src/lv2/data-access.h @@ -1,6 +1,6 @@ /* LV2 Data Access Extension - Copyright 2008-2011 David Robillard + Copyright 2008-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -16,18 +16,19 @@ */ /** - @file data-access.h - C header for the LV2 Extension Data extension - . + @defgroup data-access Data Access - This extension defines a method for (e.g.) plugin UIs to have (possibly - marshalled) access to the extension_data function on a plugin instance. + Access to plugin extension_data() for UIs, see + for details. + + @{ */ #ifndef LV2_DATA_ACCESS_H #define LV2_DATA_ACCESS_H -#define LV2_DATA_ACCESS_URI "http://lv2plug.in/ns/ext/data-access" +#define LV2_DATA_ACCESS_URI "http://lv2plug.in/ns/ext/data-access" ///< http://lv2plug.in/ns/ext/data-access +#define LV2_DATA_ACCESS_PREFIX LV2_DATA_ACCESS_URI "#" ///< http://lv2plug.in/ns/ext/data-access# #ifdef __cplusplus extern "C" { @@ -61,3 +62,7 @@ typedef struct { #endif #endif /* LV2_DATA_ACCESS_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/dynmanifest.h b/distrho/src/lv2/dynmanifest.h index fad33c86..afe32ae6 100644 --- a/distrho/src/lv2/dynmanifest.h +++ b/distrho/src/lv2/dynmanifest.h @@ -16,10 +16,12 @@ */ /** - @file dynmanifest.h - C header for the LV2 Dynamic Manifest extension - . - Revision: 1.2 + @defgroup dynmanifest Dynamic Manifest + + Support for dynamic data generation, see + for details. + + @{ */ #ifndef LV2_DYN_MANIFEST_H_INCLUDED @@ -29,7 +31,8 @@ #include "lv2.h" -#define LV2_DYN_MANIFEST_URI "http://lv2plug.in/ns/ext/dynmanifest" +#define LV2_DYN_MANIFEST_URI "http://lv2plug.in/ns/ext/dynmanifest" ///< http://lv2plug.in/ns/ext/dynmanifest +#define LV2_DYN_MANIFEST_PREFIX LV2_DYN_MANIFEST_URI "#" ///< http://lv2plug.in/ns/ext/dynmanifest# #ifdef __cplusplus extern "C" { @@ -142,3 +145,7 @@ void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle); #endif #endif /* LV2_DYN_MANIFEST_H_INCLUDED */ + +/** + @} +*/ diff --git a/distrho/src/lv2/event-helpers.h b/distrho/src/lv2/event-helpers.h index 5caaab50..2c627f7d 100644 --- a/distrho/src/lv2/event-helpers.h +++ b/distrho/src/lv2/event-helpers.h @@ -1,5 +1,5 @@ /* - Copyright 2008-2012 David Robillard + Copyright 2008-2015 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -96,8 +96,8 @@ typedef struct { } LV2_Event_Iterator; -/** Reset an iterator to point to the start of @a buf. - * @return True if @a iter is valid, otherwise false (buffer is empty) */ +/** Reset an iterator to point to the start of `buf`. + * @return True if `iter` is valid, otherwise false (buffer is empty) */ static inline bool lv2_event_begin(LV2_Event_Iterator* iter, LV2_Event_Buffer* buf) @@ -108,8 +108,8 @@ lv2_event_begin(LV2_Event_Iterator* iter, } -/** Check if @a iter is valid. - * @return True if @a iter is valid, otherwise false (past end of buffer) */ +/** Check if `iter` is valid. + * @return True if `iter` is valid, otherwise false (past end of buffer) */ static inline bool lv2_event_is_valid(LV2_Event_Iterator* iter) { @@ -117,9 +117,9 @@ lv2_event_is_valid(LV2_Event_Iterator* iter) } -/** Advance @a iter forward one event. - * @a iter must be valid. - * @return True if @a iter is valid, otherwise false (reached end of buffer) */ +/** Advance `iter` forward one event. + * `iter` must be valid. + * @return True if `iter` is valid, otherwise false (reached end of buffer) */ static inline bool lv2_event_increment(LV2_Event_Iterator* iter) { @@ -138,11 +138,11 @@ lv2_event_increment(LV2_Event_Iterator* iter) /** Dereference an event iterator (get the event currently pointed at). - * @a iter must be valid. - * @a data if non-NULL, will be set to point to the contents of the event + * `iter` must be valid. + * `data` if non-NULL, will be set to point to the contents of the event * returned. - * @return A Pointer to the event @a iter is currently pointing at, or NULL - * if the end of the buffer is reached (in which case @a data is + * @return A Pointer to the event `iter` is currently pointing at, or NULL + * if the end of the buffer is reached (in which case `data` is * also set to NULL). */ static inline LV2_Event* lv2_event_get(LV2_Event_Iterator* iter, @@ -162,8 +162,8 @@ lv2_event_get(LV2_Event_Iterator* iter, } -/** Write an event at @a iter. - * The event (if any) pointed to by @a iter will be overwritten, and @a iter +/** Write an event at `iter`. + * The event (if any) pointed to by `iter` will be overwritten, and `iter` * incremented to point to the following event (i.e. several calls to this * function can be done in sequence without twiddling iter in-between). * @return True if event was written, otherwise false (buffer is full). */ @@ -230,8 +230,8 @@ lv2_event_reserve(LV2_Event_Iterator* iter, } -/** Write an event at @a iter. - * The event (if any) pointed to by @a iter will be overwritten, and @a iter +/** Write an event at `iter`. + * The event (if any) pointed to by `iter` will be overwritten, and `iter` * incremented to point to the following event (i.e. several calls to this * function can be done in sequence without twiddling iter in-between). * @return True if event was written, otherwise false (buffer is full). */ @@ -263,4 +263,3 @@ lv2_event_write_event(LV2_Event_Iterator* iter, #endif #endif /* LV2_EVENT_HELPERS_H */ - diff --git a/distrho/src/lv2/event.h b/distrho/src/lv2/event.h index e33d8621..8b850d11 100644 --- a/distrho/src/lv2/event.h +++ b/distrho/src/lv2/event.h @@ -1,5 +1,5 @@ /* - Copyright 2008-2011 David Robillard + Copyright 2008-2016 David Robillard Copyright 2006-2007 Lars Luthman Permission to use, copy, modify, and/or distribute this software for any @@ -16,38 +16,32 @@ */ /** - @file event.h - C API for the LV2 Event extension . - - This extension is a generic transport mechanism for time stamped events - of any type (e.g. MIDI, OSC, ramps, etc). Each port can transport mixed - events of any type; the type of events and timestamps are defined by a URI - which is mapped to an integer by the host for performance reasons. - - This extension requires the host to support the LV2 URI Map extension. - Any host which supports this extension MUST guarantee that any call to - the LV2 URI Map uri_to_id function with the URI of this extension as the - 'map' argument returns a value within the range of uint16_t. + @defgroup event Event + + Generic time-stamped events, see for + details. + + @{ */ #ifndef LV2_EVENT_H #define LV2_EVENT_H -#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" -#define LV2_EVENT_PREFIX LV2_EVENT_URI "#" +#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" ///< http://lv2plug.in/ns/ext/event +#define LV2_EVENT_PREFIX LV2_EVENT_URI "#" ///< http://lv2plug.in/ns/ext/event# -#define LV2_EVENT__Event LV2_EVENT_PREFIX "Event" -#define LV2_EVENT__EventPort LV2_EVENT_PREFIX "EventPort" -#define LV2_EVENT__FrameStamp LV2_EVENT_PREFIX "FrameStamp" -#define LV2_EVENT__TimeStamp LV2_EVENT_PREFIX "TimeStamp" -#define LV2_EVENT__generatesTimeStamp LV2_EVENT_PREFIX "generatesTimeStamp" -#define LV2_EVENT__generic LV2_EVENT_PREFIX "generic" -#define LV2_EVENT__inheritsEvent LV2_EVENT_PREFIX "inheritsEvent" -#define LV2_EVENT__inheritsTimeStamp LV2_EVENT_PREFIX "inheritsTimeStamp" -#define LV2_EVENT__supportsEvent LV2_EVENT_PREFIX "supportsEvent" -#define LV2_EVENT__supportsTimeStamp LV2_EVENT_PREFIX "supportsTimeStamp" +#define LV2_EVENT__Event LV2_EVENT_PREFIX "Event" ///< http://lv2plug.in/ns/ext/event#Event +#define LV2_EVENT__EventPort LV2_EVENT_PREFIX "EventPort" ///< http://lv2plug.in/ns/ext/event#EventPort +#define LV2_EVENT__FrameStamp LV2_EVENT_PREFIX "FrameStamp" ///< http://lv2plug.in/ns/ext/event#FrameStamp +#define LV2_EVENT__TimeStamp LV2_EVENT_PREFIX "TimeStamp" ///< http://lv2plug.in/ns/ext/event#TimeStamp +#define LV2_EVENT__generatesTimeStamp LV2_EVENT_PREFIX "generatesTimeStamp" ///< http://lv2plug.in/ns/ext/event#generatesTimeStamp +#define LV2_EVENT__generic LV2_EVENT_PREFIX "generic" ///< http://lv2plug.in/ns/ext/event#generic +#define LV2_EVENT__inheritsEvent LV2_EVENT_PREFIX "inheritsEvent" ///< http://lv2plug.in/ns/ext/event#inheritsEvent +#define LV2_EVENT__inheritsTimeStamp LV2_EVENT_PREFIX "inheritsTimeStamp" ///< http://lv2plug.in/ns/ext/event#inheritsTimeStamp +#define LV2_EVENT__supportsEvent LV2_EVENT_PREFIX "supportsEvent" ///< http://lv2plug.in/ns/ext/event#supportsEvent +#define LV2_EVENT__supportsTimeStamp LV2_EVENT_PREFIX "supportsTimeStamp" ///< http://lv2plug.in/ns/ext/event#supportsTimeStamp -#define LV2_EVENT_AUDIO_STAMP 0 +#define LV2_EVENT_AUDIO_STAMP 0 ///< Special timestamp type for audio frames #include @@ -255,7 +249,7 @@ typedef struct { @param context The calling context. Like event types, this is a mapped URI, see lv2_context.h. Simple plugin with just a run() method should pass 0 here (the ID of the 'standard' LV2 run context). The host - guarantees that this function is realtime safe iff @a context is + guarantees that this function is realtime safe iff the context is realtime safe. PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. @@ -278,7 +272,7 @@ typedef struct { @param context The calling context. Like event types, this is a mapped URI, see lv2_context.h. Simple plugin with just a run() method should pass 0 here (the ID of the 'standard' LV2 run context). The host - guarantees that this function is realtime safe iff @a context is + guarantees that this function is realtime safe iff the context is realtime safe. PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. @@ -292,3 +286,7 @@ typedef struct { #endif #endif /* LV2_EVENT_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/instance-access.h b/distrho/src/lv2/instance-access.h index 06d8ddec..7e328fd8 100644 --- a/distrho/src/lv2/instance-access.h +++ b/distrho/src/lv2/instance-access.h @@ -1,6 +1,6 @@ /* LV2 Instance Access Extension - Copyright 2008-2012 David Robillard + Copyright 2008-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,23 +15,22 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef LV2_INSTANCE_ACCESS_H -#define LV2_INSTANCE_ACCESS_H +/** + @defgroup instance-access Instance Access -#define LV2_INSTANCE_ACCESS_URI "http://lv2plug.in/ns/ext/instance-access" + Access to the LV2_Handle of a plugin for UIs; see + for details. -/** - @file instance-access.h - C header for the LV2 Instance Access extension - . - - This extension defines a method for (e.g.) plugin UIs to get a direct - handle to an LV2 plugin instance (LV2_Handle), if possible. - - To support this feature the host must pass an LV2_Feature struct to the - UI instantiate method with URI "http://lv2plug.in/ns/ext/instance-access" - and data pointed directly to the LV2_Handle of the plugin instance. + @{ */ +#ifndef LV2_INSTANCE_ACCESS_H +#define LV2_INSTANCE_ACCESS_H + +#define LV2_INSTANCE_ACCESS_URI "http://lv2plug.in/ns/ext/instance-access" ///< http://lv2plug.in/ns/ext/instance-access + #endif /* LV2_INSTANCE_ACCESS_H */ +/** + @} +*/ diff --git a/distrho/src/lv2/log.h b/distrho/src/lv2/log.h index 4b7b0f49..047316dc 100644 --- a/distrho/src/lv2/log.h +++ b/distrho/src/lv2/log.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,22 +15,26 @@ */ /** - @file log.h C header for the LV2 Log extension - . + @defgroup log Log + + Interface for plugins to log via the host; see + for details. + + @{ */ #ifndef LV2_LOG_H #define LV2_LOG_H -#define LV2_LOG_URI "http://lv2plug.in/ns/ext/log" -#define LV2_LOG_PREFIX LV2_LOG_URI "#" +#define LV2_LOG_URI "http://lv2plug.in/ns/ext/log" ///< http://lv2plug.in/ns/ext/log +#define LV2_LOG_PREFIX LV2_LOG_URI "#" ///< http://lv2plug.in/ns/ext/log# -#define LV2_LOG__Entry LV2_LOG_PREFIX "Entry" -#define LV2_LOG__Error LV2_LOG_PREFIX "Error" -#define LV2_LOG__Note LV2_LOG_PREFIX "Note" -#define LV2_LOG__Trace LV2_LOG_PREFIX "Trace" -#define LV2_LOG__Warning LV2_LOG_PREFIX "Warning" -#define LV2_LOG__log LV2_LOG_PREFIX "log" +#define LV2_LOG__Entry LV2_LOG_PREFIX "Entry" ///< http://lv2plug.in/ns/ext/log#Entry +#define LV2_LOG__Error LV2_LOG_PREFIX "Error" ///< http://lv2plug.in/ns/ext/log#Error +#define LV2_LOG__Note LV2_LOG_PREFIX "Note" ///< http://lv2plug.in/ns/ext/log#Note +#define LV2_LOG__Trace LV2_LOG_PREFIX "Trace" ///< http://lv2plug.in/ns/ext/log#Trace +#define LV2_LOG__Warning LV2_LOG_PREFIX "Warning" ///< http://lv2plug.in/ns/ext/log#Warning +#define LV2_LOG__log LV2_LOG_PREFIX "log" ///< http://lv2plug.in/ns/ext/log#log #include @@ -40,12 +44,14 @@ extern "C" { #endif +/** @cond */ #ifdef __GNUC__ /** Allow type checking of printf-like functions. */ # define LV2_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) #else # define LV2_LOG_FUNC(fmt, arg1) #endif +/** @endcond */ /** Opaque data to host data for LV2_Log_Log. @@ -69,7 +75,7 @@ typedef struct _LV2_Log { The API of this function matches that of the standard C printf function, except for the addition of the first two parameters. This function may - be called from any non-realtime context, or from any context if @p type + be called from any non-realtime context, or from any context if `type` is @ref LV2_LOG__Trace. */ LV2_LOG_FUNC(3, 4) @@ -83,7 +89,7 @@ typedef struct _LV2_Log { The API of this function matches that of the standard C vprintf function, except for the addition of the first two parameters. This function may be called from any non-realtime context, or from any - context if @p type is @ref LV2_LOG__Trace. + context if `type` is @ref LV2_LOG__Trace. */ LV2_LOG_FUNC(3, 0) int (*vprintf)(LV2_Log_Handle handle, @@ -97,3 +103,7 @@ typedef struct _LV2_Log { #endif #endif /* LV2_LOG_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/logger.h b/distrho/src/lv2/logger.h index 91435448..138b96d1 100644 --- a/distrho/src/lv2/logger.h +++ b/distrho/src/lv2/logger.h @@ -1,5 +1,5 @@ /* - Copyright 2012-2013 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,13 +15,14 @@ */ /** - @file logger.h Convenience API for easy logging in plugin code. + @defgroup logger Logger + @ingroup log - This file provides simple wrappers for the most common log operations for - use in plugin implementations. If host support for logging is not - available, then these functions will print to stderr instead. + Convenience API for easy logging in plugin code. This API provides simple + wrappers for logging from a plugin, which automatically fall back to + printing to stderr if host support is unavailabe. - This header is non-normative, it is provided for convenience. + @{ */ #ifndef LV2_ATOM_LOGGER_H @@ -49,27 +50,40 @@ typedef struct { } LV2_Log_Logger; /** - Initialise @p logger. + Set `map` as the URI map for `logger`. - URIs will be mapped using @p map and stored, a reference to @p map itself is - not held. Both @p map and @p log may be NULL when unsupported by the host, - in which case the implementation will fall back to printing to stderr. + This affects the message type URIDs (Error, Warning, etc) which are passed + to the log's print functions. */ static inline void -lv2_log_logger_init(LV2_Log_Logger* logger, - LV2_URID_Map* map, - LV2_Log_Log* log) +lv2_log_logger_set_map(LV2_Log_Logger* logger, LV2_URID_Map* map) { - memset(logger, 0, sizeof(LV2_Log_Logger)); - logger->log = log; if (map) { logger->Error = map->map(map->handle, LV2_LOG__Error); logger->Note = map->map(map->handle, LV2_LOG__Note); logger->Trace = map->map(map->handle, LV2_LOG__Trace); logger->Warning = map->map(map->handle, LV2_LOG__Warning); + } else { + logger->Error = logger->Note = logger->Trace = logger->Warning = 0; } } +/** + Initialise `logger`. + + URIs will be mapped using `map` and stored, a reference to `map` itself is + not held. Both `map` and `log` may be NULL when unsupported by the host, + in which case the implementation will fall back to printing to stderr. +*/ +static inline void +lv2_log_logger_init(LV2_Log_Logger* logger, + LV2_URID_Map* map, + LV2_Log_Log* log) +{ + logger->log = log; + lv2_log_logger_set_map(logger, map); +} + /** Log a message to the host, or stderr if support is unavailable. */ @@ -80,7 +94,7 @@ lv2_log_vprintf(LV2_Log_Logger* logger, const char* fmt, va_list args) { - if (logger->log) { + if (logger && logger->log) { return logger->log->vprintf(logger->log->handle, type, fmt, args); } else { return vfprintf(stderr, fmt, args); @@ -135,12 +149,12 @@ lv2_log_warning(LV2_Log_Logger* logger, const char* fmt, ...) return ret; } -/** - @} -*/ - #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_LOG_LOGGER_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/lv2.h b/distrho/src/lv2/lv2.h index 6e0edaa0..eaca514a 100644 --- a/distrho/src/lv2/lv2.h +++ b/distrho/src/lv2/lv2.h @@ -19,9 +19,11 @@ */ /** - @file lv2.h - API for the LV2 specification . - Revision: 12.0 + @defgroup lv2core LV2 Core + + Core LV2 specification, see for details. + + @{ */ #ifndef LV2_H_INCLUDED @@ -29,93 +31,93 @@ #include -#define LV2_CORE_URI "http://lv2plug.in/ns/lv2core" -#define LV2_CORE_PREFIX LV2_CORE_URI "#" - -#define LV2_CORE__AllpassPlugin LV2_CORE_PREFIX "AllpassPlugin" -#define LV2_CORE__AmplifierPlugin LV2_CORE_PREFIX "AmplifierPlugin" -#define LV2_CORE__AnalyserPlugin LV2_CORE_PREFIX "AnalyserPlugin" -#define LV2_CORE__AudioPort LV2_CORE_PREFIX "AudioPort" -#define LV2_CORE__BandpassPlugin LV2_CORE_PREFIX "BandpassPlugin" -#define LV2_CORE__CVPort LV2_CORE_PREFIX "CVPort" -#define LV2_CORE__ChorusPlugin LV2_CORE_PREFIX "ChorusPlugin" -#define LV2_CORE__CombPlugin LV2_CORE_PREFIX "CombPlugin" -#define LV2_CORE__CompressorPlugin LV2_CORE_PREFIX "CompressorPlugin" -#define LV2_CORE__ConstantPlugin LV2_CORE_PREFIX "ConstantPlugin" -#define LV2_CORE__ControlPort LV2_CORE_PREFIX "ControlPort" -#define LV2_CORE__ConverterPlugin LV2_CORE_PREFIX "ConverterPlugin" -#define LV2_CORE__DelayPlugin LV2_CORE_PREFIX "DelayPlugin" -#define LV2_CORE__DistortionPlugin LV2_CORE_PREFIX "DistortionPlugin" -#define LV2_CORE__DynamicsPlugin LV2_CORE_PREFIX "DynamicsPlugin" -#define LV2_CORE__EQPlugin LV2_CORE_PREFIX "EQPlugin" -#define LV2_CORE__EnvelopePlugin LV2_CORE_PREFIX "EnvelopePlugin" -#define LV2_CORE__ExpanderPlugin LV2_CORE_PREFIX "ExpanderPlugin" -#define LV2_CORE__ExtensionData LV2_CORE_PREFIX "ExtensionData" -#define LV2_CORE__Feature LV2_CORE_PREFIX "Feature" -#define LV2_CORE__FilterPlugin LV2_CORE_PREFIX "FilterPlugin" -#define LV2_CORE__FlangerPlugin LV2_CORE_PREFIX "FlangerPlugin" -#define LV2_CORE__FunctionPlugin LV2_CORE_PREFIX "FunctionPlugin" -#define LV2_CORE__GatePlugin LV2_CORE_PREFIX "GatePlugin" -#define LV2_CORE__GeneratorPlugin LV2_CORE_PREFIX "GeneratorPlugin" -#define LV2_CORE__HighpassPlugin LV2_CORE_PREFIX "HighpassPlugin" -#define LV2_CORE__InputPort LV2_CORE_PREFIX "InputPort" -#define LV2_CORE__InstrumentPlugin LV2_CORE_PREFIX "InstrumentPlugin" -#define LV2_CORE__LimiterPlugin LV2_CORE_PREFIX "LimiterPlugin" -#define LV2_CORE__LowpassPlugin LV2_CORE_PREFIX "LowpassPlugin" -#define LV2_CORE__MixerPlugin LV2_CORE_PREFIX "MixerPlugin" -#define LV2_CORE__ModulatorPlugin LV2_CORE_PREFIX "ModulatorPlugin" -#define LV2_CORE__MultiEQPlugin LV2_CORE_PREFIX "MultiEQPlugin" -#define LV2_CORE__OscillatorPlugin LV2_CORE_PREFIX "OscillatorPlugin" -#define LV2_CORE__OutputPort LV2_CORE_PREFIX "OutputPort" -#define LV2_CORE__ParaEQPlugin LV2_CORE_PREFIX "ParaEQPlugin" -#define LV2_CORE__PhaserPlugin LV2_CORE_PREFIX "PhaserPlugin" -#define LV2_CORE__PitchPlugin LV2_CORE_PREFIX "PitchPlugin" -#define LV2_CORE__Plugin LV2_CORE_PREFIX "Plugin" -#define LV2_CORE__PluginBase LV2_CORE_PREFIX "PluginBase" -#define LV2_CORE__Point LV2_CORE_PREFIX "Point" -#define LV2_CORE__Port LV2_CORE_PREFIX "Port" -#define LV2_CORE__PortProperty LV2_CORE_PREFIX "PortProperty" -#define LV2_CORE__Resource LV2_CORE_PREFIX "Resource" -#define LV2_CORE__ReverbPlugin LV2_CORE_PREFIX "ReverbPlugin" -#define LV2_CORE__ScalePoint LV2_CORE_PREFIX "ScalePoint" -#define LV2_CORE__SimulatorPlugin LV2_CORE_PREFIX "SimulatorPlugin" -#define LV2_CORE__SpatialPlugin LV2_CORE_PREFIX "SpatialPlugin" -#define LV2_CORE__Specification LV2_CORE_PREFIX "Specification" -#define LV2_CORE__SpectralPlugin LV2_CORE_PREFIX "SpectralPlugin" -#define LV2_CORE__UtilityPlugin LV2_CORE_PREFIX "UtilityPlugin" -#define LV2_CORE__WaveshaperPlugin LV2_CORE_PREFIX "WaveshaperPlugin" -#define LV2_CORE__appliesTo LV2_CORE_PREFIX "appliesTo" -#define LV2_CORE__binary LV2_CORE_PREFIX "binary" -#define LV2_CORE__connectionOptional LV2_CORE_PREFIX "connectionOptional" -#define LV2_CORE__control LV2_CORE_PREFIX "control" -#define LV2_CORE__default LV2_CORE_PREFIX "default" -#define LV2_CORE__designation LV2_CORE_PREFIX "designation" -#define LV2_CORE__documentation LV2_CORE_PREFIX "documentation" -#define LV2_CORE__enumeration LV2_CORE_PREFIX "enumeration" -#define LV2_CORE__extensionData LV2_CORE_PREFIX "extensionData" -#define LV2_CORE__freeWheeling LV2_CORE_PREFIX "freeWheeling" -#define LV2_CORE__hardRTCapable LV2_CORE_PREFIX "hardRTCapable" -#define LV2_CORE__inPlaceBroken LV2_CORE_PREFIX "inPlaceBroken" -#define LV2_CORE__index LV2_CORE_PREFIX "index" -#define LV2_CORE__integer LV2_CORE_PREFIX "integer" -#define LV2_CORE__isLive LV2_CORE_PREFIX "isLive" -#define LV2_CORE__latency LV2_CORE_PREFIX "latency" -#define LV2_CORE__maximum LV2_CORE_PREFIX "maximum" -#define LV2_CORE__microVersion LV2_CORE_PREFIX "microVersion" -#define LV2_CORE__minimum LV2_CORE_PREFIX "minimum" -#define LV2_CORE__minorVersion LV2_CORE_PREFIX "minorVersion" -#define LV2_CORE__name LV2_CORE_PREFIX "name" -#define LV2_CORE__optionalFeature LV2_CORE_PREFIX "optionalFeature" -#define LV2_CORE__port LV2_CORE_PREFIX "port" -#define LV2_CORE__portProperty LV2_CORE_PREFIX "portProperty" -#define LV2_CORE__project LV2_CORE_PREFIX "project" -#define LV2_CORE__prototype LV2_CORE_PREFIX "prototype" -#define LV2_CORE__reportsLatency LV2_CORE_PREFIX "reportsLatency" -#define LV2_CORE__requiredFeature LV2_CORE_PREFIX "requiredFeature" -#define LV2_CORE__sampleRate LV2_CORE_PREFIX "sampleRate" -#define LV2_CORE__scalePoint LV2_CORE_PREFIX "scalePoint" -#define LV2_CORE__symbol LV2_CORE_PREFIX "symbol" -#define LV2_CORE__toggled LV2_CORE_PREFIX "toggled" +#define LV2_CORE_URI "http://lv2plug.in/ns/lv2core" ///< http://lv2plug.in/ns/lv2core +#define LV2_CORE_PREFIX LV2_CORE_URI "#" ///< http://lv2plug.in/ns/lv2core# + +#define LV2_CORE__AllpassPlugin LV2_CORE_PREFIX "AllpassPlugin" ///< http://lv2plug.in/ns/lv2core#AllpassPlugin +#define LV2_CORE__AmplifierPlugin LV2_CORE_PREFIX "AmplifierPlugin" ///< http://lv2plug.in/ns/lv2core#AmplifierPlugin +#define LV2_CORE__AnalyserPlugin LV2_CORE_PREFIX "AnalyserPlugin" ///< http://lv2plug.in/ns/lv2core#AnalyserPlugin +#define LV2_CORE__AudioPort LV2_CORE_PREFIX "AudioPort" ///< http://lv2plug.in/ns/lv2core#AudioPort +#define LV2_CORE__BandpassPlugin LV2_CORE_PREFIX "BandpassPlugin" ///< http://lv2plug.in/ns/lv2core#BandpassPlugin +#define LV2_CORE__CVPort LV2_CORE_PREFIX "CVPort" ///< http://lv2plug.in/ns/lv2core#CVPort +#define LV2_CORE__ChorusPlugin LV2_CORE_PREFIX "ChorusPlugin" ///< http://lv2plug.in/ns/lv2core#ChorusPlugin +#define LV2_CORE__CombPlugin LV2_CORE_PREFIX "CombPlugin" ///< http://lv2plug.in/ns/lv2core#CombPlugin +#define LV2_CORE__CompressorPlugin LV2_CORE_PREFIX "CompressorPlugin" ///< http://lv2plug.in/ns/lv2core#CompressorPlugin +#define LV2_CORE__ConstantPlugin LV2_CORE_PREFIX "ConstantPlugin" ///< http://lv2plug.in/ns/lv2core#ConstantPlugin +#define LV2_CORE__ControlPort LV2_CORE_PREFIX "ControlPort" ///< http://lv2plug.in/ns/lv2core#ControlPort +#define LV2_CORE__ConverterPlugin LV2_CORE_PREFIX "ConverterPlugin" ///< http://lv2plug.in/ns/lv2core#ConverterPlugin +#define LV2_CORE__DelayPlugin LV2_CORE_PREFIX "DelayPlugin" ///< http://lv2plug.in/ns/lv2core#DelayPlugin +#define LV2_CORE__DistortionPlugin LV2_CORE_PREFIX "DistortionPlugin" ///< http://lv2plug.in/ns/lv2core#DistortionPlugin +#define LV2_CORE__DynamicsPlugin LV2_CORE_PREFIX "DynamicsPlugin" ///< http://lv2plug.in/ns/lv2core#DynamicsPlugin +#define LV2_CORE__EQPlugin LV2_CORE_PREFIX "EQPlugin" ///< http://lv2plug.in/ns/lv2core#EQPlugin +#define LV2_CORE__EnvelopePlugin LV2_CORE_PREFIX "EnvelopePlugin" ///< http://lv2plug.in/ns/lv2core#EnvelopePlugin +#define LV2_CORE__ExpanderPlugin LV2_CORE_PREFIX "ExpanderPlugin" ///< http://lv2plug.in/ns/lv2core#ExpanderPlugin +#define LV2_CORE__ExtensionData LV2_CORE_PREFIX "ExtensionData" ///< http://lv2plug.in/ns/lv2core#ExtensionData +#define LV2_CORE__Feature LV2_CORE_PREFIX "Feature" ///< http://lv2plug.in/ns/lv2core#Feature +#define LV2_CORE__FilterPlugin LV2_CORE_PREFIX "FilterPlugin" ///< http://lv2plug.in/ns/lv2core#FilterPlugin +#define LV2_CORE__FlangerPlugin LV2_CORE_PREFIX "FlangerPlugin" ///< http://lv2plug.in/ns/lv2core#FlangerPlugin +#define LV2_CORE__FunctionPlugin LV2_CORE_PREFIX "FunctionPlugin" ///< http://lv2plug.in/ns/lv2core#FunctionPlugin +#define LV2_CORE__GatePlugin LV2_CORE_PREFIX "GatePlugin" ///< http://lv2plug.in/ns/lv2core#GatePlugin +#define LV2_CORE__GeneratorPlugin LV2_CORE_PREFIX "GeneratorPlugin" ///< http://lv2plug.in/ns/lv2core#GeneratorPlugin +#define LV2_CORE__HighpassPlugin LV2_CORE_PREFIX "HighpassPlugin" ///< http://lv2plug.in/ns/lv2core#HighpassPlugin +#define LV2_CORE__InputPort LV2_CORE_PREFIX "InputPort" ///< http://lv2plug.in/ns/lv2core#InputPort +#define LV2_CORE__InstrumentPlugin LV2_CORE_PREFIX "InstrumentPlugin" ///< http://lv2plug.in/ns/lv2core#InstrumentPlugin +#define LV2_CORE__LimiterPlugin LV2_CORE_PREFIX "LimiterPlugin" ///< http://lv2plug.in/ns/lv2core#LimiterPlugin +#define LV2_CORE__LowpassPlugin LV2_CORE_PREFIX "LowpassPlugin" ///< http://lv2plug.in/ns/lv2core#LowpassPlugin +#define LV2_CORE__MixerPlugin LV2_CORE_PREFIX "MixerPlugin" ///< http://lv2plug.in/ns/lv2core#MixerPlugin +#define LV2_CORE__ModulatorPlugin LV2_CORE_PREFIX "ModulatorPlugin" ///< http://lv2plug.in/ns/lv2core#ModulatorPlugin +#define LV2_CORE__MultiEQPlugin LV2_CORE_PREFIX "MultiEQPlugin" ///< http://lv2plug.in/ns/lv2core#MultiEQPlugin +#define LV2_CORE__OscillatorPlugin LV2_CORE_PREFIX "OscillatorPlugin" ///< http://lv2plug.in/ns/lv2core#OscillatorPlugin +#define LV2_CORE__OutputPort LV2_CORE_PREFIX "OutputPort" ///< http://lv2plug.in/ns/lv2core#OutputPort +#define LV2_CORE__ParaEQPlugin LV2_CORE_PREFIX "ParaEQPlugin" ///< http://lv2plug.in/ns/lv2core#ParaEQPlugin +#define LV2_CORE__PhaserPlugin LV2_CORE_PREFIX "PhaserPlugin" ///< http://lv2plug.in/ns/lv2core#PhaserPlugin +#define LV2_CORE__PitchPlugin LV2_CORE_PREFIX "PitchPlugin" ///< http://lv2plug.in/ns/lv2core#PitchPlugin +#define LV2_CORE__Plugin LV2_CORE_PREFIX "Plugin" ///< http://lv2plug.in/ns/lv2core#Plugin +#define LV2_CORE__PluginBase LV2_CORE_PREFIX "PluginBase" ///< http://lv2plug.in/ns/lv2core#PluginBase +#define LV2_CORE__Point LV2_CORE_PREFIX "Point" ///< http://lv2plug.in/ns/lv2core#Point +#define LV2_CORE__Port LV2_CORE_PREFIX "Port" ///< http://lv2plug.in/ns/lv2core#Port +#define LV2_CORE__PortProperty LV2_CORE_PREFIX "PortProperty" ///< http://lv2plug.in/ns/lv2core#PortProperty +#define LV2_CORE__Resource LV2_CORE_PREFIX "Resource" ///< http://lv2plug.in/ns/lv2core#Resource +#define LV2_CORE__ReverbPlugin LV2_CORE_PREFIX "ReverbPlugin" ///< http://lv2plug.in/ns/lv2core#ReverbPlugin +#define LV2_CORE__ScalePoint LV2_CORE_PREFIX "ScalePoint" ///< http://lv2plug.in/ns/lv2core#ScalePoint +#define LV2_CORE__SimulatorPlugin LV2_CORE_PREFIX "SimulatorPlugin" ///< http://lv2plug.in/ns/lv2core#SimulatorPlugin +#define LV2_CORE__SpatialPlugin LV2_CORE_PREFIX "SpatialPlugin" ///< http://lv2plug.in/ns/lv2core#SpatialPlugin +#define LV2_CORE__Specification LV2_CORE_PREFIX "Specification" ///< http://lv2plug.in/ns/lv2core#Specification +#define LV2_CORE__SpectralPlugin LV2_CORE_PREFIX "SpectralPlugin" ///< http://lv2plug.in/ns/lv2core#SpectralPlugin +#define LV2_CORE__UtilityPlugin LV2_CORE_PREFIX "UtilityPlugin" ///< http://lv2plug.in/ns/lv2core#UtilityPlugin +#define LV2_CORE__WaveshaperPlugin LV2_CORE_PREFIX "WaveshaperPlugin" ///< http://lv2plug.in/ns/lv2core#WaveshaperPlugin +#define LV2_CORE__appliesTo LV2_CORE_PREFIX "appliesTo" ///< http://lv2plug.in/ns/lv2core#appliesTo +#define LV2_CORE__binary LV2_CORE_PREFIX "binary" ///< http://lv2plug.in/ns/lv2core#binary +#define LV2_CORE__connectionOptional LV2_CORE_PREFIX "connectionOptional" ///< http://lv2plug.in/ns/lv2core#connectionOptional +#define LV2_CORE__control LV2_CORE_PREFIX "control" ///< http://lv2plug.in/ns/lv2core#control +#define LV2_CORE__default LV2_CORE_PREFIX "default" ///< http://lv2plug.in/ns/lv2core#default +#define LV2_CORE__designation LV2_CORE_PREFIX "designation" ///< http://lv2plug.in/ns/lv2core#designation +#define LV2_CORE__documentation LV2_CORE_PREFIX "documentation" ///< http://lv2plug.in/ns/lv2core#documentation +#define LV2_CORE__enumeration LV2_CORE_PREFIX "enumeration" ///< http://lv2plug.in/ns/lv2core#enumeration +#define LV2_CORE__extensionData LV2_CORE_PREFIX "extensionData" ///< http://lv2plug.in/ns/lv2core#extensionData +#define LV2_CORE__freeWheeling LV2_CORE_PREFIX "freeWheeling" ///< http://lv2plug.in/ns/lv2core#freeWheeling +#define LV2_CORE__hardRTCapable LV2_CORE_PREFIX "hardRTCapable" ///< http://lv2plug.in/ns/lv2core#hardRTCapable +#define LV2_CORE__inPlaceBroken LV2_CORE_PREFIX "inPlaceBroken" ///< http://lv2plug.in/ns/lv2core#inPlaceBroken +#define LV2_CORE__index LV2_CORE_PREFIX "index" ///< http://lv2plug.in/ns/lv2core#index +#define LV2_CORE__integer LV2_CORE_PREFIX "integer" ///< http://lv2plug.in/ns/lv2core#integer +#define LV2_CORE__isLive LV2_CORE_PREFIX "isLive" ///< http://lv2plug.in/ns/lv2core#isLive +#define LV2_CORE__latency LV2_CORE_PREFIX "latency" ///< http://lv2plug.in/ns/lv2core#latency +#define LV2_CORE__maximum LV2_CORE_PREFIX "maximum" ///< http://lv2plug.in/ns/lv2core#maximum +#define LV2_CORE__microVersion LV2_CORE_PREFIX "microVersion" ///< http://lv2plug.in/ns/lv2core#microVersion +#define LV2_CORE__minimum LV2_CORE_PREFIX "minimum" ///< http://lv2plug.in/ns/lv2core#minimum +#define LV2_CORE__minorVersion LV2_CORE_PREFIX "minorVersion" ///< http://lv2plug.in/ns/lv2core#minorVersion +#define LV2_CORE__name LV2_CORE_PREFIX "name" ///< http://lv2plug.in/ns/lv2core#name +#define LV2_CORE__optionalFeature LV2_CORE_PREFIX "optionalFeature" ///< http://lv2plug.in/ns/lv2core#optionalFeature +#define LV2_CORE__port LV2_CORE_PREFIX "port" ///< http://lv2plug.in/ns/lv2core#port +#define LV2_CORE__portProperty LV2_CORE_PREFIX "portProperty" ///< http://lv2plug.in/ns/lv2core#portProperty +#define LV2_CORE__project LV2_CORE_PREFIX "project" ///< http://lv2plug.in/ns/lv2core#project +#define LV2_CORE__prototype LV2_CORE_PREFIX "prototype" ///< http://lv2plug.in/ns/lv2core#prototype +#define LV2_CORE__reportsLatency LV2_CORE_PREFIX "reportsLatency" ///< http://lv2plug.in/ns/lv2core#reportsLatency +#define LV2_CORE__requiredFeature LV2_CORE_PREFIX "requiredFeature" ///< http://lv2plug.in/ns/lv2core#requiredFeature +#define LV2_CORE__sampleRate LV2_CORE_PREFIX "sampleRate" ///< http://lv2plug.in/ns/lv2core#sampleRate +#define LV2_CORE__scalePoint LV2_CORE_PREFIX "scalePoint" ///< http://lv2plug.in/ns/lv2core#scalePoint +#define LV2_CORE__symbol LV2_CORE_PREFIX "symbol" ///< http://lv2plug.in/ns/lv2core#symbol +#define LV2_CORE__toggled LV2_CORE_PREFIX "toggled" ///< http://lv2plug.in/ns/lv2core#toggled #ifdef __cplusplus extern "C" { @@ -135,7 +137,7 @@ typedef void * LV2_Handle; Features allow hosts to make additional functionality available to plugins without requiring modification to the LV2 API. Extensions may define new - features and specify the @ref URI and @ref data to be used if necessary. + features and specify the `URI` and `data` to be used if necessary. Some features, such as lv2:isLive, do not require the host to pass data. */ typedef struct _LV2_Feature { @@ -150,7 +152,7 @@ typedef struct _LV2_Feature { Pointer to arbitrary data. The format of this data is defined by the extension which describes the - feature with the given @ref URI. + feature with the given `URI`. */ void * data; } LV2_Feature; @@ -276,12 +278,12 @@ typedef struct _LV2_Descriptor { things that the plugin MUST NOT do within the run() function (see lv2core.ttl for details). - As a special case, when @p sample_count == 0, the plugin should update + As a special case, when `sample_count` is 0, the plugin should update any output ports that represent a single instant in time (e.g. control ports, but not audio ports). This is particularly useful for latent plugins, which should update their latency output port so hosts can pre-roll plugins to compute latency. Plugins MUST NOT crash when - @p sample_count == 0. + `sample_count` is 0. @param instance Instance to be run. @@ -340,14 +342,23 @@ typedef struct _LV2_Descriptor { const void * (*extension_data)(const char * uri); } LV2_Descriptor; +/** + Helper macro needed for LV2_SYMBOL_EXPORT when using C++. +*/ +#ifdef __cplusplus +# define LV2_SYMBOL_EXTERN extern "C" +#else +# define LV2_SYMBOL_EXTERN +#endif + /** Put this (LV2_SYMBOL_EXPORT) before any functions that are to be loaded by the host as a symbol from the dynamic library. */ #ifdef _WIN32 -# define LV2_SYMBOL_EXPORT __declspec(dllexport) +# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __declspec(dllexport) #else -# define LV2_SYMBOL_EXPORT +# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __attribute__((visibility("default"))) #endif /** @@ -368,9 +379,9 @@ typedef struct _LV2_Descriptor { function to find the LV2_Descriptor for the desired plugin. Plugins are accessed by index using values from 0 upwards. This function MUST return NULL for out of range indices, so the host can enumerate plugins by - increasing @p index until NULL is returned. + increasing `index` until NULL is returned. - Note that @p index has no meaning, hosts MUST NOT depend on it remaining + Note that `index` has no meaning, hosts MUST NOT depend on it remaining consistent between loads of the plugin library. */ LV2_SYMBOL_EXPORT @@ -418,7 +429,7 @@ typedef struct { Plugins are accessed by index using values from 0 upwards. Out of range indices MUST result in this function returning NULL, so the host can - enumerate plugins by increasing @a index until NULL is returned. + enumerate plugins by increasing `index` until NULL is returned. */ const LV2_Descriptor * (*get_plugin)(LV2_Lib_Handle handle, uint32_t index); @@ -440,6 +451,7 @@ typedef struct { be destroyed (using LV2_Lib_Descriptor::cleanup()) until all plugins loaded from that library have been destroyed. */ +LV2_SYMBOL_EXPORT const LV2_Lib_Descriptor * lv2_lib_descriptor(const char * bundle_path, const LV2_Feature *const * features); @@ -456,3 +468,7 @@ typedef const LV2_Lib_Descriptor * #endif #endif /* LV2_H_INCLUDED */ + +/** + @} +*/ diff --git a/distrho/src/lv2/midi.h b/distrho/src/lv2/midi.h index 2c53c495..79e3a833 100644 --- a/distrho/src/lv2/midi.h +++ b/distrho/src/lv2/midi.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,8 +15,12 @@ */ /** - @file midi.h - C definitions for the LV2 MIDI extension . + @defgroup midi MIDI + + Definitions of standard MIDI messages, see + for details. + + @{ */ #ifndef LV2_MIDI_H @@ -30,50 +34,50 @@ extern "C" { # include #endif -#define LV2_MIDI_URI "http://lv2plug.in/ns/ext/midi" -#define LV2_MIDI_PREFIX LV2_MIDI_URI "#" - -#define LV2_MIDI__ActiveSense LV2_MIDI_PREFIX "ActiveSense" -#define LV2_MIDI__Aftertouch LV2_MIDI_PREFIX "Aftertouch" -#define LV2_MIDI__Bender LV2_MIDI_PREFIX "Bender" -#define LV2_MIDI__ChannelPressure LV2_MIDI_PREFIX "ChannelPressure" -#define LV2_MIDI__Chunk LV2_MIDI_PREFIX "Chunk" -#define LV2_MIDI__Clock LV2_MIDI_PREFIX "Clock" -#define LV2_MIDI__Continue LV2_MIDI_PREFIX "Continue" -#define LV2_MIDI__Controller LV2_MIDI_PREFIX "Controller" -#define LV2_MIDI__MidiEvent LV2_MIDI_PREFIX "MidiEvent" -#define LV2_MIDI__NoteOff LV2_MIDI_PREFIX "NoteOff" -#define LV2_MIDI__NoteOn LV2_MIDI_PREFIX "NoteOn" -#define LV2_MIDI__ProgramChange LV2_MIDI_PREFIX "ProgramChange" -#define LV2_MIDI__QuarterFrame LV2_MIDI_PREFIX "QuarterFrame" -#define LV2_MIDI__Reset LV2_MIDI_PREFIX "Reset" -#define LV2_MIDI__SongPosition LV2_MIDI_PREFIX "SongPosition" -#define LV2_MIDI__SongSelect LV2_MIDI_PREFIX "SongSelect" -#define LV2_MIDI__Start LV2_MIDI_PREFIX "Start" -#define LV2_MIDI__Stop LV2_MIDI_PREFIX "Stop" -#define LV2_MIDI__SystemCommon LV2_MIDI_PREFIX "SystemCommon" -#define LV2_MIDI__SystemExclusive LV2_MIDI_PREFIX "SystemExclusive" -#define LV2_MIDI__SystemMessage LV2_MIDI_PREFIX "SystemMessage" -#define LV2_MIDI__SystemRealtime LV2_MIDI_PREFIX "SystemRealtime" -#define LV2_MIDI__Tick LV2_MIDI_PREFIX "Tick" -#define LV2_MIDI__TuneRequest LV2_MIDI_PREFIX "TuneRequest" -#define LV2_MIDI__VoiceMessage LV2_MIDI_PREFIX "VoiceMessage" -#define LV2_MIDI__benderValue LV2_MIDI_PREFIX "benderValue" -#define LV2_MIDI__binding LV2_MIDI_PREFIX "binding" -#define LV2_MIDI__byteNumber LV2_MIDI_PREFIX "byteNumber" -#define LV2_MIDI__channel LV2_MIDI_PREFIX "channel" -#define LV2_MIDI__chunk LV2_MIDI_PREFIX "chunk" -#define LV2_MIDI__controllerNumber LV2_MIDI_PREFIX "controllerNumber" -#define LV2_MIDI__controllerValue LV2_MIDI_PREFIX "controllerValue" -#define LV2_MIDI__noteNumber LV2_MIDI_PREFIX "noteNumber" -#define LV2_MIDI__pressure LV2_MIDI_PREFIX "pressure" -#define LV2_MIDI__programNumber LV2_MIDI_PREFIX "programNumber" -#define LV2_MIDI__property LV2_MIDI_PREFIX "property" -#define LV2_MIDI__songNumber LV2_MIDI_PREFIX "songNumber" -#define LV2_MIDI__songPosition LV2_MIDI_PREFIX "songPosition" -#define LV2_MIDI__status LV2_MIDI_PREFIX "status" -#define LV2_MIDI__statusMask LV2_MIDI_PREFIX "statusMask" -#define LV2_MIDI__velocity LV2_MIDI_PREFIX "velocity" +#define LV2_MIDI_URI "http://lv2plug.in/ns/ext/midi" ///< http://lv2plug.in/ns/ext/midi +#define LV2_MIDI_PREFIX LV2_MIDI_URI "#" ///< http://lv2plug.in/ns/ext/midi# + +#define LV2_MIDI__ActiveSense LV2_MIDI_PREFIX "ActiveSense" ///< http://lv2plug.in/ns/ext/midi#ActiveSense +#define LV2_MIDI__Aftertouch LV2_MIDI_PREFIX "Aftertouch" ///< http://lv2plug.in/ns/ext/midi#Aftertouch +#define LV2_MIDI__Bender LV2_MIDI_PREFIX "Bender" ///< http://lv2plug.in/ns/ext/midi#Bender +#define LV2_MIDI__ChannelPressure LV2_MIDI_PREFIX "ChannelPressure" ///< http://lv2plug.in/ns/ext/midi#ChannelPressure +#define LV2_MIDI__Chunk LV2_MIDI_PREFIX "Chunk" ///< http://lv2plug.in/ns/ext/midi#Chunk +#define LV2_MIDI__Clock LV2_MIDI_PREFIX "Clock" ///< http://lv2plug.in/ns/ext/midi#Clock +#define LV2_MIDI__Continue LV2_MIDI_PREFIX "Continue" ///< http://lv2plug.in/ns/ext/midi#Continue +#define LV2_MIDI__Controller LV2_MIDI_PREFIX "Controller" ///< http://lv2plug.in/ns/ext/midi#Controller +#define LV2_MIDI__MidiEvent LV2_MIDI_PREFIX "MidiEvent" ///< http://lv2plug.in/ns/ext/midi#MidiEvent +#define LV2_MIDI__NoteOff LV2_MIDI_PREFIX "NoteOff" ///< http://lv2plug.in/ns/ext/midi#NoteOff +#define LV2_MIDI__NoteOn LV2_MIDI_PREFIX "NoteOn" ///< http://lv2plug.in/ns/ext/midi#NoteOn +#define LV2_MIDI__ProgramChange LV2_MIDI_PREFIX "ProgramChange" ///< http://lv2plug.in/ns/ext/midi#ProgramChange +#define LV2_MIDI__QuarterFrame LV2_MIDI_PREFIX "QuarterFrame" ///< http://lv2plug.in/ns/ext/midi#QuarterFrame +#define LV2_MIDI__Reset LV2_MIDI_PREFIX "Reset" ///< http://lv2plug.in/ns/ext/midi#Reset +#define LV2_MIDI__SongPosition LV2_MIDI_PREFIX "SongPosition" ///< http://lv2plug.in/ns/ext/midi#SongPosition +#define LV2_MIDI__SongSelect LV2_MIDI_PREFIX "SongSelect" ///< http://lv2plug.in/ns/ext/midi#SongSelect +#define LV2_MIDI__Start LV2_MIDI_PREFIX "Start" ///< http://lv2plug.in/ns/ext/midi#Start +#define LV2_MIDI__Stop LV2_MIDI_PREFIX "Stop" ///< http://lv2plug.in/ns/ext/midi#Stop +#define LV2_MIDI__SystemCommon LV2_MIDI_PREFIX "SystemCommon" ///< http://lv2plug.in/ns/ext/midi#SystemCommon +#define LV2_MIDI__SystemExclusive LV2_MIDI_PREFIX "SystemExclusive" ///< http://lv2plug.in/ns/ext/midi#SystemExclusive +#define LV2_MIDI__SystemMessage LV2_MIDI_PREFIX "SystemMessage" ///< http://lv2plug.in/ns/ext/midi#SystemMessage +#define LV2_MIDI__SystemRealtime LV2_MIDI_PREFIX "SystemRealtime" ///< http://lv2plug.in/ns/ext/midi#SystemRealtime +#define LV2_MIDI__Tick LV2_MIDI_PREFIX "Tick" ///< http://lv2plug.in/ns/ext/midi#Tick +#define LV2_MIDI__TuneRequest LV2_MIDI_PREFIX "TuneRequest" ///< http://lv2plug.in/ns/ext/midi#TuneRequest +#define LV2_MIDI__VoiceMessage LV2_MIDI_PREFIX "VoiceMessage" ///< http://lv2plug.in/ns/ext/midi#VoiceMessage +#define LV2_MIDI__benderValue LV2_MIDI_PREFIX "benderValue" ///< http://lv2plug.in/ns/ext/midi#benderValue +#define LV2_MIDI__binding LV2_MIDI_PREFIX "binding" ///< http://lv2plug.in/ns/ext/midi#binding +#define LV2_MIDI__byteNumber LV2_MIDI_PREFIX "byteNumber" ///< http://lv2plug.in/ns/ext/midi#byteNumber +#define LV2_MIDI__channel LV2_MIDI_PREFIX "channel" ///< http://lv2plug.in/ns/ext/midi#channel +#define LV2_MIDI__chunk LV2_MIDI_PREFIX "chunk" ///< http://lv2plug.in/ns/ext/midi#chunk +#define LV2_MIDI__controllerNumber LV2_MIDI_PREFIX "controllerNumber" ///< http://lv2plug.in/ns/ext/midi#controllerNumber +#define LV2_MIDI__controllerValue LV2_MIDI_PREFIX "controllerValue" ///< http://lv2plug.in/ns/ext/midi#controllerValue +#define LV2_MIDI__noteNumber LV2_MIDI_PREFIX "noteNumber" ///< http://lv2plug.in/ns/ext/midi#noteNumber +#define LV2_MIDI__pressure LV2_MIDI_PREFIX "pressure" ///< http://lv2plug.in/ns/ext/midi#pressure +#define LV2_MIDI__programNumber LV2_MIDI_PREFIX "programNumber" ///< http://lv2plug.in/ns/ext/midi#programNumber +#define LV2_MIDI__property LV2_MIDI_PREFIX "property" ///< http://lv2plug.in/ns/ext/midi#property +#define LV2_MIDI__songNumber LV2_MIDI_PREFIX "songNumber" ///< http://lv2plug.in/ns/ext/midi#songNumber +#define LV2_MIDI__songPosition LV2_MIDI_PREFIX "songPosition" ///< http://lv2plug.in/ns/ext/midi#songPosition +#define LV2_MIDI__status LV2_MIDI_PREFIX "status" ///< http://lv2plug.in/ns/ext/midi#status +#define LV2_MIDI__statusMask LV2_MIDI_PREFIX "statusMask" ///< http://lv2plug.in/ns/ext/midi#statusMask +#define LV2_MIDI__velocity LV2_MIDI_PREFIX "velocity" ///< http://lv2plug.in/ns/ext/midi#velocity /** MIDI Message Type. @@ -184,7 +188,7 @@ typedef enum { } LV2_Midi_Controller; /** - Return true iff @p msg is a MIDI voice message (which has a channel). + Return true iff `msg` is a MIDI voice message (which has a channel). */ static inline bool lv2_midi_is_voice_message(const uint8_t* msg) { @@ -192,7 +196,7 @@ lv2_midi_is_voice_message(const uint8_t* msg) { } /** - Return true iff @p msg is a MIDI system message (which has no channel). + Return true iff `msg` is a MIDI system message (which has no channel). */ static inline bool lv2_midi_is_system_message(const uint8_t* msg) { @@ -224,3 +228,7 @@ lv2_midi_message_type(const uint8_t* msg) { #endif #endif /* LV2_MIDI_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/morph.h b/distrho/src/lv2/morph.h index df888df0..884af741 100644 --- a/distrho/src/lv2/morph.h +++ b/distrho/src/lv2/morph.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -14,21 +14,29 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef LV2_MORPH_H -#define LV2_MORPH_H +/** + @defgroup morph Morph -#include + Ports that can dynamically change type, see + for details. + + @{ +*/ -#include "lv2.h" -#include "urid.h" +#ifndef LV2_MORPH_H +#define LV2_MORPH_H -#define LV2_MORPH_URI "http://lv2plug.in/ns/ext/morph" -#define LV2_MORPH_PREFIX LV2_MORPH_URI "#" +#define LV2_MORPH_URI "http://lv2plug.in/ns/ext/morph" ///< http://lv2plug.in/ns/ext/morph +#define LV2_MORPH_PREFIX LV2_MORPH_URI "#" ///< http://lv2plug.in/ns/ext/morph# -#define LV2_MORPH__AutoMorphPort LV2_MORPH_PREFIX "AutoMorphPort" -#define LV2_MORPH__MorphPort LV2_MORPH_PREFIX "MorphPort" -#define LV2_MORPH__interface LV2_MORPH_PREFIX "interface" -#define LV2_MORPH__supportsType LV2_MORPH_PREFIX "supportsType" -#define LV2_MORPH__currentType LV2_MORPH_PREFIX "currentType" +#define LV2_MORPH__AutoMorphPort LV2_MORPH_PREFIX "AutoMorphPort" ///< http://lv2plug.in/ns/ext/morph#AutoMorphPort +#define LV2_MORPH__MorphPort LV2_MORPH_PREFIX "MorphPort" ///< http://lv2plug.in/ns/ext/morph#MorphPort +#define LV2_MORPH__interface LV2_MORPH_PREFIX "interface" ///< http://lv2plug.in/ns/ext/morph#interface +#define LV2_MORPH__supportsType LV2_MORPH_PREFIX "supportsType" ///< http://lv2plug.in/ns/ext/morph#supportsType +#define LV2_MORPH__currentType LV2_MORPH_PREFIX "currentType" ///< http://lv2plug.in/ns/ext/morph#currentType #endif /* LV2_MORPH_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/options.h b/distrho/src/lv2/options.h index 5437db59..70fb5b7a 100644 --- a/distrho/src/lv2/options.h +++ b/distrho/src/lv2/options.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,15 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/** + @defgroup options Options + + Instantiation time options, see for + details. + + @{ +*/ + #ifndef LV2_OPTIONS_H #define LV2_OPTIONS_H @@ -22,14 +31,14 @@ #include "urid.h" #include "lv2.h" -#define LV2_OPTIONS_URI "http://lv2plug.in/ns/ext/options" -#define LV2_OPTIONS_PREFIX LV2_OPTIONS_URI "#" +#define LV2_OPTIONS_URI "http://lv2plug.in/ns/ext/options" ///< http://lv2plug.in/ns/ext/options +#define LV2_OPTIONS_PREFIX LV2_OPTIONS_URI "#" ///< http://lv2plug.in/ns/ext/options# -#define LV2_OPTIONS__Option LV2_OPTIONS_PREFIX "Option" -#define LV2_OPTIONS__interface LV2_OPTIONS_PREFIX "interface" -#define LV2_OPTIONS__options LV2_OPTIONS_PREFIX "options" -#define LV2_OPTIONS__requiredOption LV2_OPTIONS_PREFIX "requiredOption" -#define LV2_OPTIONS__supportedOption LV2_OPTIONS_PREFIX "supportedOption" +#define LV2_OPTIONS__Option LV2_OPTIONS_PREFIX "Option" ///< http://lv2plug.in/ns/ext/options#Option +#define LV2_OPTIONS__interface LV2_OPTIONS_PREFIX "interface" ///< http://lv2plug.in/ns/ext/options#interface +#define LV2_OPTIONS__options LV2_OPTIONS_PREFIX "options" ///< http://lv2plug.in/ns/ext/options#options +#define LV2_OPTIONS__requiredOption LV2_OPTIONS_PREFIX "requiredOption" ///< http://lv2plug.in/ns/ext/options#requiredOption +#define LV2_OPTIONS__supportedOption LV2_OPTIONS_PREFIX "supportedOption" ///< http://lv2plug.in/ns/ext/options#supportedOption #ifdef __cplusplus extern "C" { @@ -130,3 +139,7 @@ typedef struct _LV2_Options_Interface { #endif #endif /* LV2_OPTIONS_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/parameters.h b/distrho/src/lv2/parameters.h index 9c06babc..f81e78ba 100644 --- a/distrho/src/lv2/parameters.h +++ b/distrho/src/lv2/parameters.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -14,36 +14,49 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/** + @defgroup parameters Parameters + + Common parameters for audio processing, see + . + + @{ +*/ + #ifndef LV2_PARAMETERS_H #define LV2_PARAMETERS_H -#define LV2_PARAMETERS_URI "http://lv2plug.in/ns/ext/parameters" -#define LV2_PARAMETERS_PREFIX LV2_PARAMETERS_URI "#" - -#define LV2_PARAMETERS__CompressorControls LV2_PARAMETERS_PREFIX "CompressorControls" -#define LV2_PARAMETERS__ControlGroup LV2_PARAMETERS_PREFIX "ControlGroup" -#define LV2_PARAMETERS__EnvelopeControls LV2_PARAMETERS_PREFIX "EnvelopeControls" -#define LV2_PARAMETERS__FilterControls LV2_PARAMETERS_PREFIX "FilterControls" -#define LV2_PARAMETERS__OscillatorControls LV2_PARAMETERS_PREFIX "OscillatorControls" -#define LV2_PARAMETERS__amplitude LV2_PARAMETERS_PREFIX "amplitude" -#define LV2_PARAMETERS__attack LV2_PARAMETERS_PREFIX "attack" -#define LV2_PARAMETERS__bypass LV2_PARAMETERS_PREFIX "bypass" -#define LV2_PARAMETERS__cutoffFrequency LV2_PARAMETERS_PREFIX "cutoffFrequency" -#define LV2_PARAMETERS__decay LV2_PARAMETERS_PREFIX "decay" -#define LV2_PARAMETERS__delay LV2_PARAMETERS_PREFIX "delay" -#define LV2_PARAMETERS__dryLevel LV2_PARAMETERS_PREFIX "dryLevel" -#define LV2_PARAMETERS__frequency LV2_PARAMETERS_PREFIX "frequency" -#define LV2_PARAMETERS__gain LV2_PARAMETERS_PREFIX "gain" -#define LV2_PARAMETERS__hold LV2_PARAMETERS_PREFIX "hold" -#define LV2_PARAMETERS__pulseWidth LV2_PARAMETERS_PREFIX "pulseWidth" -#define LV2_PARAMETERS__ratio LV2_PARAMETERS_PREFIX "ratio" -#define LV2_PARAMETERS__release LV2_PARAMETERS_PREFIX "release" -#define LV2_PARAMETERS__resonance LV2_PARAMETERS_PREFIX "resonance" -#define LV2_PARAMETERS__sampleRate LV2_PARAMETERS_PREFIX "sampleRate" -#define LV2_PARAMETERS__sustain LV2_PARAMETERS_PREFIX "sustain" -#define LV2_PARAMETERS__threshold LV2_PARAMETERS_PREFIX "threshold" -#define LV2_PARAMETERS__waveform LV2_PARAMETERS_PREFIX "waveform" -#define LV2_PARAMETERS__wetDryRatio LV2_PARAMETERS_PREFIX "wetDryRatio" -#define LV2_PARAMETERS__wetLevel LV2_PARAMETERS_PREFIX "wetLevel" +#define LV2_PARAMETERS_URI "http://lv2plug.in/ns/ext/parameters" ///< http://lv2plug.in/ns/ext/parameters +#define LV2_PARAMETERS_PREFIX LV2_PARAMETERS_URI "#" ///< http://lv2plug.in/ns/ext/parameters# + +#define LV2_PARAMETERS__CompressorControls LV2_PARAMETERS_PREFIX "CompressorControls" ///< http://lv2plug.in/ns/ext/parameters#CompressorControls +#define LV2_PARAMETERS__ControlGroup LV2_PARAMETERS_PREFIX "ControlGroup" ///< http://lv2plug.in/ns/ext/parameters#ControlGroup +#define LV2_PARAMETERS__EnvelopeControls LV2_PARAMETERS_PREFIX "EnvelopeControls" ///< http://lv2plug.in/ns/ext/parameters#EnvelopeControls +#define LV2_PARAMETERS__FilterControls LV2_PARAMETERS_PREFIX "FilterControls" ///< http://lv2plug.in/ns/ext/parameters#FilterControls +#define LV2_PARAMETERS__OscillatorControls LV2_PARAMETERS_PREFIX "OscillatorControls" ///< http://lv2plug.in/ns/ext/parameters#OscillatorControls +#define LV2_PARAMETERS__amplitude LV2_PARAMETERS_PREFIX "amplitude" ///< http://lv2plug.in/ns/ext/parameters#amplitude +#define LV2_PARAMETERS__attack LV2_PARAMETERS_PREFIX "attack" ///< http://lv2plug.in/ns/ext/parameters#attack +#define LV2_PARAMETERS__bypass LV2_PARAMETERS_PREFIX "bypass" ///< http://lv2plug.in/ns/ext/parameters#bypass +#define LV2_PARAMETERS__cutoffFrequency LV2_PARAMETERS_PREFIX "cutoffFrequency" ///< http://lv2plug.in/ns/ext/parameters#cutoffFrequency +#define LV2_PARAMETERS__decay LV2_PARAMETERS_PREFIX "decay" ///< http://lv2plug.in/ns/ext/parameters#decay +#define LV2_PARAMETERS__delay LV2_PARAMETERS_PREFIX "delay" ///< http://lv2plug.in/ns/ext/parameters#delay +#define LV2_PARAMETERS__dryLevel LV2_PARAMETERS_PREFIX "dryLevel" ///< http://lv2plug.in/ns/ext/parameters#dryLevel +#define LV2_PARAMETERS__frequency LV2_PARAMETERS_PREFIX "frequency" ///< http://lv2plug.in/ns/ext/parameters#frequency +#define LV2_PARAMETERS__gain LV2_PARAMETERS_PREFIX "gain" ///< http://lv2plug.in/ns/ext/parameters#gain +#define LV2_PARAMETERS__hold LV2_PARAMETERS_PREFIX "hold" ///< http://lv2plug.in/ns/ext/parameters#hold +#define LV2_PARAMETERS__pulseWidth LV2_PARAMETERS_PREFIX "pulseWidth" ///< http://lv2plug.in/ns/ext/parameters#pulseWidth +#define LV2_PARAMETERS__ratio LV2_PARAMETERS_PREFIX "ratio" ///< http://lv2plug.in/ns/ext/parameters#ratio +#define LV2_PARAMETERS__release LV2_PARAMETERS_PREFIX "release" ///< http://lv2plug.in/ns/ext/parameters#release +#define LV2_PARAMETERS__resonance LV2_PARAMETERS_PREFIX "resonance" ///< http://lv2plug.in/ns/ext/parameters#resonance +#define LV2_PARAMETERS__sampleRate LV2_PARAMETERS_PREFIX "sampleRate" ///< http://lv2plug.in/ns/ext/parameters#sampleRate +#define LV2_PARAMETERS__sustain LV2_PARAMETERS_PREFIX "sustain" ///< http://lv2plug.in/ns/ext/parameters#sustain +#define LV2_PARAMETERS__threshold LV2_PARAMETERS_PREFIX "threshold" ///< http://lv2plug.in/ns/ext/parameters#threshold +#define LV2_PARAMETERS__waveform LV2_PARAMETERS_PREFIX "waveform" ///< http://lv2plug.in/ns/ext/parameters#waveform +#define LV2_PARAMETERS__wetDryRatio LV2_PARAMETERS_PREFIX "wetDryRatio" ///< http://lv2plug.in/ns/ext/parameters#wetDryRatio +#define LV2_PARAMETERS__wetLevel LV2_PARAMETERS_PREFIX "wetLevel" ///< http://lv2plug.in/ns/ext/parameters#wetLevel #endif /* LV2_PARAMETERS_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/patch.h b/distrho/src/lv2/patch.h index 32242646..e78164d3 100644 --- a/distrho/src/lv2/patch.h +++ b/distrho/src/lv2/patch.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,42 +15,52 @@ */ /** - @file patch.h C header for the LV2 Patch extension - . + @defgroup patch Patch - The patch extension is purely data, this header merely defines URIs - for convenience. + Messages for accessing and manipulating properties, see + for details. + + Note the patch extension is purely data, this header merely defines URIs for + convenience. + + @{ */ #ifndef LV2_PATCH_H #define LV2_PATCH_H -#define LV2_PATCH_URI "http://lv2plug.in/ns/ext/patch" -#define LV2_PATCH_PREFIX LV2_PATCH_URI "#" - -#define LV2_PATCH__Ack LV2_PATCH_PREFIX "Ack" -#define LV2_PATCH__Delete LV2_PATCH_PREFIX "Delete" -#define LV2_PATCH__Error LV2_PATCH_PREFIX "Error" -#define LV2_PATCH__Get LV2_PATCH_PREFIX "Get" -#define LV2_PATCH__Message LV2_PATCH_PREFIX "Message" -#define LV2_PATCH__Move LV2_PATCH_PREFIX "Move" -#define LV2_PATCH__Patch LV2_PATCH_PREFIX "Patch" -#define LV2_PATCH__Post LV2_PATCH_PREFIX "Post" -#define LV2_PATCH__Put LV2_PATCH_PREFIX "Put" -#define LV2_PATCH__Request LV2_PATCH_PREFIX "Request" -#define LV2_PATCH__Response LV2_PATCH_PREFIX "Response" -#define LV2_PATCH__Set LV2_PATCH_PREFIX "Set" -#define LV2_PATCH__add LV2_PATCH_PREFIX "add" -#define LV2_PATCH__body LV2_PATCH_PREFIX "body" -#define LV2_PATCH__destination LV2_PATCH_PREFIX "destination" -#define LV2_PATCH__property LV2_PATCH_PREFIX "property" -#define LV2_PATCH__readable LV2_PATCH_PREFIX "readable" -#define LV2_PATCH__remove LV2_PATCH_PREFIX "remove" -#define LV2_PATCH__request LV2_PATCH_PREFIX "request" -#define LV2_PATCH__subject LV2_PATCH_PREFIX "subject" -#define LV2_PATCH__sequenceNumber LV2_PATCH_PREFIX "sequenceNumber" -#define LV2_PATCH__value LV2_PATCH_PREFIX "value" -#define LV2_PATCH__wildcard LV2_PATCH_PREFIX "wildcard" -#define LV2_PATCH__writable LV2_PATCH_PREFIX "writable" +#define LV2_PATCH_URI "http://lv2plug.in/ns/ext/patch" ///< http://lv2plug.in/ns/ext/patch +#define LV2_PATCH_PREFIX LV2_PATCH_URI "#" ///< http://lv2plug.in/ns/ext/patch# + +#define LV2_PATCH__Ack LV2_PATCH_PREFIX "Ack" ///< http://lv2plug.in/ns/ext/patch#Ack +#define LV2_PATCH__Delete LV2_PATCH_PREFIX "Delete" ///< http://lv2plug.in/ns/ext/patch#Delete +#define LV2_PATCH__Copy LV2_PATCH_PREFIX "Copy" ///< http://lv2plug.in/ns/ext/patch#Copy +#define LV2_PATCH__Error LV2_PATCH_PREFIX "Error" ///< http://lv2plug.in/ns/ext/patch#Error +#define LV2_PATCH__Get LV2_PATCH_PREFIX "Get" ///< http://lv2plug.in/ns/ext/patch#Get +#define LV2_PATCH__Message LV2_PATCH_PREFIX "Message" ///< http://lv2plug.in/ns/ext/patch#Message +#define LV2_PATCH__Move LV2_PATCH_PREFIX "Move" ///< http://lv2plug.in/ns/ext/patch#Move +#define LV2_PATCH__Patch LV2_PATCH_PREFIX "Patch" ///< http://lv2plug.in/ns/ext/patch#Patch +#define LV2_PATCH__Post LV2_PATCH_PREFIX "Post" ///< http://lv2plug.in/ns/ext/patch#Post +#define LV2_PATCH__Put LV2_PATCH_PREFIX "Put" ///< http://lv2plug.in/ns/ext/patch#Put +#define LV2_PATCH__Request LV2_PATCH_PREFIX "Request" ///< http://lv2plug.in/ns/ext/patch#Request +#define LV2_PATCH__Response LV2_PATCH_PREFIX "Response" ///< http://lv2plug.in/ns/ext/patch#Response +#define LV2_PATCH__Set LV2_PATCH_PREFIX "Set" ///< http://lv2plug.in/ns/ext/patch#Set +#define LV2_PATCH__accept LV2_PATCH_PREFIX "accept" ///< http://lv2plug.in/ns/ext/patch#accept +#define LV2_PATCH__add LV2_PATCH_PREFIX "add" ///< http://lv2plug.in/ns/ext/patch#add +#define LV2_PATCH__body LV2_PATCH_PREFIX "body" ///< http://lv2plug.in/ns/ext/patch#body +#define LV2_PATCH__destination LV2_PATCH_PREFIX "destination" ///< http://lv2plug.in/ns/ext/patch#destination +#define LV2_PATCH__property LV2_PATCH_PREFIX "property" ///< http://lv2plug.in/ns/ext/patch#property +#define LV2_PATCH__readable LV2_PATCH_PREFIX "readable" ///< http://lv2plug.in/ns/ext/patch#readable +#define LV2_PATCH__remove LV2_PATCH_PREFIX "remove" ///< http://lv2plug.in/ns/ext/patch#remove +#define LV2_PATCH__request LV2_PATCH_PREFIX "request" ///< http://lv2plug.in/ns/ext/patch#request +#define LV2_PATCH__subject LV2_PATCH_PREFIX "subject" ///< http://lv2plug.in/ns/ext/patch#subject +#define LV2_PATCH__sequenceNumber LV2_PATCH_PREFIX "sequenceNumber" ///< http://lv2plug.in/ns/ext/patch#sequenceNumber +#define LV2_PATCH__value LV2_PATCH_PREFIX "value" ///< http://lv2plug.in/ns/ext/patch#value +#define LV2_PATCH__wildcard LV2_PATCH_PREFIX "wildcard" ///< http://lv2plug.in/ns/ext/patch#wildcard +#define LV2_PATCH__writable LV2_PATCH_PREFIX "writable" ///< http://lv2plug.in/ns/ext/patch#writable #endif /* LV2_PATCH_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/port-groups.h b/distrho/src/lv2/port-groups.h index 4dd8cf4e..a1bcd122 100644 --- a/distrho/src/lv2/port-groups.h +++ b/distrho/src/lv2/port-groups.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,50 +15,57 @@ */ /** - @file port-groups.h - C definitions for the LV2 Port Groups extension - . + @defgroup port-groups Port Groups + + Multi-channel groups of LV2 ports, see + for details. + + @{ */ #ifndef LV2_PORT_GROUPS_H #define LV2_PORT_GROUPS_H -#define LV2_PORT_GROUPS_URI "http://lv2plug.in/ns/ext/port-groups" -#define LV2_PORT_GROUPS_PREFIX LV2_PORT_GROUPS_URI "#" +#define LV2_PORT_GROUPS_URI "http://lv2plug.in/ns/ext/port-groups" ///< http://lv2plug.in/ns/ext/port-groups +#define LV2_PORT_GROUPS_PREFIX LV2_PORT_GROUPS_URI "#" ///< http://lv2plug.in/ns/ext/port-groups# -#define LV2_PORT_GROUPS__DiscreteGroup LV2_PORT_GROUPS_PREFIX "DiscreteGroup" -#define LV2_PORT_GROUPS__Element LV2_PORT_GROUPS_PREFIX "Element" -#define LV2_PORT_GROUPS__FivePointOneGroup LV2_PORT_GROUPS_PREFIX "FivePointOneGroup" -#define LV2_PORT_GROUPS__FivePointZeroGroup LV2_PORT_GROUPS_PREFIX "FivePointZeroGroup" -#define LV2_PORT_GROUPS__FourPointZeroGroup LV2_PORT_GROUPS_PREFIX "FourPointZeroGroup" -#define LV2_PORT_GROUPS__Group LV2_PORT_GROUPS_PREFIX "Group" -#define LV2_PORT_GROUPS__InputGroup LV2_PORT_GROUPS_PREFIX "InputGroup" -#define LV2_PORT_GROUPS__MidSideGroup LV2_PORT_GROUPS_PREFIX "MidSideGroup" -#define LV2_PORT_GROUPS__MonoGroup LV2_PORT_GROUPS_PREFIX "MonoGroup" -#define LV2_PORT_GROUPS__OutputGroup LV2_PORT_GROUPS_PREFIX "OutputGroup" -#define LV2_PORT_GROUPS__SevenPointOneGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneGroup" -#define LV2_PORT_GROUPS__SevenPointOneWideGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneWideGroup" -#define LV2_PORT_GROUPS__SixPointOneGroup LV2_PORT_GROUPS_PREFIX "SixPointOneGroup" -#define LV2_PORT_GROUPS__StereoGroup LV2_PORT_GROUPS_PREFIX "StereoGroup" -#define LV2_PORT_GROUPS__ThreePointZeroGroup LV2_PORT_GROUPS_PREFIX "ThreePointZeroGroup" -#define LV2_PORT_GROUPS__center LV2_PORT_GROUPS_PREFIX "center" -#define LV2_PORT_GROUPS__centerLeft LV2_PORT_GROUPS_PREFIX "centerLeft" -#define LV2_PORT_GROUPS__centerRight LV2_PORT_GROUPS_PREFIX "centerRight" -#define LV2_PORT_GROUPS__element LV2_PORT_GROUPS_PREFIX "element" -#define LV2_PORT_GROUPS__group LV2_PORT_GROUPS_PREFIX "group" -#define LV2_PORT_GROUPS__left LV2_PORT_GROUPS_PREFIX "left" -#define LV2_PORT_GROUPS__lowFrequencyEffects LV2_PORT_GROUPS_PREFIX "lowFrequencyEffects" -#define LV2_PORT_GROUPS__mainInput LV2_PORT_GROUPS_PREFIX "mainInput" -#define LV2_PORT_GROUPS__mainOutput LV2_PORT_GROUPS_PREFIX "mainOutput" -#define LV2_PORT_GROUPS__rearCenter LV2_PORT_GROUPS_PREFIX "rearCenter" -#define LV2_PORT_GROUPS__rearLeft LV2_PORT_GROUPS_PREFIX "rearLeft" -#define LV2_PORT_GROUPS__rearRight LV2_PORT_GROUPS_PREFIX "rearRight" -#define LV2_PORT_GROUPS__right LV2_PORT_GROUPS_PREFIX "right" -#define LV2_PORT_GROUPS__side LV2_PORT_GROUPS_PREFIX "side" -#define LV2_PORT_GROUPS__sideChainOf LV2_PORT_GROUPS_PREFIX "sideChainOf" -#define LV2_PORT_GROUPS__sideLeft LV2_PORT_GROUPS_PREFIX "sideLeft" -#define LV2_PORT_GROUPS__sideRight LV2_PORT_GROUPS_PREFIX "sideRight" -#define LV2_PORT_GROUPS__source LV2_PORT_GROUPS_PREFIX "source" -#define LV2_PORT_GROUPS__subGroupOf LV2_PORT_GROUPS_PREFIX "subGroupOf" +#define LV2_PORT_GROUPS__DiscreteGroup LV2_PORT_GROUPS_PREFIX "DiscreteGroup" ///< http://lv2plug.in/ns/ext/port-groups#DiscreteGroup +#define LV2_PORT_GROUPS__Element LV2_PORT_GROUPS_PREFIX "Element" ///< http://lv2plug.in/ns/ext/port-groups#Element +#define LV2_PORT_GROUPS__FivePointOneGroup LV2_PORT_GROUPS_PREFIX "FivePointOneGroup" ///< http://lv2plug.in/ns/ext/port-groups#FivePointOneGroup +#define LV2_PORT_GROUPS__FivePointZeroGroup LV2_PORT_GROUPS_PREFIX "FivePointZeroGroup" ///< http://lv2plug.in/ns/ext/port-groups#FivePointZeroGroup +#define LV2_PORT_GROUPS__FourPointZeroGroup LV2_PORT_GROUPS_PREFIX "FourPointZeroGroup" ///< http://lv2plug.in/ns/ext/port-groups#FourPointZeroGroup +#define LV2_PORT_GROUPS__Group LV2_PORT_GROUPS_PREFIX "Group" ///< http://lv2plug.in/ns/ext/port-groups#Group +#define LV2_PORT_GROUPS__InputGroup LV2_PORT_GROUPS_PREFIX "InputGroup" ///< http://lv2plug.in/ns/ext/port-groups#InputGroup +#define LV2_PORT_GROUPS__MidSideGroup LV2_PORT_GROUPS_PREFIX "MidSideGroup" ///< http://lv2plug.in/ns/ext/port-groups#MidSideGroup +#define LV2_PORT_GROUPS__MonoGroup LV2_PORT_GROUPS_PREFIX "MonoGroup" ///< http://lv2plug.in/ns/ext/port-groups#MonoGroup +#define LV2_PORT_GROUPS__OutputGroup LV2_PORT_GROUPS_PREFIX "OutputGroup" ///< http://lv2plug.in/ns/ext/port-groups#OutputGroup +#define LV2_PORT_GROUPS__SevenPointOneGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneGroup" ///< http://lv2plug.in/ns/ext/port-groups#SevenPointOneGroup +#define LV2_PORT_GROUPS__SevenPointOneWideGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneWideGroup" ///< http://lv2plug.in/ns/ext/port-groups#SevenPointOneWideGroup +#define LV2_PORT_GROUPS__SixPointOneGroup LV2_PORT_GROUPS_PREFIX "SixPointOneGroup" ///< http://lv2plug.in/ns/ext/port-groups#SixPointOneGroup +#define LV2_PORT_GROUPS__StereoGroup LV2_PORT_GROUPS_PREFIX "StereoGroup" ///< http://lv2plug.in/ns/ext/port-groups#StereoGroup +#define LV2_PORT_GROUPS__ThreePointZeroGroup LV2_PORT_GROUPS_PREFIX "ThreePointZeroGroup" ///< http://lv2plug.in/ns/ext/port-groups#ThreePointZeroGroup +#define LV2_PORT_GROUPS__center LV2_PORT_GROUPS_PREFIX "center" ///< http://lv2plug.in/ns/ext/port-groups#center +#define LV2_PORT_GROUPS__centerLeft LV2_PORT_GROUPS_PREFIX "centerLeft" ///< http://lv2plug.in/ns/ext/port-groups#centerLeft +#define LV2_PORT_GROUPS__centerRight LV2_PORT_GROUPS_PREFIX "centerRight" ///< http://lv2plug.in/ns/ext/port-groups#centerRight +#define LV2_PORT_GROUPS__element LV2_PORT_GROUPS_PREFIX "element" ///< http://lv2plug.in/ns/ext/port-groups#element +#define LV2_PORT_GROUPS__group LV2_PORT_GROUPS_PREFIX "group" ///< http://lv2plug.in/ns/ext/port-groups#group +#define LV2_PORT_GROUPS__left LV2_PORT_GROUPS_PREFIX "left" ///< http://lv2plug.in/ns/ext/port-groups#left +#define LV2_PORT_GROUPS__lowFrequencyEffects LV2_PORT_GROUPS_PREFIX "lowFrequencyEffects" ///< http://lv2plug.in/ns/ext/port-groups#lowFrequencyEffects +#define LV2_PORT_GROUPS__mainInput LV2_PORT_GROUPS_PREFIX "mainInput" ///< http://lv2plug.in/ns/ext/port-groups#mainInput +#define LV2_PORT_GROUPS__mainOutput LV2_PORT_GROUPS_PREFIX "mainOutput" ///< http://lv2plug.in/ns/ext/port-groups#mainOutput +#define LV2_PORT_GROUPS__rearCenter LV2_PORT_GROUPS_PREFIX "rearCenter" ///< http://lv2plug.in/ns/ext/port-groups#rearCenter +#define LV2_PORT_GROUPS__rearLeft LV2_PORT_GROUPS_PREFIX "rearLeft" ///< http://lv2plug.in/ns/ext/port-groups#rearLeft +#define LV2_PORT_GROUPS__rearRight LV2_PORT_GROUPS_PREFIX "rearRight" ///< http://lv2plug.in/ns/ext/port-groups#rearRight +#define LV2_PORT_GROUPS__right LV2_PORT_GROUPS_PREFIX "right" ///< http://lv2plug.in/ns/ext/port-groups#right +#define LV2_PORT_GROUPS__side LV2_PORT_GROUPS_PREFIX "side" ///< http://lv2plug.in/ns/ext/port-groups#side +#define LV2_PORT_GROUPS__sideChainOf LV2_PORT_GROUPS_PREFIX "sideChainOf" ///< http://lv2plug.in/ns/ext/port-groups#sideChainOf +#define LV2_PORT_GROUPS__sideLeft LV2_PORT_GROUPS_PREFIX "sideLeft" ///< http://lv2plug.in/ns/ext/port-groups#sideLeft +#define LV2_PORT_GROUPS__sideRight LV2_PORT_GROUPS_PREFIX "sideRight" ///< http://lv2plug.in/ns/ext/port-groups#sideRight +#define LV2_PORT_GROUPS__source LV2_PORT_GROUPS_PREFIX "source" ///< http://lv2plug.in/ns/ext/port-groups#source +#define LV2_PORT_GROUPS__subGroupOf LV2_PORT_GROUPS_PREFIX "subGroupOf" ///< http://lv2plug.in/ns/ext/port-groups#subGroupOf #endif /* LV2_PORT_GROUPS_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/port-props.h b/distrho/src/lv2/port-props.h index 11274cc4..ef2b64d3 100644 --- a/distrho/src/lv2/port-props.h +++ b/distrho/src/lv2/port-props.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,28 +15,34 @@ */ /** - @file port-props.h - C definitions for the LV2 Port Props extension - . + @defgroup port-props Port Properties + + Various port properties. + + @{ */ #ifndef LV2_PORT_PROPS_H #define LV2_PORT_PROPS_H -#define LV2_PORT_PROPS_URI "http://lv2plug.in/ns/ext/port-props" -#define LV2_PORT_PROPS_PREFIX LV2_PORT_PROPS_URI "#" - -#define LV2_PORT_PROPS__causesArtifacts LV2_PORT_PROPS_PREFIX "causesArtifacts" -#define LV2_PORT_PROPS__continuousCV LV2_PORT_PROPS_PREFIX "continuousCV" -#define LV2_PORT_PROPS__discreteCV LV2_PORT_PROPS_PREFIX "discreteCV" -#define LV2_PORT_PROPS__displayPriority LV2_PORT_PROPS_PREFIX "displayPriority" -#define LV2_PORT_PROPS__expensive LV2_PORT_PROPS_PREFIX "expensive" -#define LV2_PORT_PROPS__hasStrictBounds LV2_PORT_PROPS_PREFIX "hasStrictBounds" -#define LV2_PORT_PROPS__logarithmic LV2_PORT_PROPS_PREFIX "logarithmic" -#define LV2_PORT_PROPS__notAutomatic LV2_PORT_PROPS_PREFIX "notAutomatic" -#define LV2_PORT_PROPS__notOnGUI LV2_PORT_PROPS_PREFIX "notOnGUI" -#define LV2_PORT_PROPS__rangeSteps LV2_PORT_PROPS_PREFIX "rangeSteps" -#define LV2_PORT_PROPS__supportsStrictBounds LV2_PORT_PROPS_PREFIX "supportsStrictBounds" -#define LV2_PORT_PROPS__trigger LV2_PORT_PROPS_PREFIX "trigger" +#define LV2_PORT_PROPS_URI "http://lv2plug.in/ns/ext/port-props" ///< http://lv2plug.in/ns/ext/port-props +#define LV2_PORT_PROPS_PREFIX LV2_PORT_PROPS_URI "#" ///< http://lv2plug.in/ns/ext/port-props# + +#define LV2_PORT_PROPS__causesArtifacts LV2_PORT_PROPS_PREFIX "causesArtifacts" ///< http://lv2plug.in/ns/ext/port-props#causesArtifacts +#define LV2_PORT_PROPS__continuousCV LV2_PORT_PROPS_PREFIX "continuousCV" ///< http://lv2plug.in/ns/ext/port-props#continuousCV +#define LV2_PORT_PROPS__discreteCV LV2_PORT_PROPS_PREFIX "discreteCV" ///< http://lv2plug.in/ns/ext/port-props#discreteCV +#define LV2_PORT_PROPS__displayPriority LV2_PORT_PROPS_PREFIX "displayPriority" ///< http://lv2plug.in/ns/ext/port-props#displayPriority +#define LV2_PORT_PROPS__expensive LV2_PORT_PROPS_PREFIX "expensive" ///< http://lv2plug.in/ns/ext/port-props#expensive +#define LV2_PORT_PROPS__hasStrictBounds LV2_PORT_PROPS_PREFIX "hasStrictBounds" ///< http://lv2plug.in/ns/ext/port-props#hasStrictBounds +#define LV2_PORT_PROPS__logarithmic LV2_PORT_PROPS_PREFIX "logarithmic" ///< http://lv2plug.in/ns/ext/port-props#logarithmic +#define LV2_PORT_PROPS__notAutomatic LV2_PORT_PROPS_PREFIX "notAutomatic" ///< http://lv2plug.in/ns/ext/port-props#notAutomatic +#define LV2_PORT_PROPS__notOnGUI LV2_PORT_PROPS_PREFIX "notOnGUI" ///< http://lv2plug.in/ns/ext/port-props#notOnGUI +#define LV2_PORT_PROPS__rangeSteps LV2_PORT_PROPS_PREFIX "rangeSteps" ///< http://lv2plug.in/ns/ext/port-props#rangeSteps +#define LV2_PORT_PROPS__supportsStrictBounds LV2_PORT_PROPS_PREFIX "supportsStrictBounds" ///< http://lv2plug.in/ns/ext/port-props#supportsStrictBounds +#define LV2_PORT_PROPS__trigger LV2_PORT_PROPS_PREFIX "trigger" ///< http://lv2plug.in/ns/ext/port-props#trigger #endif /* LV2_PORT_PROPS_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/presets.h b/distrho/src/lv2/presets.h index 4851febe..7c51b1c6 100644 --- a/distrho/src/lv2/presets.h +++ b/distrho/src/lv2/presets.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,20 +15,27 @@ */ /** - @file presets.h + @defgroup presets Presets - C definitions for the LV2 Presets extension - . + Presets for plugins, see for details. + + @{ */ #ifndef LV2_PRESETS_H #define LV2_PRESETS_H -#define LV2_PRESETS_URI "http://lv2plug.in/ns/ext/presets" -#define LV2_PRESETS_PREFIX LV2_PRESETS_URI "#" +#define LV2_PRESETS_URI "http://lv2plug.in/ns/ext/presets" ///< http://lv2plug.in/ns/ext/presets +#define LV2_PRESETS_PREFIX LV2_PRESETS_URI "#" ///< http://lv2plug.in/ns/ext/presets# -#define LV2_PRESETS__Preset LV2_PRESETS_PREFIX "Preset" -#define LV2_PRESETS__preset LV2_PRESETS_PREFIX "preset" -#define LV2_PRESETS__value LV2_PRESETS_PREFIX "value" +#define LV2_PRESETS__Bank LV2_PRESETS_PREFIX "Bank" ///< http://lv2plug.in/ns/ext/presets#Bank +#define LV2_PRESETS__Preset LV2_PRESETS_PREFIX "Preset" ///< http://lv2plug.in/ns/ext/presets#Preset +#define LV2_PRESETS__bank LV2_PRESETS_PREFIX "bank" ///< http://lv2plug.in/ns/ext/presets#bank +#define LV2_PRESETS__preset LV2_PRESETS_PREFIX "preset" ///< http://lv2plug.in/ns/ext/presets#preset +#define LV2_PRESETS__value LV2_PRESETS_PREFIX "value" ///< http://lv2plug.in/ns/ext/presets#value #endif /* LV2_PRESETS_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/resize-port.h b/distrho/src/lv2/resize-port.h index fa3abd7d..5eb47f23 100644 --- a/distrho/src/lv2/resize-port.h +++ b/distrho/src/lv2/resize-port.h @@ -1,5 +1,5 @@ /* - Copyright 2007-2012 David Robillard + Copyright 2007-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -14,18 +14,26 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/** + @defgroup resize-port Resize Port + + Dynamically sized LV2 port buffers. + + @{ +*/ + #ifndef LV2_RESIZE_PORT_H #define LV2_RESIZE_PORT_H #include #include -#define LV2_RESIZE_PORT_URI "http://lv2plug.in/ns/ext/resize-port" -#define LV2_RESIZE_PORT_PREFIX LV2_RESIZE_PORT_URI "#" +#define LV2_RESIZE_PORT_URI "http://lv2plug.in/ns/ext/resize-port" ///< http://lv2plug.in/ns/ext/resize-port +#define LV2_RESIZE_PORT_PREFIX LV2_RESIZE_PORT_URI "#" ///< http://lv2plug.in/ns/ext/resize-port# -#define LV2_RESIZE_PORT__asLargeAs LV2_RESIZE_PORT_PREFIX "asLargeAs" -#define LV2_RESIZE_PORT__minimumSize LV2_RESIZE_PORT_PREFIX "minimumSize" -#define LV2_RESIZE_PORT__resize LV2_RESIZE_PORT_PREFIX "resize" +#define LV2_RESIZE_PORT__asLargeAs LV2_RESIZE_PORT_PREFIX "asLargeAs" ///< http://lv2plug.in/ns/ext/port#asLargeAs +#define LV2_RESIZE_PORT__minimumSize LV2_RESIZE_PORT_PREFIX "minimumSize" ///< http://lv2plug.in/ns/ext/port#minimumSize +#define LV2_RESIZE_PORT__resize LV2_RESIZE_PORT_PREFIX "resize" ///< http://lv2plug.in/ns/ext/port#resize #ifdef __cplusplus extern "C" { @@ -40,22 +48,25 @@ typedef enum { LV2_RESIZE_PORT_ERR_NO_SPACE = 2 /**< Insufficient space. */ } LV2_Resize_Port_Status; +/** Opaque data for resize method. */ typedef void* LV2_Resize_Port_Feature_Data; +/** Host feature to allow plugins to resize their port buffers. */ typedef struct { + /** Opaque data for resize method. */ LV2_Resize_Port_Feature_Data data; /** - Resize a port buffer to at least @a size bytes. - + Resize a port buffer to at least `size` bytes. + This function MAY return an error, in which case the port buffer was not resized and the port is still connected to the same location. Plugins MUST gracefully handle this situation. - + This function is in the audio threading class. - + The host MUST preserve the contents of the port buffer when resizing. - + Plugins MAY resize a port many times in a single run callback. Hosts SHOULD make this as inexpensive as possible. */ @@ -70,3 +81,6 @@ typedef struct { #endif /* LV2_RESIZE_PORT_H */ +/** + @} +*/ diff --git a/distrho/src/lv2/state.h b/distrho/src/lv2/state.h index 7e162cb4..fe891419 100644 --- a/distrho/src/lv2/state.h +++ b/distrho/src/lv2/state.h @@ -1,5 +1,5 @@ /* - Copyright 2010-2012 David Robillard + Copyright 2010-2016 David Robillard Copyright 2010 Leonard Ritter Permission to use, copy, modify, and/or distribute this software for any @@ -16,37 +16,45 @@ */ /** - @file state.h - C API for the LV2 State extension . + @defgroup state State + @ingroup lv2 + + An interface for LV2 plugins to save and restore state, see + for details. + + @{ */ #ifndef LV2_STATE_H #define LV2_STATE_H +#include "lv2.h" + +#include #include #include -#include "lv2.h" - -#define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" -#define LV2_STATE_PREFIX LV2_STATE_URI "#" +#define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" ///< http://lv2plug.in/ns/ext/state +#define LV2_STATE_PREFIX LV2_STATE_URI "#" ///< http://lv2plug.in/ns/ext/state# -#define LV2_STATE__State LV2_STATE_PREFIX "State" -#define LV2_STATE__interface LV2_STATE_PREFIX "interface" -#define LV2_STATE__loadDefaultState LV2_STATE_PREFIX "loadDefaultState" -#define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" -#define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" -#define LV2_STATE__state LV2_STATE_PREFIX "state" +#define LV2_STATE__State LV2_STATE_PREFIX "State" ///< http://lv2plug.in/ns/ext/state#State +#define LV2_STATE__interface LV2_STATE_PREFIX "interface" ///< http://lv2plug.in/ns/ext/state#interface +#define LV2_STATE__loadDefaultState LV2_STATE_PREFIX "loadDefaultState" ///< http://lv2plug.in/ns/ext/state#loadDefaultState +#define LV2_STATE__freePath LV2_STATE_PREFIX "freePath" ///< http://lv2plug.in/ns/ext/state#freePath +#define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" ///< http://lv2plug.in/ns/ext/state#makePath +#define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" ///< http://lv2plug.in/ns/ext/state#mapPath +#define LV2_STATE__state LV2_STATE_PREFIX "state" ///< http://lv2plug.in/ns/ext/state#state +#define LV2_STATE__threadSafeRestore LV2_STATE_PREFIX "threadSafeRestore" ///< http://lv2plug.in/ns/ext/state#threadSafeRestore +#define LV2_STATE__StateChanged LV2_STATE_PREFIX "StateChanged" ///< http://lv2plug.in/ns/ext/state#StateChanged #ifdef __cplusplus extern "C" { -#else -# include #endif -typedef void* LV2_State_Handle; -typedef void* LV2_State_Map_Path_Handle; -typedef void* LV2_State_Make_Path_Handle; +typedef void* LV2_State_Handle; ///< Opaque handle for state save/restore +typedef void* LV2_State_Free_Path_Handle; ///< Opaque handle for state:freePath feature +typedef void* LV2_State_Map_Path_Handle; ///< Opaque handle for state:mapPath feature +typedef void* LV2_State_Make_Path_Handle; ///< Opaque handle for state:makePath feature /** Flags describing value characteristics. @@ -61,8 +69,8 @@ typedef enum { Values with this flag contain no pointers or references to other areas of memory. It is safe to copy POD values with a simple memcpy and store them for the duration of the process. A POD value is not necessarily - safe to trasmit between processes or machines (e.g. filenames are POD), - see LV2_STATE_IS_PORTABLE for details. + safe to trasmit between processes or machines (for example, filenames + are POD), see LV2_STATE_IS_PORTABLE for details. Implementations MUST NOT attempt to copy or serialise a non-POD value if they do not understand its type (and thus know how to correctly do so). @@ -84,9 +92,9 @@ typedef enum { Native data. This flag is used by the host to indicate that the saved data is only - going to be used locally in the currently running process (e.g. for - instance duplication or snapshots), so the plugin should use the most - efficient representation possible and not worry about serialisation + going to be used locally in the currently running process (for things + like instance duplication or snapshots), so the plugin should use the + most efficient representation possible and not worry about serialisation and portability. */ LV2_STATE_IS_NATIVE = 1 << 2 @@ -106,11 +114,11 @@ typedef enum { /** A host-provided function to store a property. @param handle Must be the handle passed to LV2_State_Interface.save(). - @param key The key to store @p value under (URID). + @param key The key to store `value` under (URID). @param value Pointer to the value to be stored. - @param size The size of @p value in bytes. - @param type The type of @p value (URID). - @param flags LV2_State_Flags for @p value. + @param size The size of `value` in bytes. + @param type The type of `value` (URID). + @param flags LV2_State_Flags for `value`. @return 0 on success, otherwise a non-zero error code. The host passes a callback of this type to LV2_State_Interface.save(). This @@ -120,7 +128,7 @@ typedef enum { DO NOT INVENT NONSENSE URI SCHEMES FOR THE KEY. Best is to use keys from existing vocabularies. If nothing appropriate is available, use http URIs that point to somewhere you can host documents so documentation can be made - resolvable (e.g. a child of the plugin or project URI). If this is not + resolvable (typically a child of the plugin or project URI). If this is not possible, invent a URN scheme, e.g. urn:myproj:whatever. The plugin MUST NOT pass an invalid URI key. @@ -130,8 +138,8 @@ typedef enum { (http://lv2plug.in/ns/ext/atom) wherever possible. The plugin SHOULD attempt to fall-back and avoid the error if possible. - Note that @p size MUST be > 0, and @p value MUST point to a valid region of - memory @p size bytes long (this is required to make restore unambiguous). + Note that `size` MUST be > 0, and `value` MUST point to a valid region of + memory `size` bytes long (this is required to make restore unambiguous). The plugin MUST NOT attempt to use this function outside of the LV2_State_Interface.restore() context. @@ -152,7 +160,7 @@ typedef LV2_State_Status (*LV2_State_Store_Function)( @param type (Output) If non-NULL, set to the type of the restored value. @param flags (Output) If non-NULL, set to the flags for the restored value. @return A pointer to the restored value (object), or NULL if no value - has been stored under @p key. + has been stored under `key`. A callback of this type is passed by the host to LV2_State_Interface.restore(). This callback is called repeatedly by the @@ -187,14 +195,14 @@ typedef const void* (*LV2_State_Retrieve_Function)( authors should consider this possibility, and always store sensible data with meaningful types to avoid such problems in the future. */ -typedef struct _LV2_State_Interface { +typedef struct { /** - Save plugin state using a host-provided @p store callback. + Save plugin state using a host-provided `store` callback. @param instance The instance handle of the plugin. @param store The host-provided store callback. @param handle An opaque pointer to host data which MUST be passed as the - handle parameter to @p store if it is called. + handle parameter to `store` if it is called. @param flags Flags describing desired properties of this save. These flags may be used to determine the most appropriate values to store. @param features Extensible parameter for passing any additional @@ -205,16 +213,16 @@ typedef struct _LV2_State_Interface { possible, and consider the possibility of state being restored much later on a different machine. - The @p handle pointer and @p store function MUST NOT be used + The `handle` pointer and `store` function MUST NOT be used beyond the scope of save(). This function has its own special threading class: it may not be called concurrently with any "Instantiation" function, but it may be called concurrently with functions in any other class, unless the definition of - that class prohibits it (e.g. it may not be called concurrently with a - "Discovery" function, but it may be called concurrently with an "Audio" - function. The plugin is responsible for any locking or lock-free - techniques necessary to make this possible. + that class prohibits it (for example, it may not be called concurrently + with a "Discovery" function, but it may be called concurrently with an + "Audio" function. The plugin is responsible for any locking or + lock-free techniques necessary to make this possible. Note that in the simple case where state is only modified by restore(), there are no synchronization issues since save() is never called @@ -231,12 +239,12 @@ typedef struct _LV2_State_Interface { const LV2_Feature *const * features); /** - Restore plugin state using a host-provided @p retrieve callback. + Restore plugin state using a host-provided `retrieve` callback. @param instance The instance handle of the plugin. @param retrieve The host-provided retrieve callback. @param handle An opaque pointer to host data which MUST be passed as the - handle parameter to @p retrieve if it is called. + handle parameter to `retrieve` if it is called. @param flags Currently unused. @param features Extensible parameter for passing any additional features to be used for this restore. @@ -248,7 +256,7 @@ typedef struct _LV2_State_Interface { not be retrieved. This allows the host to reset the plugin state with an empty map. - The @p handle pointer and @p store function MUST NOT be used + The `handle` pointer and `store` function MUST NOT be used beyond the scope of restore(). This function is in the "Instantiation" threading class as defined by @@ -263,7 +271,7 @@ typedef struct _LV2_State_Interface { } LV2_State_Interface; /** - Feature data for state:mapPath (LV2_STATE__mapPath). + Feature data for state:mapPath (@ref LV2_STATE__mapPath). */ typedef struct { /** @@ -273,38 +281,38 @@ typedef struct { /** Map an absolute path to an abstract path for use in plugin state. - @param handle MUST be the @p handle member of this struct. + @param handle MUST be the `handle` member of this struct. @param absolute_path The absolute path of a file. @return An abstract path suitable for use in plugin state. The plugin MUST use this function to map any paths that will be stored in plugin state. The returned value is an abstract path which MAY not - be an actual file system path; @ref absolute_path() MUST be used to map + be an actual file system path; absolute_path() MUST be used to map it to an actual path in order to use the file. Plugins MUST NOT make any assumptions about abstract paths except that they can be mapped back to the absolute path of the "same" file (though - not necessarily the same original path) using @ref absolute_path(). + not necessarily the same original path) using absolute_path(). This function may only be called within the context of - LV2_State_Interface methods. The caller is responsible for freeing the - returned value with free(). + LV2_State_Interface methods. The caller must free the returned value + with LV2_State_Free_Path.free_path(). */ char* (*abstract_path)(LV2_State_Map_Path_Handle handle, const char* absolute_path); /** Map an abstract path from plugin state to an absolute path. - @param handle MUST be the @p handle member of this struct. - @param abstract_path An abstract path (e.g. a path from plugin state). + @param handle MUST be the `handle` member of this struct. + @param abstract_path An abstract path (typically from plugin state). @return An absolute file system path. The plugin MUST use this function in order to actually open or otherwise use any paths loaded from plugin state. This function may only be called within the context of - LV2_State_Interface methods. The caller is responsible for freeing the - returned value with free(). + LV2_State_Interface methods. The caller must free the returned value + with LV2_State_Free_Path.free_path(). */ char* (*absolute_path)(LV2_State_Map_Path_Handle handle, const char* abstract_path); @@ -321,7 +329,7 @@ typedef struct { /** Return a path the plugin may use to create a new file. - @param handle MUST be the @p handle member of this struct. + @param handle MUST be the `handle` member of this struct. @param path The path of the new file within a namespace unique to this plugin instance. @return The absolute path to use for the new file. @@ -332,22 +340,50 @@ typedef struct { LV2_Descriptor.instantiate()). The host MUST do whatever is necessary for the plugin to be able to - create a file at the returned path (e.g. using fopen), including - creating any leading directories. + create a file at the returned path (for example, using fopen()), + including creating any leading directories. If this function is passed to LV2_Descriptor.instantiate(), it may be called from any non-realtime context. If it is passed to LV2_State_Interface.save(), it may only be called within the dynamic scope of that function call. - The caller is responsible for freeing the returned value with free(). + The caller must free the returned value with + LV2_State_Free_Path.free_path(). */ char* (*path)(LV2_State_Make_Path_Handle handle, const char* path); } LV2_State_Make_Path; +/** + Feature data for state:freePath (@ref LV2_STATE__freePath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Free_Path_Handle handle; + + /** + Free a path returned by a state feature. + + @param handle MUST be the `handle` member of this struct. + @param path The path previously returned by a state feature. + + This function can be used by plugins to free paths allocated by the host + and returned by state features (LV2_State_Map_Path.abstract_path(), + LV2_State_Map_Path.absolute_path(), and LV2_State_Make_Path.path()). + */ + void (*free_path)(LV2_State_Free_Path_Handle handle, + char* path); +} LV2_State_Free_Path; + #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_STATE_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/time.h b/distrho/src/lv2/time.h index 3bb06142..ec5007c3 100644 --- a/distrho/src/lv2/time.h +++ b/distrho/src/lv2/time.h @@ -1,5 +1,5 @@ /* - Copyright 2011 David Robillard + Copyright 2011-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,35 +15,39 @@ */ /** - @file time.h C header for the LV2 Time extension - . + @defgroup time Time + + Properties for describing time, see for + details. + + Note the time extension is purely data, this header merely defines URIs for + convenience. + + @{ */ #ifndef LV2_TIME_H #define LV2_TIME_H -#ifdef __cplusplus -extern "C" { -#endif - -#define LV2_TIME_URI "http://lv2plug.in/ns/ext/time" - -#define LV2_TIME__Time LV2_TIME_URI "#Time" -#define LV2_TIME__Position LV2_TIME_URI "#Position" -#define LV2_TIME__Rate LV2_TIME_URI "#Rate" -#define LV2_TIME__position LV2_TIME_URI "#position" -#define LV2_TIME__barBeat LV2_TIME_URI "#barBeat" -#define LV2_TIME__bar LV2_TIME_URI "#bar" -#define LV2_TIME__beat LV2_TIME_URI "#beat" -#define LV2_TIME__beatUnit LV2_TIME_URI "#beatUnit" -#define LV2_TIME__beatsPerBar LV2_TIME_URI "#beatsPerBar" -#define LV2_TIME__beatsPerMinute LV2_TIME_URI "#beatsPerMinute" -#define LV2_TIME__frame LV2_TIME_URI "#frame" -#define LV2_TIME__framesPerSecond LV2_TIME_URI "#framesPerSecond" -#define LV2_TIME__speed LV2_TIME_URI "#speed" - -#ifdef __cplusplus -} /* extern "C" */ -#endif +#define LV2_TIME_URI "http://lv2plug.in/ns/ext/time" ///< http://lv2plug.in/ns/ext/time +#define LV2_TIME_PREFIX LV2_TIME_URI "#" ///< http://lv2plug.in/ns/ext/time# + +#define LV2_TIME__Time LV2_TIME_PREFIX "Time" ///< http://lv2plug.in/ns/ext/time#Time +#define LV2_TIME__Position LV2_TIME_PREFIX "Position" ///< http://lv2plug.in/ns/ext/time#Position +#define LV2_TIME__Rate LV2_TIME_PREFIX "Rate" ///< http://lv2plug.in/ns/ext/time#Rate +#define LV2_TIME__position LV2_TIME_PREFIX "position" ///< http://lv2plug.in/ns/ext/time#position +#define LV2_TIME__barBeat LV2_TIME_PREFIX "barBeat" ///< http://lv2plug.in/ns/ext/time#barBeat +#define LV2_TIME__bar LV2_TIME_PREFIX "bar" ///< http://lv2plug.in/ns/ext/time#bar +#define LV2_TIME__beat LV2_TIME_PREFIX "beat" ///< http://lv2plug.in/ns/ext/time#beat +#define LV2_TIME__beatUnit LV2_TIME_PREFIX "beatUnit" ///< http://lv2plug.in/ns/ext/time#beatUnit +#define LV2_TIME__beatsPerBar LV2_TIME_PREFIX "beatsPerBar" ///< http://lv2plug.in/ns/ext/time#beatsPerBar +#define LV2_TIME__beatsPerMinute LV2_TIME_PREFIX "beatsPerMinute" ///< http://lv2plug.in/ns/ext/time#beatsPerMinute +#define LV2_TIME__frame LV2_TIME_PREFIX "frame" ///< http://lv2plug.in/ns/ext/time#frame +#define LV2_TIME__framesPerSecond LV2_TIME_PREFIX "framesPerSecond" ///< http://lv2plug.in/ns/ext/time#framesPerSecond +#define LV2_TIME__speed LV2_TIME_PREFIX "speed" ///< http://lv2plug.in/ns/ext/time#speed + +/** + @} +*/ #endif /* LV2_TIME_H */ diff --git a/distrho/src/lv2/units.h b/distrho/src/lv2/units.h index a40f0973..1debf60b 100644 --- a/distrho/src/lv2/units.h +++ b/distrho/src/lv2/units.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,48 +15,55 @@ */ /** - @file units.h - C definitions for the LV2 Units extension - . + @defgroup units Units + + Units for LV2 values, see for + details. + + @{ */ #ifndef LV2_UNITS_H #define LV2_UNITS_H -#define LV2_UNITS_URI "http://lv2plug.in/ns/extensions/units" -#define LV2_UNITS_PREFIX LV2_UNITS_URI "#" - -#define LV2_UNITS__Conversion LV2_UNITS_PREFIX "Conversion" -#define LV2_UNITS__Unit LV2_UNITS_PREFIX "Unit" -#define LV2_UNITS__bar LV2_UNITS_PREFIX "bar" -#define LV2_UNITS__beat LV2_UNITS_PREFIX "beat" -#define LV2_UNITS__bpm LV2_UNITS_PREFIX "bpm" -#define LV2_UNITS__cent LV2_UNITS_PREFIX "cent" -#define LV2_UNITS__cm LV2_UNITS_PREFIX "cm" -#define LV2_UNITS__coef LV2_UNITS_PREFIX "coef" -#define LV2_UNITS__conversion LV2_UNITS_PREFIX "conversion" -#define LV2_UNITS__db LV2_UNITS_PREFIX "db" -#define LV2_UNITS__degree LV2_UNITS_PREFIX "degree" -#define LV2_UNITS__frame LV2_UNITS_PREFIX "frame" -#define LV2_UNITS__hz LV2_UNITS_PREFIX "hz" -#define LV2_UNITS__inch LV2_UNITS_PREFIX "inch" -#define LV2_UNITS__khz LV2_UNITS_PREFIX "khz" -#define LV2_UNITS__km LV2_UNITS_PREFIX "km" -#define LV2_UNITS__m LV2_UNITS_PREFIX "m" -#define LV2_UNITS__mhz LV2_UNITS_PREFIX "mhz" -#define LV2_UNITS__midiNote LV2_UNITS_PREFIX "midiNote" -#define LV2_UNITS__mile LV2_UNITS_PREFIX "mile" -#define LV2_UNITS__min LV2_UNITS_PREFIX "min" -#define LV2_UNITS__mm LV2_UNITS_PREFIX "mm" -#define LV2_UNITS__ms LV2_UNITS_PREFIX "ms" -#define LV2_UNITS__name LV2_UNITS_PREFIX "name" -#define LV2_UNITS__oct LV2_UNITS_PREFIX "oct" -#define LV2_UNITS__pc LV2_UNITS_PREFIX "pc" -#define LV2_UNITS__prefixConversion LV2_UNITS_PREFIX "prefixConversion" -#define LV2_UNITS__render LV2_UNITS_PREFIX "render" -#define LV2_UNITS__s LV2_UNITS_PREFIX "s" -#define LV2_UNITS__semitone12TET LV2_UNITS_PREFIX "semitone12TET" -#define LV2_UNITS__symbol LV2_UNITS_PREFIX "symbol" -#define LV2_UNITS__unit LV2_UNITS_PREFIX "unit" +#define LV2_UNITS_URI "http://lv2plug.in/ns/extensions/units" ///< http://lv2plug.in/ns/extensions/units +#define LV2_UNITS_PREFIX LV2_UNITS_URI "#" ///< http://lv2plug.in/ns/extensions/units# + +#define LV2_UNITS__Conversion LV2_UNITS_PREFIX "Conversion" ///< http://lv2plug.in/ns/ext/units#Conversion +#define LV2_UNITS__Unit LV2_UNITS_PREFIX "Unit" ///< http://lv2plug.in/ns/ext/units#Unit +#define LV2_UNITS__bar LV2_UNITS_PREFIX "bar" ///< http://lv2plug.in/ns/ext/units#bar +#define LV2_UNITS__beat LV2_UNITS_PREFIX "beat" ///< http://lv2plug.in/ns/ext/units#beat +#define LV2_UNITS__bpm LV2_UNITS_PREFIX "bpm" ///< http://lv2plug.in/ns/ext/units#bpm +#define LV2_UNITS__cent LV2_UNITS_PREFIX "cent" ///< http://lv2plug.in/ns/ext/units#cent +#define LV2_UNITS__cm LV2_UNITS_PREFIX "cm" ///< http://lv2plug.in/ns/ext/units#cm +#define LV2_UNITS__coef LV2_UNITS_PREFIX "coef" ///< http://lv2plug.in/ns/ext/units#coef +#define LV2_UNITS__conversion LV2_UNITS_PREFIX "conversion" ///< http://lv2plug.in/ns/ext/units#conversion +#define LV2_UNITS__db LV2_UNITS_PREFIX "db" ///< http://lv2plug.in/ns/ext/units#db +#define LV2_UNITS__degree LV2_UNITS_PREFIX "degree" ///< http://lv2plug.in/ns/ext/units#degree +#define LV2_UNITS__frame LV2_UNITS_PREFIX "frame" ///< http://lv2plug.in/ns/ext/units#frame +#define LV2_UNITS__hz LV2_UNITS_PREFIX "hz" ///< http://lv2plug.in/ns/ext/units#hz +#define LV2_UNITS__inch LV2_UNITS_PREFIX "inch" ///< http://lv2plug.in/ns/ext/units#inch +#define LV2_UNITS__khz LV2_UNITS_PREFIX "khz" ///< http://lv2plug.in/ns/ext/units#khz +#define LV2_UNITS__km LV2_UNITS_PREFIX "km" ///< http://lv2plug.in/ns/ext/units#km +#define LV2_UNITS__m LV2_UNITS_PREFIX "m" ///< http://lv2plug.in/ns/ext/units#m +#define LV2_UNITS__mhz LV2_UNITS_PREFIX "mhz" ///< http://lv2plug.in/ns/ext/units#mhz +#define LV2_UNITS__midiNote LV2_UNITS_PREFIX "midiNote" ///< http://lv2plug.in/ns/ext/units#midiNote +#define LV2_UNITS__mile LV2_UNITS_PREFIX "mile" ///< http://lv2plug.in/ns/ext/units#mile +#define LV2_UNITS__min LV2_UNITS_PREFIX "min" ///< http://lv2plug.in/ns/ext/units#min +#define LV2_UNITS__mm LV2_UNITS_PREFIX "mm" ///< http://lv2plug.in/ns/ext/units#mm +#define LV2_UNITS__ms LV2_UNITS_PREFIX "ms" ///< http://lv2plug.in/ns/ext/units#ms +#define LV2_UNITS__name LV2_UNITS_PREFIX "name" ///< http://lv2plug.in/ns/ext/units#name +#define LV2_UNITS__oct LV2_UNITS_PREFIX "oct" ///< http://lv2plug.in/ns/ext/units#oct +#define LV2_UNITS__pc LV2_UNITS_PREFIX "pc" ///< http://lv2plug.in/ns/ext/units#pc +#define LV2_UNITS__prefixConversion LV2_UNITS_PREFIX "prefixConversion" ///< http://lv2plug.in/ns/ext/units#prefixConversion +#define LV2_UNITS__render LV2_UNITS_PREFIX "render" ///< http://lv2plug.in/ns/ext/units#render +#define LV2_UNITS__s LV2_UNITS_PREFIX "s" ///< http://lv2plug.in/ns/ext/units#s +#define LV2_UNITS__semitone12TET LV2_UNITS_PREFIX "semitone12TET" ///< http://lv2plug.in/ns/ext/units#semitone12TET +#define LV2_UNITS__symbol LV2_UNITS_PREFIX "symbol" ///< http://lv2plug.in/ns/ext/units#symbol +#define LV2_UNITS__unit LV2_UNITS_PREFIX "unit" ///< http://lv2plug.in/ns/ext/units#unit #endif /* LV2_UNITS_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/uri-map.h b/distrho/src/lv2/uri-map.h index 2062af3a..12d52be1 100644 --- a/distrho/src/lv2/uri-map.h +++ b/distrho/src/lv2/uri-map.h @@ -1,5 +1,5 @@ /* - Copyright 2008-2011 David Robillard + Copyright 2008-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,8 +15,9 @@ */ /** - @file - C header for the LV2 URI Map extension . + @defgroup uri-map URI Map + + C API for the LV2 URI Map extension . This extension defines a simple mechanism for plugins to map URIs to integers, usually for performance reasons (e.g. processing events typed by @@ -25,12 +26,15 @@ values for use in the audio thread without doing any string comparison. This allows the extensibility of RDF with the performance of integers (or centrally defined enumerations). + + @{ */ #ifndef LV2_URI_MAP_H #define LV2_URI_MAP_H -#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" +#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" ///< http://lv2plug.in/ns/ext/uri-map +#define LV2_URI_MAP_PREFIX LV2_URI_MAP_URI "#" ///< http://lv2plug.in/ns/ext/uri-map# #include @@ -96,3 +100,7 @@ typedef struct { #endif #endif /* LV2_URI_MAP_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/urid.h b/distrho/src/lv2/urid.h index c317f7ff..c9ee2e0e 100644 --- a/distrho/src/lv2/urid.h +++ b/distrho/src/lv2/urid.h @@ -1,5 +1,5 @@ /* - Copyright 2008-2012 David Robillard + Copyright 2008-2016 David Robillard Copyright 2011 Gabriel M. Beddingfield Permission to use, copy, modify, and/or distribute this software for any @@ -16,22 +16,25 @@ */ /** - @file urid.h - C header for the LV2 URID extension + @defgroup urid URID + + Features for mapping URIs to and from integers, see + for details. + + @{ */ #ifndef LV2_URID_H #define LV2_URID_H -#define LV2_URID_URI "http://lv2plug.in/ns/ext/urid" -#define LV2_URID_PREFIX LV2_URID_URI "#" +#define LV2_URID_URI "http://lv2plug.in/ns/ext/urid" ///< http://lv2plug.in/ns/ext/urid +#define LV2_URID_PREFIX LV2_URID_URI "#" ///< http://lv2plug.in/ns/ext/urid# -#define LV2_URID__map LV2_URID_PREFIX "map" -#define LV2_URID__unmap LV2_URID_PREFIX "unmap" +#define LV2_URID__map LV2_URID_PREFIX "map" ///< http://lv2plug.in/ns/ext/urid#map +#define LV2_URID__unmap LV2_URID_PREFIX "unmap" ///< http://lv2plug.in/ns/ext/urid#unmap -/* Legacy defines */ -#define LV2_URID_MAP_URI LV2_URID__map -#define LV2_URID_UNMAP_URI LV2_URID__unmap +#define LV2_URID_MAP_URI LV2_URID__map ///< Legacy +#define LV2_URID_UNMAP_URI LV2_URID__unmap ///< Legacy #include @@ -106,11 +109,11 @@ typedef struct _LV2_URID_Unmap { /** Get the URI for a previously mapped numeric ID. - Returns NULL if @p urid is not yet mapped. Otherwise, the corresponding + Returns NULL if `urid` is not yet mapped. Otherwise, the corresponding URI is returned in a canonical form. This MAY not be the exact same string that was originally passed to LV2_URID_Map::map(), but it MUST be an identical URI according to the URI syntax specification (RFC3986). A - non-NULL return for a given @p urid will always be the same for the life + non-NULL return for a given `urid` will always be the same for the life of the plugin. Plugins that intend to perform string comparison on unmapped URIs SHOULD first canonicalise URI strings with a call to map_uri() followed by a call to unmap_uri(). @@ -127,3 +130,7 @@ typedef struct _LV2_URID_Unmap { #endif #endif /* LV2_URID_H */ + +/** + @} +*/ diff --git a/distrho/src/lv2/worker.h b/distrho/src/lv2/worker.h index fc09d556..6c01057a 100644 --- a/distrho/src/lv2/worker.h +++ b/distrho/src/lv2/worker.h @@ -1,5 +1,5 @@ /* - Copyright 2012 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -15,8 +15,12 @@ */ /** - @file worker.h C header for the LV2 Worker extension - . + @defgroup worker Worker + + Support for non-realtime plugin operations, see + for details. + + @{ */ #ifndef LV2_WORKER_H @@ -26,18 +30,18 @@ #include "lv2.h" -#define LV2_WORKER_URI "http://lv2plug.in/ns/ext/worker" -#define LV2_WORKER_PREFIX LV2_WORKER_URI "#" +#define LV2_WORKER_URI "http://lv2plug.in/ns/ext/worker" ///< http://lv2plug.in/ns/ext/worker +#define LV2_WORKER_PREFIX LV2_WORKER_URI "#" ///< http://lv2plug.in/ns/ext/worker# -#define LV2_WORKER__interface LV2_WORKER_PREFIX "interface" -#define LV2_WORKER__schedule LV2_WORKER_PREFIX "schedule" +#define LV2_WORKER__interface LV2_WORKER_PREFIX "interface" ///< http://lv2plug.in/ns/ext/worker#interface +#define LV2_WORKER__schedule LV2_WORKER_PREFIX "schedule" ///< http://lv2plug.in/ns/ext/worker#schedule #ifdef __cplusplus extern "C" { #endif /** - A status code for worker functions. + Status code for worker functions. */ typedef enum { LV2_WORKER_SUCCESS = 0, /**< Completed successfully. */ @@ -45,12 +49,13 @@ typedef enum { LV2_WORKER_ERR_NO_SPACE = 2 /**< Failed due to lack of space. */ } LV2_Worker_Status; +/** Opaque handle for LV2_Worker_Interface::work(). */ typedef void* LV2_Worker_Respond_Handle; /** A function to respond to run() from the worker method. - The @p data MUST be safe for the host to copy and later pass to + The `data` MUST be safe for the host to copy and later pass to work_response(), and the host MUST guarantee that it will be eventually passed to work_response() if this function returns LV2_WORKER_SUCCESS. */ @@ -60,7 +65,7 @@ typedef LV2_Worker_Status (*LV2_Worker_Respond_Function)( const void* data); /** - LV2 Plugin Worker Interface. + Plugin Worker Interface. This is the interface provided by the plugin to implement a worker method. The plugin's extension_data() method should return an LV2_Worker_Interface @@ -71,14 +76,17 @@ typedef struct _LV2_Worker_Interface { The worker method. This is called by the host in a non-realtime context as requested, possibly with an arbitrary message to handle. - A response can be sent to run() using @p respond. The plugin MUST NOT - make any assumptions about which thread calls this method, other than - the fact that there are no real-time requirements. + A response can be sent to run() using `respond`. The plugin MUST NOT + make any assumptions about which thread calls this method, except that + there are no real-time requirements and only one call may be executed at + a time. That is, the host MAY call this method from any non-real-time + thread, but MUST NOT make concurrent calls to this method from several + threads. @param instance The LV2 instance this is a method on. @param respond A function for sending a response to run(). - @param handle Must be passed to @p respond if it is called. - @param size The size of @p data. + @param handle Must be passed to `respond` if it is called. + @param size The size of `data`. @param data Data from run(), or NULL. */ LV2_Worker_Status (*work)(LV2_Handle instance, @@ -92,7 +100,7 @@ typedef struct _LV2_Worker_Interface { run() context when a response from the worker is ready. @param instance The LV2 instance this is a method on. - @param size The size of @p body. + @param size The size of `body`. @param body Message body, or NULL. */ LV2_Worker_Status (*work_response)(LV2_Handle instance, @@ -112,8 +120,15 @@ typedef struct _LV2_Worker_Interface { LV2_Worker_Status (*end_run)(LV2_Handle instance); } LV2_Worker_Interface; +/** Opaque handle for LV2_Worker_Schedule. */ typedef void* LV2_Worker_Schedule_Handle; +/** + Schedule Worker Host Feature. + + The host passes this feature to provide a schedule_work() function, which + the plugin can use to schedule a worker call from run(). +*/ typedef struct _LV2_Worker_Schedule { /** Opaque host data. @@ -138,12 +153,12 @@ typedef struct _LV2_Worker_Schedule { immediately, and responses from the worker are delivered immediately, the effect of the work takes place immediately with sample accuracy. - The @p data MUST be safe for the host to copy and later pass to work(), + The `data` MUST be safe for the host to copy and later pass to work(), and the host MUST guarantee that it will be eventually passed to work() if this function returns LV2_WORKER_SUCCESS. @param handle The handle field of this struct. - @param size The size of @p data. + @param size The size of `data`. @param data Message to pass to work(), or NULL. */ LV2_Worker_Status (*schedule_work)(LV2_Worker_Schedule_Handle handle, @@ -156,3 +171,7 @@ typedef struct _LV2_Worker_Schedule { #endif #endif /* LV2_WORKER_H */ + +/** + @} +*/ From a461061fef0959f5fabbe580c7c65a9cb9c6e514 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 02:24:26 +0000 Subject: [PATCH 315/504] Set lv2:control as designation for event ports Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2.cpp | 4 ++-- distrho/src/DistrhoPluginLV2export.cpp | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 3d27fb59..04e8f983 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -588,8 +588,8 @@ public: const LV2_Atom* value = nullptr; lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); - if (property != nullptr && property->type == fURIDs.atomURID && value != nullptr && - (value->type == fURIDs.atomPath || value->type == fURIDs.atomString)) + if (property != nullptr && property->type == fURIDs.atomURID && + value != nullptr && (value->type == fURIDs.atomPath || value->type == fURIDs.atomString)) { fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); } diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 9878e5eb..c49f7fe4 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -369,7 +369,6 @@ void lv2_generate_ttl(const char* const basename) pluginString += "\n"; #if DISTRHO_PLUGIN_WANT_STATE - // define writable states as lv2 parameters bool hasHostVisibleState = false; for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) @@ -429,10 +428,11 @@ void lv2_generate_ttl(const char* const basename) continue; const String& key(plugin.getStateKey(i)); - pluginString += " patch:readable <" DISTRHO_PLUGIN_URI "#" + key + "> ;\n"; if ((hints & kStateIsHostWritable) == kStateIsHostWritable) pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + "> ;\n"; + else + pluginString += " patch:readable <" DISTRHO_PLUGIN_URI "#" + key + "> ;\n"; } pluginString += "\n"; } @@ -641,12 +641,15 @@ void lv2_generate_ttl(const char* const basename) # if DISTRHO_PLUGIN_WANT_MIDI_INPUT pluginString += " atom:supports midi:MidiEvent ;\n"; # endif +# if DISTRHO_PLUGIN_WANT_TIMEPOS + pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; +# endif # if DISTRHO_PLUGIN_WANT_STATE if (hasHostVisibleState) + { pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; -# endif -# if DISTRHO_PLUGIN_WANT_TIMEPOS - pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; + pluginString += " lv2:designation lv2:control ;\n"; + } # endif pluginString += " ] ;\n\n"; ++portIndex; @@ -668,7 +671,10 @@ void lv2_generate_ttl(const char* const basename) # endif # if DISTRHO_PLUGIN_WANT_STATE if (hasHostVisibleState) + { pluginString += " atom:supports <" LV2_PATCH__Message "> ;\n"; + pluginString += " lv2:designation lv2:control ;\n"; + } # endif pluginString += " ] ;\n\n"; ++portIndex; From a0b25654c979ecdfbec019046696bfdce15c78d7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 03:06:58 +0000 Subject: [PATCH 316/504] Fix build if midi out enabled but not state Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 04e8f983..323225ad 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -1007,7 +1007,7 @@ public: setState(key, value); -#if DISTRHO_LV2_USE_EVENTS_OUT +#if DISTRHO_PLUGIN_WANT_STATE // signal msg needed for UI fNeededUiSends[i] = true; #endif From c28c8b9e15b3f20dc9c09c1002ac6deb67d4af9a Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 04:26:23 +0000 Subject: [PATCH 317/504] Fix LV2 event out size for readable states Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 323225ad..2b0bb4a2 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -772,6 +772,8 @@ public: lv2_atom_forge_string(&atomForge, value.buffer(), static_cast(value.length()+1)); lv2_atom_forge_pop(&atomForge, &forgeFrame); + + msgSize = ((LV2_Atom*)msgBuf)->size; } else { From 007a457e1e0d18c1cfb39ca6b18b9ab246523d0b Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Feb 2022 04:43:50 +0000 Subject: [PATCH 318/504] Make sure the new `updateState` calls `setState` first Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 4 ++-- distrho/src/DistrhoPluginLV2.cpp | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 138caeaa..9fdc86aa 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -994,8 +994,8 @@ public: #if DISTRHO_PLUGIN_WANT_STATE /** - Notify the host about a state value change.@n - This function will automatically trigger a state update on the UI side.@n + Set state value and notify the host about the change.@n + This function will call `setState()` and also trigger an update on the UI side as necessary.@n It must not be called during run.@n The state must be host readable. @note this function does nothing on DSSI plugin format, as DSSI only supports UI->DSP messages. diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 2b0bb4a2..0e8df089 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -1267,10 +1267,16 @@ private: // save this key if necessary if (fPlugin.wantStateKey(key)) - updateState(key, newValue, false); + updateInternalState(key, newValue, false); } - bool updateState(const char* const key, const char* const newValue, const bool sendToUI) + bool updateState(const char* const key, const char* const newValue) + { + fPlugin.setState(key, newValue); + return updateInternalState(key, newValue, true); + } + + bool updateInternalState(const char* const key, const char* const newValue, const bool sendToUI) { // key must already exist for (StringToStringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) @@ -1343,7 +1349,7 @@ private: #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, true); + return ((PluginLv2*)ptr)->updateState(key, value); } #endif From f2cd4c825953971ca62a24f529f8b554cb30bbaa Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 14 Feb 2022 01:34:14 +0000 Subject: [PATCH 319/504] Do not send updated state to LV2 UI when unnecessary Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2.cpp | 23 ++++++++++--- distrho/src/DistrhoUILV2.cpp | 55 ++++++++++++++++++++++++++------ 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 0e8df089..1ad0b16a 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -570,7 +570,11 @@ public: if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) { for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) + { + if (fPlugin.getStateHints(i) & kStateIsOnlyForDSP) + continue; fNeededUiSends[i] = true; + } } // no, send to DSP as usual else if (fWorker != nullptr) @@ -930,10 +934,14 @@ public: if (curKey != key) continue; - const String& value(cit->second); - const uint32_t hints = fPlugin.getStateHints(i); + #if ! DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + // do not save UI-only messages if there is no UI available + if ((hints & kStateIsOnlyForUI) == 0x0) + continue; + #endif + if (hints & kStateIsHostReadable) { lv2key = DISTRHO_PLUGIN_URI "#"; @@ -949,6 +957,8 @@ public: lv2key += key; + const String& value(cit->second); + // some hosts need +1 for the null terminator, even though the type is string store(handle, fUridMap->map(fUridMap->handle, lv2key.buffer()), @@ -1011,7 +1021,8 @@ public: #if DISTRHO_PLUGIN_WANT_STATE // signal msg needed for UI - fNeededUiSends[i] = true; + if ((hints & kStateIsOnlyForDSP) == 0x0) + fNeededUiSends[i] = true; #endif } @@ -1061,7 +1072,8 @@ public: { if (fPlugin.getStateKey(i) == key) { - fNeededUiSends[i] = true; + if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) + fNeededUiSends[i] = true; break; } } @@ -1293,7 +1305,8 @@ private: { if (fPlugin.getStateKey(i) == key) { - fNeededUiSends[i] = true; + if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) + fNeededUiSends[i] = true; break; } } diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 19df7a1b..0bb2158f 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -181,6 +181,33 @@ public: fUI.stateChanged(key, value); } + else if (atom->type == fURIDs.atomObject) + { + /* TODO + const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)LV2_ATOM_BODY_CONST(atom); + + const LV2_Atom* property = nullptr; + const LV2_Atom* avalue = nullptr; + lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &avalue, 0); + + DISTRHO_SAFE_ASSERT_RETURN(property != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(avalue != nullptr,); + + DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID,); + DISTRHO_SAFE_ASSERT_RETURN(avalue->type == fURIDs.atomPath || avalue->type == fURIDs.atomString,); + + if (property != nullptr && property->type == fURIDs.atomURID && + avalue != nullptr && (avalue->type == fURIDs.atomPath || avalue->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); + + d_stdout("received atom object '%s' '%s'", key, value); + + fUI.stateChanged(key, value); + } + */ + } else { d_stdout("received atom not dpfKeyValue"); @@ -368,15 +395,19 @@ private: // LV2 URIDs const struct URIDs { const LV2_URID_Map* _uridMap; - LV2_URID dpfKeyValue; - LV2_URID atomEventTransfer; - LV2_URID atomFloat; - LV2_URID atomLong; - LV2_URID atomPath; - LV2_URID atomString; - LV2_URID midiEvent; - LV2_URID paramSampleRate; - LV2_URID patchSet; + 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), @@ -384,11 +415,15 @@ private: 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)), - patchSet(map(LV2_PATCH__Set)) {} + patchProperty(map(LV2_PATCH__property)), + patchSet(map(LV2_PATCH__Set)), + patchValue(map(LV2_PATCH__value)) {} inline LV2_URID map(const char* const uri) const { From f24e5317cd7b57c8ab36b0987ec29a5579cd5dd2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 14 Feb 2022 02:26:09 +0000 Subject: [PATCH 320/504] Fix VST3 bundle find on Windows Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 4 ++-- distrho/src/DistrhoUtils.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 9abeb858..8033483a 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -4216,9 +4216,9 @@ bool ENTRYFNNAME(void*) String tmpPath(getBinaryFilename()); tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); - DISTRHO_SAFE_ASSERT_RETURN(tmpPath.endsWith("/Contents"), true); + DISTRHO_SAFE_ASSERT_RETURN(tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents"), true); - tmpPath.truncate(tmpPath.rfind('/')); + tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); bundlePath = tmpPath; d_nextBundlePath = bundlePath.buffer(); } diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index d19343fa..7aaed1a2 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -59,7 +59,7 @@ const char* getBinaryFilename() # endif CHAR filenameBuf[MAX_PATH]; filenameBuf[0] = '\0'; - GetModuleFileName(hInstance, filenameBuf, sizeof(filenameBuf)); + GetModuleFileNameA(hInstance, filenameBuf, sizeof(filenameBuf)); filename = filenameBuf; #elif !defined(STATIC_BUILD) Dl_info info; From 60c10055eab602e0f29fa0a4446b0f22d8c79696 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 20 Feb 2022 10:21:36 +0000 Subject: [PATCH 321/504] Fix ambiguous StandaloneWindow::getApp() Signed-off-by: falkTX --- dgl/StandaloneWindow.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp index f86e4c39..673a85e6 100644 --- a/dgl/StandaloneWindow.hpp +++ b/dgl/StandaloneWindow.hpp @@ -71,6 +71,7 @@ public: bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0) { return Window::addIdleCallback(callback, timerFrequencyInMs); } bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } + Application& getApp() const noexcept { return Window::getApp(); } const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } double getScaleFactor() const noexcept { return Window::getScaleFactor(); } void setGeometryConstraints(uint minimumWidth, uint minimumHeight, From 4cc6973d5defdbb45f89b1bf694277ee49a75c2a Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 7 Mar 2022 14:08:49 +0000 Subject: [PATCH 322/504] Accept a few other verbose make flags Signed-off-by: falkTX --- Makefile.base.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index af7aaf48..7c63bcfc 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -489,8 +489,12 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Handle the verbosity switch -ifeq ($(VERBOSE),true) SILENT = + +ifeq ($(VERBOSE),1) +else ifeq ($(VERBOSE),y) +else ifeq ($(VERBOSE),yes) +else ifeq ($(VERBOSE),true) else SILENT = @ endif From 20477b35dd76527e85fc7ac476c07ed8d521f8a3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 7 Mar 2022 20:58:37 +0000 Subject: [PATCH 323/504] Add DISTRHO_API macro, unused for now Signed-off-by: falkTX --- dgl/Application.hpp | 2 +- dgl/Window.hpp | 2 +- distrho/src/DistrhoDefines.h | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 1024e226..28404e39 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -33,7 +33,7 @@ START_NAMESPACE_DGL Unless stated otherwise, functions within this class are not thread-safe. */ -class Application +class DISTRHO_API Application { public: /** diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 3de57e86..35054562 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -52,7 +52,7 @@ class TopLevelWidget; ... */ -class Window +class DISTRHO_API Window { struct PrivateData; diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index 32499a62..929593c2 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -27,10 +27,12 @@ /* Check OS */ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define DISTRHO_API # define DISTRHO_PLUGIN_EXPORT extern "C" __declspec (dllexport) # define DISTRHO_OS_WINDOWS 1 # define DISTRHO_DLL_EXTENSION "dll" #else +# define DISTRHO_API # define DISTRHO_PLUGIN_EXPORT extern "C" __attribute__ ((visibility("default"))) # if defined(__APPLE__) # define DISTRHO_OS_MAC 1 From aad2a31f76251d031638d6385a68efef25452715 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 7 Mar 2022 20:58:59 +0000 Subject: [PATCH 324/504] Add static plugin target Signed-off-by: falkTX --- Makefile.plugins.mk | 18 ++++++++++++++++++ distrho/DistrhoPluginMain.cpp | 4 ++++ distrho/DistrhoUIMain.cpp | 4 ++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 2e39991c..0700fd04 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -129,6 +129,7 @@ ifneq ($(VST3_FILENAME),) vst3 = $(TARGET_DIR)/$(VST3_FILENAME) endif shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) +static = $(TARGET_DIR)/$(NAME).a ifeq ($(MACOS),true) vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist @@ -474,6 +475,21 @@ endif @echo "Creating shared library for $(NAME)" $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@ +# --------------------------------------------------------------------------------------------------------------------- +# Static + +static: $(static) + +ifeq ($(HAVE_DGL),true) +$(static): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.o $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.o +else +$(static): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.o +endif + -@mkdir -p $(shell dirname $@) + @echo "Creating static library for $(NAME)" + $(SILENT)rm -f $@ + $(SILENT)$(AR) crs $@ $^ + # --------------------------------------------------------------------------------------------------------------------- # macOS files @@ -503,6 +519,7 @@ endif -include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d -include $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.d +-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d @@ -510,5 +527,6 @@ endif -include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d -include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d +-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp index 6affef3c..2676cca9 100644 --- a/distrho/DistrhoPluginMain.cpp +++ b/distrho/DistrhoPluginMain.cpp @@ -32,6 +32,10 @@ #elif defined(DISTRHO_PLUGIN_TARGET_SHARED) DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin(); DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); } +#elif defined(DISTRHO_PLUGIN_TARGET_STATIC) +START_NAMESPACE_DISTRHO +Plugin* createStaticPlugin() { return createPlugin(); } +END_NAMESPACE_DISTRHO #else # error unsupported format #endif diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index aeeade58..9b898bc7 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -28,7 +28,7 @@ // nothing #elif defined(DISTRHO_PLUGIN_TARGET_VST3) # include "src/DistrhoUIVST3.cpp" -#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) +#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC) // nothing #else # error unsupported format From 72576cba9231e75b8dcc2b6fd792069213f8e088 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Mar 2022 21:30:25 +0000 Subject: [PATCH 325/504] Fix logic around LV2 state save when no gui is available Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 1ad0b16a..43ba64e9 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -938,8 +938,8 @@ public: #if ! DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS // do not save UI-only messages if there is no UI available - if ((hints & kStateIsOnlyForUI) == 0x0) - continue; + if (hints & kStateIsOnlyForUI) + break; #endif if (hints & kStateIsHostReadable) @@ -966,6 +966,8 @@ public: value.length()+1, urid, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); + + break; } } From 55916eb0fba3a829a6e50bd38371b6ad37912292 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 10 Mar 2022 21:32:11 +0000 Subject: [PATCH 326/504] Use -fno-gnu-unique by default Signed-off-by: falkTX --- Makefile.base.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile.base.mk b/Makefile.base.mk index 7c63bcfc..62b6c2cd 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -172,6 +172,10 @@ ifeq ($(NOOPT),true) BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections endif +ifneq ($(MACOS_OR_WINDOWS),true) +BASE_FLAGS += -fno-gnu-unique +endif + ifeq ($(WINDOWS),true) # Assume we want posix BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS From 21330021cea0854dc6f21a0c718e0e3692dc441f Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 11 Mar 2022 09:47:59 +0000 Subject: [PATCH 327/504] Add window offset related functions --- dgl/Window.hpp | 35 ++++++++++++++++++++++++++ dgl/src/Window.cpp | 42 +++++++++++++++++++++++++++++++ dgl/src/pugl.cpp | 12 +++++++++ dgl/src/pugl.hpp | 8 ++++-- distrho/src/DistrhoUIInternal.hpp | 9 +++++++ 5 files changed, 104 insertions(+), 2 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 35054562..0f0b6075 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -206,6 +206,41 @@ public: */ void setResizable(bool resizable); + /** + Get X offset, typically 0. + */ + int getOffsetX() const noexcept; + + /** + Get Y offset, typically 0. + */ + int getOffsetY() const noexcept; + + /** + Get offset. + */ + Point getOffset() const noexcept; + + /** + Set X offset. + */ + void setOffsetX(int x); + + /** + Set Y offset. + */ + void setOffsetY(int y); + + /** + Set offset using @a x and @a y values. + */ + void setOffset(int x, int y); + + /** + Set offset. + */ + void setOffset(const Point& offset); + /** Get width. */ diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index d67ab2b4..e77790e0 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -155,6 +155,48 @@ void Window::setResizable(const bool resizable) pData->setResizable(resizable); } +int Window::getOffsetX() const noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); + + return puglGetFrame(pData->view).x; +} + +int Window::getOffsetY() const noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); + + return puglGetFrame(pData->view).y; +} + +Point Window::getOffset() const noexcept +{ + DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point()); + + const PuglRect rect = puglGetFrame(pData->view); + return Point(rect.x, rect.y); +} + +void Window::setOffsetX(const int x) +{ + setOffset(x, getOffsetY()); +} + +void Window::setOffsetY(const int y) +{ + setOffset(getOffsetX(), y); +} + +void Window::setOffset(const int x, const int y) +{ + puglSetWindowOffset(pData->view, x, y); +} + +void Window::setOffset(const Point& offset) +{ + setOffset(offset.getX(), offset.getY()); +} + uint Window::getWidth() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index a570c37f..bf21d2c6 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -339,6 +339,18 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co return PUGL_SUCCESS; } +// -------------------------------------------------------------------------------------------------------------------- +// set window offset without changing size + +PuglStatus puglSetWindowOffset(PuglView* const view, const int x, const int y) +{ + // TODO custom setFrame version + PuglRect rect = puglGetFrame(view); + rect.x = x; + rect.y = y; + return puglSetFrame(view, rect); +} + // -------------------------------------------------------------------------------------------------------------------- // set window size with default size and without changing frame x/y position diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 2075627a..afa0cf05 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -81,11 +81,15 @@ puglSetMatchingBackendForCurrentBuild(PuglView* view); // Combine puglSetMinSize and puglSetAspectRatio PUGL_API PuglStatus -puglSetGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect); +puglSetGeometryConstraints(PuglView* view, uint width, uint height, bool aspect); + +// set window offset without changing size +PUGL_API PuglStatus +puglSetWindowOffset(PuglView* view, int x, int y); // set window size with default size and without changing frame x/y position PUGL_API PuglStatus -puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); +puglSetWindowSize(PuglView* view, uint width, uint height); // DGL specific, build-specific drawing prepare PUGL_API void diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 31d97f0d..26786260 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -284,6 +284,15 @@ public: // ------------------------------------------------------------------- + void setWindowOffset(const int x, const int y) + { +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + // TODO +#else + uiData->window->setOffset(x, y); +#endif + } + #ifdef DISTRHO_PLUGIN_TARGET_VST3 void setWindowSizeForVST3(const uint width, const uint height) { From 2e304d24dc3cdecda34681fe0bee93d8921a0d72 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 12 Mar 2022 01:21:03 +0000 Subject: [PATCH 328/504] Fix some compiler warnings Signed-off-by: falkTX --- dgl/Window.hpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 2 +- distrho/src/DistrhoUIInternal.hpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 0f0b6075..51a7d2a3 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -512,7 +512,7 @@ private: bool isVST3, bool doPostInit); - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window); + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window) }; // ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 8033483a..0257c14f 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1554,7 +1554,7 @@ public: if (hints & kParameterIsBoolean) { - const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; + const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f; value = value > midRange ? ranges.max : ranges.min; } else if (hints & kParameterIsInteger) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 26786260..6deab0f4 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -288,6 +288,7 @@ public: { #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI // TODO + (void)x; (void)y; #else uiData->window->setOffset(x, y); #endif From 1add41ad99e983e19d539efaa19842b5cfba3e10 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 12 Mar 2022 12:08:32 +0000 Subject: [PATCH 329/504] Fix standalone for STATIC_BUILD Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index de43684e..acdd9f37 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -951,7 +951,7 @@ int main(int argc, char* argv[]) d_nextSampleRate = jackbridge_get_sample_rate(client); d_nextCanRequestParameterValueChanges = true; - #ifndef DISTRHO_OS_WINDOWS + #if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD) // find plugin bundle static String bundlePath; if (bundlePath.isEmpty()) From 697293675f5f085e71edd37e997229fc6c7e0ddb Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 12 Mar 2022 13:12:06 +0000 Subject: [PATCH 330/504] Really fix static build for standalone Signed-off-by: falkTX --- Makefile.plugins.mk | 4 ++++ distrho/src/DistrhoPluginJACK.cpp | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 0700fd04..3a409351 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -46,6 +46,10 @@ ifeq ($(HAVE_PULSEAUDIO),true) BASE_FLAGS += -DHAVE_PULSEAUDIO endif +ifeq ($(STATIC_BUILD),true) +JACK_LIBS += $(shell $(PKG_CONFIG) --libs jack) +endif + ifeq ($(MACOS),true) JACK_LIBS += -framework CoreAudio -framework CoreFoundation else ifeq ($(WINDOWS),true) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index acdd9f37..0b417fc1 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,7 +15,10 @@ */ #include "DistrhoPluginInternal.hpp" -#include "../DistrhoPluginUtils.hpp" + +#if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD) +# include "../DistrhoPluginUtils.hpp" +#endif #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" @@ -28,6 +31,10 @@ # include "../extra/Thread.hpp" #endif +#ifdef STATIC_BUILD +# define JACKBRIDGE_DIRECT +#endif + #include "jackbridge/JackBridge.cpp" #include "lv2/lv2.h" From 7cd27b17fbbd196beff8c1fcc209f10e9c24f274 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 12 Mar 2022 14:42:19 +0000 Subject: [PATCH 331/504] Set plugin dummy flag if RUNNING_UNDER_LV2LINT env var is set --- distrho/src/DistrhoPluginLV2.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 43ba64e9..b3e7956a 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -1486,6 +1486,9 @@ static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, cons d_nextBundlePath = bundlePath; d_nextCanRequestParameterValueChanges = ctrlInPortChangeReq != nullptr; + if (std::getenv("RUNNING_UNDER_LV2LINT") != nullptr) + d_nextPluginIsDummy = true; + return new PluginLv2(sampleRate, uridMap, worker, ctrlInPortChangeReq, usingNominal); } From a449da1881e3d5bffeb45be476ac34302870708d Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 15 Mar 2022 11:35:29 +0000 Subject: [PATCH 332/504] Fix vst3 filename for 32bit windows Signed-off-by: falkTX --- Makefile.plugins.mk | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 3a409351..f326c690 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -109,13 +109,15 @@ endif ifeq ($(LINUX),true) VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so -endif -ifeq ($(MACOS),true) +else ifeq ($(MACOS),true) VST3_CONTENTS = $(NAME).vst3/Contents VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) +else ifeq ($(WINDOWS),true) +ifeq ($(CPU_I386),true) +VST3_FILENAME = $(NAME).vst3/Contents/x86-win/$(NAME).vst3 +else ifeq ($(CPU_X86_64),true) +VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3 endif -ifeq ($(WINDOWS),true) -VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-win/$(NAME).vst3 endif # --------------------------------------------------------------------------------------------------------------------- From ddf878c13a08dd212e3dcc3ce1bd708ea2373dbb Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 19 Mar 2022 17:52:40 +0000 Subject: [PATCH 333/504] Add WINDOWS_ICON_ID build macro, show msgbox on windows on error Signed-off-by: falkTX --- Makefile.base.mk | 4 ++++ dgl/src/pugl.cpp | 25 ++++++++++++++++++++++++- distrho/src/DistrhoPluginJACK.cpp | 8 ++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 62b6c2cd..908171e7 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -444,6 +444,10 @@ ifeq ($(FILE_BROWSER_DISABLED),true) BUILD_CXX_FLAGS += -DDGL_FILE_BROWSER_DISABLED endif +ifneq ($(WINDOWS_ICON_ID),) +BUILD_CXX_FLAGS += -DDGL_WINDOWS_ICON_ID=$(WINDOWS_ICON_ID) +endif + ifeq ($(USE_OPENGL3),true) BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 endif diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index bf21d2c6..154d8a68 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -567,7 +567,30 @@ void puglWin32ShowCentered(PuglView* const view) } else { - ShowWindow(impl->hwnd, SW_SHOWNORMAL); +#ifdef DGL_WINDOWS_ICON_ID + WNDCLASSEX wClass; + std::memset(&wClass, 0, sizeof(wClass)); + + const HINSTANCE hInstance = GetModuleHandle(nullptr); + + if (GetClassInfoEx(hInstance, view->world->className, &wClass)) + wClass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)); + + SetClassLongPtr(impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID))); +#endif + + MONITORINFO mInfo; + std::memset(&mInfo, 0, sizeof(mInfo)); + mInfo.cbSize = sizeof(mInfo); + + if (GetMonitorInfo(MonitorFromWindow(impl->hwnd, MONITOR_DEFAULTTOPRIMARY), &mInfo)) + SetWindowPos(impl->hwnd, + HWND_TOP, + mInfo.rcWork.left + (mInfo.rcWork.right - view->frame.width) / 2, + mInfo.rcWork.top + (mInfo.rcWork.bottom - view->frame.height) / 2, + 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); + else + ShowWindow(impl->hwnd, SW_NORMAL); } SetFocus(impl->hwnd); diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 0b417fc1..f9423543 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -944,11 +944,15 @@ int main(int argc, char* argv[]) if (errorString.isNotEmpty()) { errorString[errorString.length()-2] = '.'; - d_stderr("Failed to create jack client, reason was:\n%s", errorString.buffer()); + d_stderr("Failed to create the JACK client, reason was:\n%s", errorString.buffer()); } else - d_stderr("Failed to create jack client, cannot continue!"); + d_stderr("Failed to create the JACK client, cannot continue!"); + #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI + const String win32error = "Failed to create JACK client, reason was:\n" + errorString; + MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR); + #endif return 1; } From a941917f15be21e6bdce6793addec225537cccc5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 20 Mar 2022 14:53:40 +0000 Subject: [PATCH 334/504] Remove workaround for VST2 uppercase keys, as it breaks some hosts --- distrho/src/DistrhoPluginVST2.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 95429c7c..6afa717f 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -359,23 +359,10 @@ public: if (index > 0) { // keyboard events must always be lowercase - bool needsShiftRevert = false; if (index >= 'A' && index <= 'Z') - { index += 'a' - 'A'; // A-Z -> a-z - if ((fKeyboardModifiers & kModifierShift) == 0x0) - { - needsShiftRevert = true; - fKeyboardModifiers |= kModifierShift; - } - } - fUI.handlePluginKeyboardVST2(down, static_cast(index), fKeyboardModifiers); - - if (needsShiftRevert) - fKeyboardModifiers &= ~kModifierShift; - return 1; } From 1ec670c9cc7fef66dfc42802a4b69e285bb7455e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 26 Mar 2022 16:23:45 +0000 Subject: [PATCH 335/504] Make JACK Standalone more useful on windows Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 68 +++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index f9423543..9f85875c 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -907,6 +907,36 @@ int main(int argc, char* argv[]) return runSelfTests() ? 0 : 1; #endif +#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/ + */ + bool hasConsole = false; + + HANDLE consoleHandleOut, consoleHandleError; + + if (AttachConsole(ATTACH_PARENT_PROCESS)) + { + // Redirect unbuffered STDOUT to the console + consoleHandleOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (consoleHandleOut != INVALID_HANDLE_VALUE) + { + freopen("CONOUT$", "w", stdout); + setvbuf(stdout, NULL, _IONBF, 0); + } + + // Redirect unbuffered STDERR to the console + consoleHandleError = GetStdHandle(STD_ERROR_HANDLE); + if (consoleHandleError != INVALID_HANDLE_VALUE) + { + freopen("CONOUT$", "w", stderr); + setvbuf(stderr, NULL, _IONBF, 0); + } + + hasConsole = true; + } +#endif + jack_status_t status = jack_status_t(0x0); jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); @@ -950,9 +980,19 @@ int main(int argc, char* argv[]) d_stderr("Failed to create the JACK client, cannot continue!"); #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI + // make sure message box is high-dpi aware + if (const HMODULE user32 = LoadLibrary("user32.dll")) + { + typedef BOOL(WINAPI* SPDA)(void); + if (const SPDA SetProcessDPIAware = (SPDA)GetProcAddress(user32, "SetProcessDPIAware")) + SetProcessDPIAware(); + FreeLibrary(user32); + } + const String win32error = "Failed to create JACK client, reason was:\n" + errorString; MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR); #endif + return 1; } @@ -992,6 +1032,34 @@ int main(int argc, char* argv[]) const PluginJack p(client); +#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/ + */ + + // Send "enter" to release application from the console + // This is a hack, but if not used the console doesn't know the application has + // returned. The "enter" key only sent if the console window is in focus. + if (hasConsole && (GetConsoleWindow() == GetForegroundWindow() || SetFocus(GetConsoleWindow()) != nullptr)) + { + INPUT ip; + // Set up a generic keyboard event. + ip.type = INPUT_KEYBOARD; + ip.ki.wScan = 0; // hardware scan code for key + ip.ki.time = 0; + ip.ki.dwExtraInfo = 0; + + // Send the "Enter" key + ip.ki.wVk = 0x0D; // virtual-key code for the "Enter" key + ip.ki.dwFlags = 0; // 0 for key press + SendInput(1, &ip, sizeof(INPUT)); + + // Release the "Enter" key + ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release + SendInput(1, &ip, sizeof(INPUT)); + } +#endif + return 0; #ifndef DPF_RUNTIME_TESTING From f26b8147d309f8b3cdcaa6b3e6d8dac773658009 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 26 Mar 2022 17:05:24 +0000 Subject: [PATCH 336/504] win32: make sure to return context back to ourselves when resizing Signed-off-by: falkTX --- dgl/src/pugl.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 154d8a68..5492493f 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -163,7 +163,7 @@ START_NAMESPACE_DGL bool puglBackendEnter(PuglView* const view) { - return view->backend->enter(view, NULL) == PUGL_SUCCESS; + return view->backend->enter(view, nullptr) == PUGL_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- @@ -171,7 +171,7 @@ bool puglBackendEnter(PuglView* const view) void puglBackendLeave(PuglView* const view) { - view->backend->leave(view, NULL); + view->backend->leave(view, nullptr); } // -------------------------------------------------------------------------------------------------------------------- @@ -397,14 +397,20 @@ PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), FALSE, puglWinGetWindowExFlags(view)); - if (! SetWindowPos(view->impl->hwnd, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER)) - return PUGL_UNKNOWN_ERROR; + if (SetWindowPos(view->impl->hwnd, + HWND_TOP, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER)) + { + // make sure to return context back to ourselves + view->backend->enter(view, nullptr); + return PUGL_SUCCESS; + } + + return PUGL_UNKNOWN_ERROR; } #else // matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow From 2e224d4a7fda99a705879648a3b335f6c12b131f Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 14:12:28 +0100 Subject: [PATCH 337/504] Try a DBus desktop portal property before assuming not available Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 105 +++++++++++++++++----------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index a40fd3b4..9575d8eb 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -359,52 +359,77 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, // optional, can be null DBusConnection* const dbuscon = handle->dbuscon; - if (dbuscon != nullptr && dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr)) + // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.FileChooser + if (dbuscon != nullptr) { - // https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.FileChooser - if (DBusMessage* const message = dbus_message_new_method_call("org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.FileChooser", - options.saving ? "SaveFile" : "OpenFile")) + // if this is the first time we are calling into DBus, check if things are working + static bool firstTry = true; + + if (firstTry) { - char windowIdStr[32]; - memset(windowIdStr, 0, sizeof(windowIdStr)); -# ifdef HAVE_X11 - snprintf(windowIdStr, sizeof(windowIdStr)-1, "x11:%llx", (ulonglong)windowId); -# endif - const char* windowIdStrPtr = windowIdStr; - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &windowIdStrPtr, - DBUS_TYPE_STRING, &windowTitle, - DBUS_TYPE_INVALID); - - DBusMessageIter iter, array; - dbus_message_iter_init_append(message, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); + firstTry = false; + if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.FileChooser", + "version")) { - DBusMessageIter dict, variant, variantArray; - const char* const current_folder_key = "current_folder"; - const char* const current_folder_val = startDir.buffer(); - - dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict); - dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, ¤t_folder_key); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "ay", &variant); - dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &variantArray); - dbus_message_iter_append_fixed_array(&variantArray, DBUS_TYPE_BYTE, - ¤t_folder_val, startDir.length()+1); - dbus_message_iter_close_container(&variant, &variantArray); - dbus_message_iter_close_container(&dict, &variant); - dbus_message_iter_close_container(&array, &dict); - } - - dbus_message_iter_close_container(&iter, &array); + if (DBusMessage* const reply = dbus_connection_send_with_reply_and_block(dbuscon, msg, 250, nullptr)) + dbus_message_unref(reply); - dbus_connection_send(dbuscon, message, nullptr); + dbus_message_unref(msg); + } + } - dbus_message_unref(message); - return handle.release(); + // Any subsquent calls should have this DBus service active + if (dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr)) + { + if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.FileChooser", + options.saving ? "SaveFile" : "OpenFile")) + { + #ifdef HAVE_X11 + char windowIdStr[32]; + memset(windowIdStr, 0, sizeof(windowIdStr)); + snprintf(windowIdStr, sizeof(windowIdStr)-1, "x11:%llx", (ulonglong)windowId); + const char* windowIdStrPtr = windowIdStr; + #endif + + dbus_message_append_args(msg, + #ifdef HAVE_X11 + DBUS_TYPE_STRING, &windowIdStrPtr, + #endif + DBUS_TYPE_STRING, &windowTitle, + DBUS_TYPE_INVALID); + + DBusMessageIter iter, array; + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); + + { + DBusMessageIter dict, variant, variantArray; + const char* const current_folder_key = "current_folder"; + const char* const current_folder_val = startDir.buffer(); + + dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, nullptr, &dict); + dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, ¤t_folder_key); + dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "ay", &variant); + dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &variantArray); + dbus_message_iter_append_fixed_array(&variantArray, DBUS_TYPE_BYTE, + ¤t_folder_val, startDir.length()+1); + dbus_message_iter_close_container(&variant, &variantArray); + dbus_message_iter_close_container(&dict, &variant); + dbus_message_iter_close_container(&array, &dict); + } + + dbus_message_iter_close_container(&iter, &array); + + dbus_connection_send(dbuscon, msg, nullptr); + + dbus_message_unref(msg); + return handle.release(); + } } } #endif From c122af48955dace9d01ada91b05d8d7359a9f08d Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 14:36:33 +0100 Subject: [PATCH 338/504] Do not check for dbus portal if already available Signed-off-by: falkTX --- distrho/extra/FileBrowserDialog.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index 9575d8eb..195b4a8a 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -363,11 +363,11 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, if (dbuscon != nullptr) { // if this is the first time we are calling into DBus, check if things are working - static bool firstTry = true; + static bool checkAvailable = !dbus_bus_name_has_owner(dbuscon, "org.freedesktop.portal.Desktop", nullptr); - if (firstTry) + if (checkAvailable) { - firstTry = false; + checkAvailable = false; if (DBusMessage* const msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", From e2dcae3758684b8c6a3157da71aad3757c9e652c Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 18:29:24 +0100 Subject: [PATCH 339/504] Fix make clean Closes #367 Signed-off-by: falkTX --- Makefile.plugins.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index f326c690..74c3f475 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -323,7 +323,11 @@ $(BUILD_DIR)/%.mm.o: %.mm clean: rm -rf $(BUILD_DIR) - rm -rf $(TARGET_DIR)/$(NAME) $(TARGET_DIR)/$(NAME)-* $(TARGET_DIR)/$(NAME).lv2 + rm -rf $(TARGET_DIR)/$(NAME) + rm -rf $(TARGET_DIR)/$(NAME)-* + rm -rf $(TARGET_DIR)/$(NAME).lv2 + rm -rf $(TARGET_DIR)/$(NAME).vst + rm -rf $(TARGET_DIR)/$(NAME).vst3 # --------------------------------------------------------------------------------------------------------------------- # DGL From 4313bac8873d9f62c9a6319a4a8faba8219147e2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 20:09:35 +0100 Subject: [PATCH 340/504] Make external UIs work as VST3 See #360 for initial implementation Signed-off-by: falkTX --- distrho/src/DistrhoUIInternal.hpp | 20 +-- distrho/src/DistrhoUIVST3.cpp | 243 +++++++++++++++++++++++------- 2 files changed, 202 insertions(+), 61 deletions(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 6deab0f4..fb36f6e3 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -262,7 +262,16 @@ public: // ------------------------------------------------------------------- -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) +#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) + void idleForVST3() + { + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); + + uiData->app.triggerIdleCallbacks(); + ui->uiIdle(); + } + +# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs) { uiData->window->addIdleCallback(cb, timerFrequencyInMs); @@ -272,14 +281,7 @@ public: { uiData->window->removeIdleCallback(cb); } - - void idleForVST3() - { - DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); - - uiData->app.triggerIdleCallbacks(); - ui->uiIdle(); - } +# endif #endif // ------------------------------------------------------------------- diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index d0e8422c..154b98c6 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -21,6 +21,11 @@ #include "travesty/host.h" #include "travesty/view.h" +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI && defined(DISTRHO_OS_WINDOWS) +# include +# define DPF_VST3_WIN32_TIMER_ID 1 +#endif + /* TODO items: * - mousewheel event * - key down/up events @@ -29,7 +34,9 @@ */ #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) -# define DPF_VST3_USING_HOST_RUN_LOOP +# define DPF_VST3_USING_HOST_RUN_LOOP 1 +#else +# define DPF_VST3_USING_HOST_RUN_LOOP 0 #endif #ifndef DPF_VST3_TIMER_INTERVAL @@ -96,6 +103,130 @@ static void applyGeometryConstraints(const uint minimumWidth, // -------------------------------------------------------------------------------------------------------------------- +/** + * 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) + : 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 + { + } + + 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) + constexpr const CFTimeInterval interval = DPF_VST3_TIMER_INTERVAL * 0.0001; + + CFRunLoopTimerContext context = {}; + context.info = this; + fTimerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0, + platformIdleTimerCallback, &context); + 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. + * There is no need for implementing a window proc because DefWindowProc already calls the + * callback function when processing WM_TIMER messages. + */ + fTimerWindowClassName = ( + #ifdef DISTRHO_PLUGIN_BRAND + DISTRHO_PLUGIN_BRAND + #else + DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE) + #endif + "-" DISTRHO_PLUGIN_NAME "-" + ); + + char suffix[9]; + std::snprintf(suffix, 8, "%08x", std::rand()); + suffix[8] = '\0'; + fTimerWindowClassName += suffix; + + WNDCLASSEX cls; + ZeroMemory(&cls, sizeof(cls)); + cls.cbSize = sizeof(WNDCLASSEX); + cls.cbWndExtra = sizeof(LONG_PTR); + cls.lpszClassName = fTimerWindowClassName.buffer(); + cls.lpfnWndProc = DefWindowProc; + RegisterClassEx(&cls); + + fTimerWindow = CreateWindowEx(0, cls.lpszClassName, "DPF Timer Helper", + 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); + DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,); + + SetWindowLongPtr(fTimerWindow, GWLP_USERDATA, reinterpret_cast(static_cast(this))); + SetTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID, DPF_VST3_TIMER_INTERVAL, + static_cast(platformIdleTimerCallback)); + #endif + } + + ~NativeIdleCallback() + { + if (!fCallbackRegistered) + return; + + #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + fUI.removeIdleCallbackForVST3(this); + #elif defined(DISTRHO_OS_MAC) + CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); + CFRelease(fTimerRef); + #elif defined(DISTRHO_OS_WINDOWS) + DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,); + KillTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID); + DestroyWindow(fTimerWindow); + UnregisterClass(fTimerWindowClassName, nullptr); + #endif + } + +private: + UIExporter& fUI; + bool fCallbackRegistered; + + #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if defined(DISTRHO_OS_MAC) + CFRunLoopTimerRef fTimerRef; + + static void platformIdleTimerCallback(CFRunLoopTimerRef, void* const info) + { + static_cast(info)->idleCallback(); + } + #elif defined(DISTRHO_OS_WINDOWS) + HWND fTimerWindow; + String fTimerWindowClassName; + + WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD) + { + reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA))->idleCallback(); + } + #endif + #endif +}; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + /** * VST3 UI class. * @@ -105,8 +236,8 @@ static void applyGeometryConstraints(const uint minimumWidth, * The low-level VST3 stuff comes after. */ class UIVst3 -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) - : public IdleCallback +#if !DPF_VST3_USING_HOST_RUN_LOOP + : public NativeIdleCallback #endif { public: @@ -119,7 +250,11 @@ public: const double sampleRate, void* const instancePointer, const bool willResizeFromHost) - : fView(view), + : +#if !DPF_VST3_USING_HOST_RUN_LOOP + NativeIdleCallback(fUI), +#endif + fView(view), fHostApplication(host), fConnection(connection), fFrame(frame), @@ -142,9 +277,6 @@ public: ~UIVst3() { -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) - fUI.removeIdleCallbackForVST3(this); -#endif if (fConnection != nullptr) disconnect(); } @@ -153,11 +285,12 @@ public: { if (fIsResizingFromHost && nextWidth > 0 && nextHeight > 0) { -#ifdef DISTRHO_OS_MAC + #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); nextWidth *= scaleFactor; nextHeight *= scaleFactor; -#endif + #endif + if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight) { d_stdout("postInit sets new size as %u %u", nextWidth, nextHeight); @@ -168,9 +301,9 @@ public: if (fConnection != nullptr) connect(fConnection); -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) - fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL); -#endif + #if !DPF_VST3_USING_HOST_RUN_LOOP + registerNativeIdleCallback(); + #endif } // ---------------------------------------------------------------------------------------------------------------- @@ -199,17 +332,17 @@ public: dglmods |= kModifierShift; if (modifiers & (1 << 1)) dglmods |= kModifierAlt; -# ifdef DISTRHO_OS_MAC + #ifdef DISTRHO_OS_MAC if (modifiers & (1 << 2)) dglmods |= kModifierSuper; if (modifiers & (1 << 3)) dglmods |= kModifierControl; -# else + #else if (modifiers & (1 << 2)) dglmods |= kModifierControl; if (modifiers & (1 << 3)) dglmods |= kModifierSuper; -# endif + #endif return fUI.handlePluginKeyboardVST3(true, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; } @@ -230,17 +363,17 @@ public: dglmods |= kModifierShift; if (modifiers & (1 << 1)) dglmods |= kModifierAlt; -# ifdef DISTRHO_OS_MAC + #ifdef DISTRHO_OS_MAC if (modifiers & (1 << 2)) dglmods |= kModifierSuper; if (modifiers & (1 << 3)) dglmods |= kModifierControl; -# else + #else if (modifiers & (1 << 2)) dglmods |= kModifierControl; if (modifiers & (1 << 3)) dglmods |= kModifierSuper; -# endif + #endif return fUI.handlePluginKeyboardVST3(false, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; } @@ -259,11 +392,12 @@ public: rect->left = rect->top = 0; rect->right = fUI.getWidth(); rect->bottom = fUI.getHeight(); -#ifdef DISTRHO_OS_MAC + #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); rect->right /= scaleFactor; rect->bottom /= scaleFactor; -#endif + #endif + d_stdout("getSize request returning %i %i", rect->right, rect->bottom); return V3_OK; } @@ -271,13 +405,14 @@ public: v3_result onSize(v3_view_rect* const orect) { v3_view_rect rect = *orect; -#ifdef DISTRHO_OS_MAC + + #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); rect.top *= scaleFactor; rect.left *= scaleFactor; rect.right *= scaleFactor; rect.bottom *= scaleFactor; -#endif + #endif if (fIsResizingFromPlugin) { @@ -308,11 +443,13 @@ public: uint minimumWidth, minimumHeight; bool keepAspectRatio; fUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); -#ifdef DISTRHO_OS_MAC + + #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); minimumWidth /= scaleFactor; minimumHeight /= scaleFactor; -#endif + #endif + applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); return V3_TRUE; } @@ -414,7 +551,7 @@ public: return V3_OK; } -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE if (std::strcmp(msgid, "state-set") == 0) { int64_t keyLength = -1; @@ -462,7 +599,7 @@ public: std::free(value16); return V3_OK; } -#endif + #endif d_stdout("UIVst3 received unknown msg '%s'", msgid); @@ -482,25 +619,25 @@ public: return V3_OK; } + #if DPF_VST3_USING_HOST_RUN_LOOP // ---------------------------------------------------------------------------------------------------------------- - // special idle callback on macOS and Windows + // v3_timer_handler interface calls -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) - void idleCallback() override + void onTimer() { - fUI.idleForVST3(); + fUI.plugin_idle(); doIdleStuff(); } -#else + #else // ---------------------------------------------------------------------------------------------------------------- - // v3_timer_handler interface calls + // special idle callback without v3_timer_handler - void onTimer() + void idleCallback() override { - fUI.plugin_idle(); + fUI.idleForVST3(); doIdleStuff(); } -#endif + #endif void doIdleStuff() { @@ -629,11 +766,11 @@ private: DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); -#ifdef DISTRHO_OS_MAC + #ifdef DISTRHO_OS_MAC const double scaleFactor = fUI.getScaleFactor(); width /= scaleFactor; height /= scaleFactor; -#endif + #endif if (fIsResizingFromHost) { @@ -657,7 +794,7 @@ private: ((UIVst3*)ptr)->setSize(width, height); } -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); @@ -684,9 +821,9 @@ private: { ((UIVst3*)ptr)->sendNote(channel, note, velocity); } -#endif + #endif -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE void setState(const char* const key, const char* const value) { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); @@ -711,7 +848,7 @@ private: { ((UIVst3*)ptr)->setState(key, value); } -#endif + #endif }; // -------------------------------------------------------------------------------------------------------------------- @@ -887,7 +1024,7 @@ struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { } }; -#ifdef DPF_VST3_USING_HOST_RUN_LOOP +#if DPF_VST3_USING_HOST_RUN_LOOP // -------------------------------------------------------------------------------------------------------------------- // dpf_timer_handler @@ -963,9 +1100,9 @@ struct dpf_plugin_view : v3_plugin_view_cpp { std::atomic_int refcounter; ScopedPointer connection; ScopedPointer scale; -#ifdef DPF_VST3_USING_HOST_RUN_LOOP + #if DPF_VST3_USING_HOST_RUN_LOOP ScopedPointer timer; -#endif + #endif ScopedPointer uivst3; // cached values v3_host_application** const hostApplication; @@ -1017,7 +1154,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { connection = nullptr; scale = nullptr; - #ifdef DPF_VST3_USING_HOST_RUN_LOOP + #if DPF_VST3_USING_HOST_RUN_LOOP timer = nullptr; #endif uivst3 = nullptr; @@ -1055,7 +1192,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return V3_OK; } -#ifndef DISTRHO_OS_MAC + #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", @@ -1068,7 +1205,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { *iface = &view->scale; return V3_OK; } -#endif + #endif d_stdout("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); @@ -1116,7 +1253,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { } } -#ifndef DISTRHO_OS_MAC + #ifndef DISTRHO_OS_MAC if (dpf_plugin_view_content_scale* const scale = view->scale) { if (const int refcount = scale->refcounter) @@ -1125,7 +1262,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount); } } -#endif + #endif if (unclean) return 0; @@ -1163,7 +1300,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { { if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) { - #ifdef DPF_VST3_USING_HOST_RUN_LOOP + #if DPF_VST3_USING_HOST_RUN_LOOP // find host run loop to plug ourselves into (required on some systems) DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG); @@ -1189,7 +1326,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { view->nextWidth = 0; view->nextHeight = 0; - #ifdef DPF_VST3_USING_HOST_RUN_LOOP + #if DPF_VST3_USING_HOST_RUN_LOOP // register a timer host run loop stuff view->timer = new dpf_timer_handler(view->uivst3); v3_cpp_obj(runloop)->register_timer(runloop, @@ -1210,7 +1347,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view* const view = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); - #ifdef DPF_VST3_USING_HOST_RUN_LOOP + #if DPF_VST3_USING_HOST_RUN_LOOP // unregister our timer as needed if (v3_run_loop** const runloop = view->runloop) { @@ -1297,6 +1434,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->getSize(rect); + // FIXME check if all this is really needed const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -1383,6 +1521,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->checkSizeConstraint(rect); + // FIXME check if all this is really needed const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; UIExporter tmpUI(nullptr, 0, view->sampleRate, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, From caa0fc9b70efbf6fd4fc16f027914e8dd2f76f27 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 20:21:02 +0100 Subject: [PATCH 341/504] VST3: unregister native callback before destroying window Signed-off-by: falkTX --- distrho/src/DistrhoUIVST3.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 154b98c6..f6697f2e 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -182,11 +182,8 @@ public: #endif } - ~NativeIdleCallback() + void unregisterNativeIdleCallback() { - if (!fCallbackRegistered) - return; - #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI fUI.removeIdleCallbackForVST3(this); #elif defined(DISTRHO_OS_MAC) @@ -277,6 +274,10 @@ public: ~UIVst3() { + #if !DPF_VST3_USING_HOST_RUN_LOOP + unregisterNativeIdleCallback(); + #endif + if (fConnection != nullptr) disconnect(); } From e623a70ac8313de9c8a48eba56b7d1c0aca35058 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 20:41:01 +0100 Subject: [PATCH 342/504] VST3: fix controller state never updated when separate Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 8 +++++++- distrho/src/DistrhoUIVST3.cpp | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 0257c14f..b1ec2ea8 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2056,11 +2056,17 @@ public: #if DISTRHO_PLUGIN_WANT_STATE if (std::strcmp(msgid, "state-set") == 0) { + const v3_result res = notify_state(attrs); + #if DPF_VST3_USES_SEPARATE_CONTROLLER + if (res != V3_OK) + return res; + + // notify component of the change DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR); return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message); #else - return notify_state(attrs); + return res; #endif } #endif diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index f6697f2e..34cac156 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -160,8 +160,8 @@ public: ); char suffix[9]; - std::snprintf(suffix, 8, "%08x", std::rand()); - suffix[8] = '\0'; + std::snprintf(suffix, sizeof(suffix), "%08x", std::rand()); + suffix[sizeof(suffix)-1] = '\0'; fTimerWindowClassName += suffix; WNDCLASSEX cls; From c3796f9a79d550c0e1b1be3f22635b2c8a23ec28 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 20:43:57 +0100 Subject: [PATCH 343/504] Cleanup Signed-off-by: falkTX --- examples/States/ExamplePluginStates.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/examples/States/ExamplePluginStates.cpp b/examples/States/ExamplePluginStates.cpp index 47a8e4dc..9808952c 100644 --- a/examples/States/ExamplePluginStates.cpp +++ b/examples/States/ExamplePluginStates.cpp @@ -104,11 +104,6 @@ The plugin will be treated as an effect, but it will not change the host audio." /* -------------------------------------------------------------------------------------------------------- * Init */ - /** - This plugin has no parameters. - */ - void initParameter(uint32_t, Parameter&) override {} - /** Set the name of the program @a index. This function will be called once, shortly after the plugin is created. @@ -180,12 +175,6 @@ The plugin will be treated as an effect, but it will not change the host audio." /* -------------------------------------------------------------------------------------------------------- * Internal data */ - /** - This plugin has no parameters.. - */ - void setParameterValue(uint32_t, float) override {} - float getParameterValue(uint32_t) const override { return 0.0f; } - /** Load a program. The host may call this function from any context, including realtime processing. From e512b25281af5887033dae4bfb1c10c40c18fde7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 20:50:39 +0100 Subject: [PATCH 344/504] VST3: Fix for hosts assuming string size includes null byte Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 4 ++-- distrho/src/DistrhoUIVST3.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index b1ec2ea8..269771f7 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2097,12 +2097,12 @@ public: int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1)); DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); - res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength); + res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*(keyLength+1)); DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, keyLength, res); if (valueLength != 0) { - res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength); + res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*(valueLength+1)); DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, valueLength, res); } diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 34cac156..2bd8a7b8 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -573,12 +573,12 @@ public: int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1)); DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); - res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength); + res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*(keyLength+1)); DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, keyLength, res); if (valueLength != 0) { - res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength); + res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*(valueLength+1)); DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, valueLength, res); } From 39cd4986bfa489fa43ef84cf05b4871d14710135 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 28 Mar 2022 21:14:46 +0100 Subject: [PATCH 345/504] Fix macOS build --- distrho/src/DistrhoUIVST3.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 2bd8a7b8..c490570b 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -21,9 +21,13 @@ #include "travesty/host.h" #include "travesty/view.h" -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI && defined(DISTRHO_OS_WINDOWS) -# include -# define DPF_VST3_WIN32_TIMER_ID 1 +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# if defined(DISTRHO_OS_MAC) +# include +# elif defined(DISTRHO_OS_WINDOWS) +# include +# define DPF_VST3_WIN32_TIMER_ID 1 +# endif #endif /* TODO items: From 1bab2267628ec5c58e8b41d9df4f346bbde3545a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 Apr 2022 02:50:12 +0100 Subject: [PATCH 346/504] Allow to pass winId to jack standalone, for special purposes Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 9f85875c..f4347698 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -123,11 +123,11 @@ class PluginJack #endif { public: - PluginJack(jack_client_t* const client) + PluginJack(jack_client_t* const client, const uintptr_t winId) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), #if DISTRHO_PLUGIN_HAS_UI fUI(this, - 0, // winId + winId, d_nextSampleRate, nullptr, // edit param setParameterValueCallback, @@ -1030,7 +1030,13 @@ int main(int argc, char* argv[]) } #endif - const PluginJack p(client); + uintptr_t winId = 0; +#if DISTRHO_PLUGIN_HAS_UI + if (argc == 3 && std::strcmp(argv[1], "embed") == 0) + winId = static_cast(std::atoll(argv[2])); +#endif + + const PluginJack p(client, winId); #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI /* the code below is based on From 347b5cf14f31428da2a33bfdc253db64ce0f9ff0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Apr 2022 20:45:30 +0100 Subject: [PATCH 347/504] Fix build warning Signed-off-by: falkTX --- distrho/src/DistrhoPluginJACK.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index f4347698..20ed6e5d 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -231,6 +231,9 @@ public: #else while (! gCloseSignalReceived) d_sleep(1); + + // unused + (void)winId; #endif } From c2f66ac3c7d62082d38cc806bd86e15cebb9c6a7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Apr 2022 21:34:49 +0100 Subject: [PATCH 348/504] Fix String compatibility with std::map Closes #371 Signed-off-by: falkTX --- distrho/extra/String.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index d9a91ca4..02383958 100644 --- a/distrho/extra/String.hpp +++ b/distrho/extra/String.hpp @@ -869,6 +869,12 @@ public: return operator+(str.fBuffer); } + // needed for std::map compatibility + bool operator<(const String& str) const noexcept + { + return std::strcmp(fBuffer, str.fBuffer) < 0; + } + // ------------------------------------------------------------------- private: From 5cd8449963d73af49f4cba5278f9c72f9a5d5121 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 12 Apr 2022 16:48:31 +0100 Subject: [PATCH 349/504] Fix missing EXTRA_LIBS in separate lv2 targets Signed-off-by: falkTX --- Makefile.plugins.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 74c3f475..2af556c6 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -436,12 +436,12 @@ endif $(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_LV2DSP) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin UI for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ # --------------------------------------------------------------------------------------------------------------------- # VST2 From d95936445f94a1a99b4256cd10c0860cda555891 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 19 Apr 2022 14:12:20 +0100 Subject: [PATCH 350/504] Allow GPLv2.0+ as license style field for LV2 export Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index c49f7fe4..3255b966 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -1028,6 +1028,7 @@ void lv2_generate_ttl(const char* const basename) else if (uplicense == "GPL-2.0-OR-LATER" || uplicense == "GPL2+" || uplicense == "GPLV2+" || + uplicense == "GPLV2.0+" || uplicense == "GPL V2+") { pluginString += " doap:license ;\n\n"; @@ -1041,6 +1042,7 @@ void lv2_generate_ttl(const char* const basename) else if (uplicense == "GPL-3.0-OR-LATER" || uplicense == "GPL3+" || uplicense == "GPLV3+" || + uplicense == "GPLV3.0+" || uplicense == "GPL V3+") { pluginString += " doap:license ;\n\n"; @@ -1093,11 +1095,11 @@ void lv2_generate_ttl(const char* const basename) // generic fallbacks else if (uplicense.startsWith("GPL")) { - pluginString += " doap:license ;\n\n"; + pluginString += " doap:license ;\n\n"; } else if (uplicense.startsWith("LGPL")) { - pluginString += " doap:license ;\n\n"; + pluginString += " doap:license ;\n\n"; } // unknown or not handled yet, log a warning From 3a74debff941f86fadbc083ed10e1f82bc84de13 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Apr 2022 02:30:07 +0100 Subject: [PATCH 351/504] Return 1 to effKeysRequired Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 6afa717f..341c3035 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -59,6 +59,7 @@ #define effGetProgramNameIndexed 29 #define effGetPlugCategory 35 #define effVendorSpecific 50 +#define effKeysRequired 57 #define effEditKeyDown 59 #define effEditKeyUp 60 #define kVstVersion 2400 @@ -761,6 +762,9 @@ public: break; # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + case effKeysRequired: + return 1; + case effEditKeyDown: if (fVstUI != nullptr) return fVstUI->handlePluginKeyEvent(true, index, value); From 48fc694c31f709d4a46f727795917fd3371eb0da Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Apr 2022 13:06:18 +0100 Subject: [PATCH 352/504] Cleanup unclear v3_funknown use Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 4 +- distrho/src/travesty/audio_processor.h | 14 ++++--- distrho/src/travesty/base.h | 56 ++++++++++++-------------- distrho/src/travesty/bstream.h | 5 ++- distrho/src/travesty/component.h | 5 ++- distrho/src/travesty/edit_controller.h | 11 +++-- distrho/src/travesty/events.h | 5 ++- distrho/src/travesty/factory.h | 11 +++-- distrho/src/travesty/host.h | 7 ++-- distrho/src/travesty/message.h | 11 +++-- distrho/src/travesty/view.h | 23 +++++++---- 11 files changed, 86 insertions(+), 66 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 269771f7..5f59091a 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2962,7 +2962,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_base - static v3_result V3_API initialize(void* const self, v3_plugin_base::v3_funknown** const context) + static v3_result V3_API initialize(void* const self, v3_funknown** const context) { dpf_edit_controller* const controller = *static_cast(self); @@ -3671,7 +3671,7 @@ struct dpf_component : v3_component_cpp { // ---------------------------------------------------------------------------------------------------------------- // v3_plugin_base - static v3_result V3_API initialize(void* const self, v3_plugin_base::v3_funknown** const context) + static v3_result V3_API initialize(void* const self, v3_funknown** const context) { dpf_component* const component = *static_cast(self); diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h index 85b2297d..8783d8e5 100644 --- a/distrho/src/travesty/audio_processor.h +++ b/distrho/src/travesty/audio_processor.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -90,8 +90,9 @@ struct v3_process_setup { */ struct v3_param_value_queue { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_param_id (V3_API* get_param_id)(void* self); int32_t (V3_API* get_point_count)(void* self); v3_result (V3_API* get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); @@ -102,8 +103,9 @@ static constexpr const v3_tuid v3_param_value_queue_iid = V3_ID(0x01263A18, 0xED074F6F, 0x98C9D356, 0x4686F9BA); struct v3_param_changes { +#ifndef __cplusplus struct v3_funknown; - +#endif int32_t (V3_API* get_param_count)(void* self); struct v3_param_value_queue** (V3_API* get_param_data)(void* self, int32_t idx); struct v3_param_value_queue** (V3_API* add_param_data)(void* self, v3_param_id* id, int32_t* index); @@ -181,8 +183,9 @@ enum { }; struct v3_process_context_requirements { +#ifndef __cplusplus struct v3_funknown; - +#endif uint32_t (V3_API* get_process_context_requirements)(void* self); }; @@ -222,8 +225,9 @@ struct v3_process_data { */ struct v3_audio_processor { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs); v3_result (V3_API* get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index c5890334..ebfb7497 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -24,34 +24,8 @@ * deal with C vs C++ differences */ -#ifdef __cplusplus - -/** - * cast object into its proper C++ type. - * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. - * - * we can use this as a little helper for keeping both C and C++ compatiblity. - * specialized templated calls are defined where required - * (that is, object inherits from something other than `v3_funknown`) - * - * example usage: `v3_cpp_obj(obj)->method(obj, args...);` - */ -template static inline -constexpr T* v3_cpp_obj(T** obj) -{ - /** - * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, - * but we need everything to be `static_cast` for it to be `constexpr` compatible. - */ - return static_cast(static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3)); -} - -#else - -# ifndef constexpr -# define constexpr -# endif - +#if !defined(__cplusplus) && !defined(constexpr) +# define constexpr #endif /** @@ -178,8 +152,9 @@ static constexpr const v3_tuid v3_funknown_iid = */ struct v3_plugin_base { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* initialize)(void* self, struct v3_funknown** context); v3_result (V3_API* terminate)(void* self); }; @@ -189,6 +164,27 @@ static constexpr const v3_tuid v3_plugin_base_iid = #ifdef __cplusplus +/** + * cast object into its proper C++ type. + * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. + * + * we can use this as a little helper for keeping both C and C++ compatiblity. + * specialized templated calls are defined where required + * (that is, object inherits from something other than `v3_funknown`) + * + * example usage: `v3_cpp_obj(obj)->method(obj, args...);` + */ + +template static inline +constexpr T* v3_cpp_obj(T** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast(static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3)); +} + /** * helper C++ functions to manually call v3_funknown methods on an object. */ diff --git a/distrho/src/travesty/bstream.h b/distrho/src/travesty/bstream.h index a1bfcd3f..92bbc3b4 100644 --- a/distrho/src/travesty/bstream.h +++ b/distrho/src/travesty/bstream.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -25,8 +25,9 @@ enum v3_seek_mode { }; struct v3_bstream { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API *read)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_read); v3_result (V3_API *write)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_written); v3_result (V3_API *seek)(void* self, int64_t pos, int32_t seek_mode, int64_t* result); diff --git a/distrho/src/travesty/component.h b/distrho/src/travesty/component.h index 4e73c477..652da36e 100644 --- a/distrho/src/travesty/component.h +++ b/distrho/src/travesty/component.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -89,8 +89,9 @@ struct v3_bus_info { struct v3_routing_info; struct v3_component { +#ifndef __cplusplus struct v3_plugin_base; - +#endif v3_result (V3_API *get_controller_class_id)(void* self, v3_tuid class_id); v3_result (V3_API *set_io_mode)(void* self, int32_t io_mode); int32_t (V3_API *get_bus_count)(void* self, int32_t media_type, int32_t bus_direction); diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index 9edb151a..fbe48193 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -40,8 +40,9 @@ enum { }; struct v3_component_handler { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* begin_edit)(void* self, v3_param_id); v3_result (V3_API* perform_edit)(void* self, v3_param_id, double value_normalised); v3_result (V3_API* end_edit)(void* self, v3_param_id); @@ -77,8 +78,9 @@ struct v3_param_info { }; struct v3_edit_controller { +#ifndef __cplusplus struct v3_plugin_base; - +#endif v3_result (V3_API* set_component_state)(void* self, struct v3_bstream**); v3_result (V3_API* set_state)(void* self, struct v3_bstream**); v3_result (V3_API* get_state)(void* self, struct v3_bstream**); @@ -102,8 +104,9 @@ static constexpr const v3_tuid v3_edit_controller_iid = */ struct v3_midi_mapping { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* get_midi_controller_assignment)(void* self, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id); }; diff --git a/distrho/src/travesty/events.h b/distrho/src/travesty/events.h index 72a4830a..73798d9f 100644 --- a/distrho/src/travesty/events.h +++ b/distrho/src/travesty/events.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -132,8 +132,9 @@ struct v3_event { */ struct v3_event_list { +#ifndef __cplusplus struct v3_funknown; - +#endif uint32_t (V3_API* get_event_count)(void* self); v3_result (V3_API* get_event)(void* self, int32_t idx, struct v3_event* event); v3_result (V3_API* add_event)(void* self, struct v3_event* event); diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index adad83cd..1b14f4ba 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -37,8 +37,9 @@ struct v3_class_info { }; struct v3_plugin_factory { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API *get_factory_info)(void* self, struct v3_factory_info*); int32_t (V3_API *num_classes)(void* self); v3_result (V3_API *get_class_info)(void* self, int32_t idx, struct v3_class_info*); @@ -70,8 +71,9 @@ struct v3_class_info_2 { }; struct v3_plugin_factory_2 { +#ifndef __cplusplus struct v3_plugin_factory; - +#endif v3_result (V3_API *get_class_info_2)(void* self, int32_t idx, struct v3_class_info_2*); }; @@ -98,8 +100,9 @@ struct v3_class_info_3 { }; struct v3_plugin_factory_3 { +#ifndef __cplusplus struct v3_plugin_factory_2; - +#endif v3_result (V3_API *get_class_info_utf16)(void* self, int32_t idx, struct v3_class_info_3*); v3_result (V3_API *set_host_context)(void* self, struct v3_funknown** host); }; diff --git a/distrho/src/travesty/host.h b/distrho/src/travesty/host.h index 032a7eed..bb6566bd 100644 --- a/distrho/src/travesty/host.h +++ b/distrho/src/travesty/host.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -21,12 +21,13 @@ #include "align_push.h" /** - * connection point + * host application */ struct v3_host_application { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* get_name)(void* self, v3_str_128 name); // wtf? v3_result (V3_API* create_instance)(void* self, v3_tuid cid, v3_tuid iid, void** obj); }; diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h index 3cd77a68..3ab8f461 100644 --- a/distrho/src/travesty/message.h +++ b/distrho/src/travesty/message.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -25,8 +25,9 @@ */ struct v3_attribute_list { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* set_int)(void* self, const char* id, int64_t value); v3_result (V3_API* get_int)(void* self, const char* id, int64_t* value); v3_result (V3_API* set_float)(void* self, const char* id, double value); @@ -45,8 +46,9 @@ static constexpr const v3_tuid v3_attribute_list_iid = */ struct v3_message { +#ifndef __cplusplus struct v3_funknown; - +#endif const char* (V3_API* get_message_id)(void* self); void (V3_API* set_message_id)(void* self, const char* id); v3_attribute_list** (V3_API* get_attributes)(void* self); @@ -60,8 +62,9 @@ static constexpr const v3_tuid v3_message_iid = */ struct v3_connection_point { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* connect)(void* self, struct v3_connection_point** other); v3_result (V3_API* disconnect)(void* self, struct v3_connection_point** other); v3_result (V3_API* notify)(void* self, struct v3_message** message); diff --git a/distrho/src/travesty/view.h b/distrho/src/travesty/view.h index 0abc3be1..2f5c698d 100644 --- a/distrho/src/travesty/view.h +++ b/distrho/src/travesty/view.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -48,8 +48,9 @@ struct v3_view_rect { struct v3_plugin_frame; struct v3_plugin_view { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* is_platform_type_supported)(void* self, const char* platform_type); v3_result (V3_API* attached)(void* self, void* parent, const char* platform_type); v3_result (V3_API* removed)(void* self); @@ -72,8 +73,9 @@ static constexpr const v3_tuid v3_plugin_view_iid = */ struct v3_plugin_frame { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* resize_view)(void* self, struct v3_plugin_view**, struct v3_view_rect*); }; @@ -86,8 +88,9 @@ static constexpr const v3_tuid v3_plugin_frame_iid = */ struct v3_plugin_view_content_scale { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* set_content_scale_factor)(void* self, float factor); }; @@ -99,8 +102,9 @@ static constexpr const v3_tuid v3_plugin_view_content_scale_iid = */ struct v3_plugin_view_parameter_finder { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* find_parameter)(void* self, int32_t x, int32_t y, v3_param_id *); }; @@ -112,8 +116,9 @@ static constexpr const v3_tuid v3_plugin_view_parameter_finder_iid = */ struct v3_event_handler { +#ifndef __cplusplus struct v3_funknown; - +#endif void (V3_API* on_fd_is_set)(void* self, int fd); }; @@ -125,8 +130,9 @@ static constexpr const v3_tuid v3_event_handler_iid = */ struct v3_timer_handler { +#ifndef __cplusplus struct v3_funknown; - +#endif void (V3_API* on_timer)(void* self); }; @@ -138,8 +144,9 @@ static constexpr const v3_tuid v3_timer_handler_iid = */ struct v3_run_loop { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* register_event_handler)(void* self, v3_event_handler** handler, int fd); v3_result (V3_API* unregister_event_handler)(void* self, v3_event_handler** handler); v3_result (V3_API* register_timer)(void* self, v3_timer_handler** handler, uint64_t ms); From 68de732eecbd1d8febf94e15558c5adaa45dfa9b Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Apr 2022 13:32:59 +0100 Subject: [PATCH 353/504] Fix vst3 InitDll function on windows Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 5f59091a..5511dea2 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -4199,19 +4199,22 @@ const void* GetPluginFactory(void) #if defined(DISTRHO_OS_MAC) # define ENTRYFNNAME bundleEntry +# define ENTRYFNNAMEARGS void* # define EXITFNNAME bundleExit #elif defined(DISTRHO_OS_WINDOWS) # define ENTRYFNNAME InitDll +# define ENTRYFNNAMEARGS void # define EXITFNNAME ExitDll #else # define ENTRYFNNAME ModuleEntry +# define ENTRYFNNAMEARGS void* # define EXITFNNAME ModuleExit #endif DISTRHO_PLUGIN_EXPORT -bool ENTRYFNNAME(void*); +bool ENTRYFNNAME(ENTRYFNNAMEARGS); -bool ENTRYFNNAME(void*) +bool ENTRYFNNAME(ENTRYFNNAMEARGS) { USE_NAMESPACE_DISTRHO; From d3d8f910e35142b5289c073a36ef1c23becc2e46 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 1 May 2022 01:36:23 +0100 Subject: [PATCH 354/504] Revert "Return 1 to effKeysRequired" This reverts commit 3a74debff941f86fadbc083ed10e1f82bc84de13. --- distrho/src/DistrhoPluginVST2.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 341c3035..6afa717f 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -59,7 +59,6 @@ #define effGetProgramNameIndexed 29 #define effGetPlugCategory 35 #define effVendorSpecific 50 -#define effKeysRequired 57 #define effEditKeyDown 59 #define effEditKeyUp 60 #define kVstVersion 2400 @@ -762,9 +761,6 @@ public: break; # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - case effKeysRequired: - return 1; - case effEditKeyDown: if (fVstUI != nullptr) return fVstUI->handlePluginKeyEvent(true, index, value); From 40b6db5b964fdd17cc23236b3eb9c2eb00105773 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 1 May 2022 08:46:43 +0200 Subject: [PATCH 355/504] Update travesty --- distrho/src/travesty/edit_controller.h | 13 +++++ distrho/src/travesty/unit.h | 69 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 distrho/src/travesty/unit.h diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h index fbe48193..c83a285b 100644 --- a/distrho/src/travesty/edit_controller.h +++ b/distrho/src/travesty/edit_controller.h @@ -52,6 +52,19 @@ struct v3_component_handler { static constexpr const v3_tuid v3_component_handler_iid = V3_ID(0x93A0BEA3, 0x0BD045DB, 0x8E890B0C, 0xC1E46AC6); +struct v3_component_handler2 { +#ifndef __cplusplus + struct v3_funknown; +#endif + v3_result (V3_API* set_dirty)(void* self, v3_bool state); + v3_result (V3_API* request_open_editor)(void* self, const char* name); + v3_result (V3_API* start_group_edit)(void* self); + v3_result (V3_API* finish_group_edit)(void* self); +}; + +static constexpr const v3_tuid v3_component_handler2_iid = + V3_ID(0xF040B4B3, 0xA36045EC, 0xABCDC045, 0xB4D5A2CC); + /** * edit controller */ diff --git a/distrho/src/travesty/unit.h b/distrho/src/travesty/unit.h new file mode 100644 index 00000000..77bc9adc --- /dev/null +++ b/distrho/src/travesty/unit.h @@ -0,0 +1,69 @@ +/* + * travesty, pure C VST3-compatible interface + * Copyright (C) 2021-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include "base.h" + +#include "align_push.h" + +struct v3_unit_info { + int32_t id; // 0 for root unit + int32_t parent_unit_id; // -1 for none + v3_str_128 name; + int32_t program_list_id; // -1 for none +}; + +struct v3_program_list_info { + int32_t id; + v3_str_128 name; + int32_t programCount; +}; + +struct v3_unit_information { +#ifndef __cplusplus + struct v3_funknown; +#endif + int32_t (V3_API* get_unit_count)(void* self); + v3_result (V3_API* get_unit_info)(void* self, int32_t unit_idx, v3_unit_info* info); + int32_t (V3_API* get_program_list_count)(void* self); + v3_result (V3_API* get_program_list_info)(void* self, int32_t list_idx, v3_program_list_info* info); + v3_result (V3_API* get_program_name)(void* self, int32_t list_id, int32_t program_idx, v3_str_128 name); + v3_result (V3_API* get_program_info)(void* self, int32_t list_id, int32_t program_idx, const char *attribute_id, v3_str_128 attribute_value); + v3_result (V3_API* has_program_pitch_names)(void* self, int32_t list_id, int32_t program_idx); + v3_result (V3_API* get_program_pitch_name)(void* self, int32_t list_id, int32_t program_idx, int16_t midi_pitch, v3_str_128 name); + int32_t (V3_API* get_selected_unit)(void* self); + v3_result (V3_API* select_unit)(void* self, int32_t unit_id); + v3_result (V3_API* get_unit_by_bus)(void* self, int32_t type, int32_t bus_direction, int32_t bus_idx, int32_t channel, int32_t* unit_id); + v3_result (V3_API* set_unit_program_data)(void* self, int32_t list_or_unit_id, int32_t program_idx, struct v3_bstream** data); +}; + +static constexpr const v3_tuid v3_unit_information_iid = + V3_ID(0x3D4BD6B5, 0x913A4FD2, 0xA886E768, 0xA5EB92C1); + +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_unit_information_cpp : v3_funknown { + v3_unit_information unit; +}; + +#endif + +#include "align_pop.h" From fba127ec61b9bb4e81c16f6e981b0fb699cb3682 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 4 May 2022 13:19:20 +0100 Subject: [PATCH 356/504] Fix potential windows build issue Signed-off-by: falkTX --- dgl/src/ApplicationPrivateData.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dgl/src/ApplicationPrivateData.hpp b/dgl/src/ApplicationPrivateData.hpp index 40fbc1a5..0e33c985 100644 --- a/dgl/src/ApplicationPrivateData.hpp +++ b/dgl/src/ApplicationPrivateData.hpp @@ -22,6 +22,9 @@ #include #ifdef DISTRHO_OS_WINDOWS +# ifndef NOMINMAX +# define NOMINMAX +# endif # include # include typedef HANDLE d_ThreadHandle; From 793b546e33aa59e4db90d0c83d303801f1a2fd79 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 4 May 2022 13:25:57 +0100 Subject: [PATCH 357/504] Fix build under wine Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 6 +++--- dgl/src/pugl.cpp | 14 ++++++++------ dgl/src/pugl.hpp | 14 ++++++++------ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 6a9467d5..b1bfc7c5 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -378,7 +378,7 @@ void Window::PrivateData::focus() if (! isEmbed) puglRaiseWindow(view); -#ifdef HAVE_X11 +#if defined(HAVE_X11) && !defined(DISTRHO_OS_MAC) && !defined(DISTRHO_OS_WINDOWS) puglX11GrabFocus(view); #else puglGrabFocus(view); @@ -765,7 +765,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< View created, a #PuglEventCreate case PUGL_CREATE: -#ifdef HAVE_X11 +#if defined(HAVE_X11) && !defined(DISTRHO_OS_MAC) && !defined(DISTRHO_OS_WINDOWS) if (! pData->isEmbed) puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); #endif diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 5492493f..ae99221c 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -459,7 +459,8 @@ void puglFallbackOnResize(PuglView* const view) #endif } -#ifdef DISTRHO_OS_MAC +#if defined(DISTRHO_OS_MAC) + // -------------------------------------------------------------------------------------------------------------------- // macOS specific, allow standalone window to gain focus @@ -537,9 +538,9 @@ void puglMacOSShowCentered(PuglView* const view) } // -------------------------------------------------------------------------------------------------------------------- -#endif -#ifdef DISTRHO_OS_WINDOWS +#elif defined(DISTRHO_OS_WINDOWS) + // -------------------------------------------------------------------------------------------------------------------- // win32 specific, call ShowWindow with SW_RESTORE @@ -616,9 +617,9 @@ void puglWin32SetWindowResizable(PuglView* const view, const bool resizable) } // -------------------------------------------------------------------------------------------------------------------- -#endif -#ifdef HAVE_X11 +#elif defined(HAVE_X11) + // -------------------------------------------------------------------------------------------------------------------- // X11 specific, safer way to grab focus @@ -666,6 +667,7 @@ void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandal } // -------------------------------------------------------------------------------------------------------------------- + #endif // HAVE_X11 #ifndef DISTRHO_OS_MAC diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index afa0cf05..30613ea6 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -99,7 +99,8 @@ puglOnDisplayPrepare(PuglView* view); PUGL_API void puglFallbackOnResize(PuglView* view); -#ifdef DISTRHO_OS_MAC +#if defined(DISTRHO_OS_MAC) + // macOS specific, allow standalone window to gain focus PUGL_API void puglMacOSActivateApp(); @@ -115,9 +116,9 @@ puglMacOSRemoveChildWindow(PuglView* view, PuglView* child); // macOS specific, center view based on parent coordinates (if there is one) PUGL_API void puglMacOSShowCentered(PuglView* view); -#endif -#ifdef DISTRHO_OS_WINDOWS +#elif defined(DISTRHO_OS_WINDOWS) + // win32 specific, call ShowWindow with SW_RESTORE PUGL_API void puglWin32RestoreWindow(PuglView* view); @@ -129,9 +130,9 @@ puglWin32ShowCentered(PuglView* view); // win32 specific, set or unset WS_SIZEBOX style flag PUGL_API void puglWin32SetWindowResizable(PuglView* view, bool resizable); -#endif -#ifdef HAVE_X11 +#elif defined(HAVE_X11) + // X11 specific, safer way to grab focus PUGL_API PuglStatus puglX11GrabFocus(const PuglView* view); @@ -139,6 +140,7 @@ puglX11GrabFocus(const PuglView* view); // X11 specific, set dialog window type and pid hints PUGL_API void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); + #endif PUGL_END_DECLS From 7d4e299e4397fcdf7cf307c97897678550e60c00 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 4 May 2022 16:36:33 +0200 Subject: [PATCH 358/504] Fix compat with old macOS --- distrho/extra/FileBrowserDialog.cpp | 5 +++++ distrho/src/DistrhoDefines.h | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialog.cpp index 195b4a8a..df109341 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialog.cpp @@ -303,6 +303,10 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, ScopedPointer handle(new FileBrowserData(options.saving)); #ifdef DISTRHO_OS_MAC +# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_8 + // unsupported + return nullptr; +# else NSSavePanel* const nsBasePanel = handle->nsBasePanel; DISTRHO_SAFE_ASSERT_RETURN(nsBasePanel != nullptr, nullptr); @@ -349,6 +353,7 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, } }]; }); +# endif #endif #ifdef DISTRHO_OS_WINDOWS diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index 929593c2..94c6a2a4 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -90,9 +90,9 @@ #endif /* Define DISTRHO_DEPRECATED_BY */ -#if defined(__clang__) && defined(DISTRHO_PROPER_CPP11_SUPPORT) +#if defined(__clang__) && (__clang_major__ * 100 + __clang_minor__) >= 502 # define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("", other))) -#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 480 +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 # define DISTRHO_DEPRECATED_BY(other) __attribute__((deprecated("Use " other))) #else # define DISTRHO_DEPRECATED_BY(other) DISTRHO_DEPRECATED From cd1861ae893c21045e9f47245bf41cabca7d5f2f Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 7 May 2022 12:45:04 +0100 Subject: [PATCH 359/504] Set SDK version as needed for some vst3 hosts Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 5511dea2..f6ccabb8 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -4131,7 +4131,7 @@ struct dpf_factory : v3_plugin_factory_cpp { DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); - DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); + DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version)); return V3_OK; } @@ -4154,7 +4154,7 @@ struct dpf_factory : v3_plugin_factory_cpp { DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy_utf16(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); - DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version)); + DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version)); return V3_OK; } From 081d4d7392ef587f46dc3b216369655828bbda7f Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 12 May 2022 09:42:19 +0100 Subject: [PATCH 360/504] Tell vst2 hosts that offline rendering APIs are not supported --- distrho/src/DistrhoPluginVST2.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 6afa717f..86a020bd 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1019,6 +1019,8 @@ public: #else return -1; #endif + if (std::strcmp(canDo, "offline") == 0) + return -1; } break; From dba284c267c74b12338a576c609885553c19552e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 14 May 2022 19:27:11 +0100 Subject: [PATCH 361/504] Fix some compiler warnings Signed-off-by: falkTX --- dgl/Cairo.hpp | 2 +- dgl/NanoVG.hpp | 2 +- dgl/OpenGL.hpp | 6 +++--- dgl/SubWidget.hpp | 2 +- dgl/TopLevelWidget.hpp | 2 +- distrho/DistrhoUI.hpp | 2 +- distrho/src/DistrhoUIPrivateData.hpp | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index 567aa63f..cef76734 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -151,7 +151,7 @@ public: /** Destructor. */ - virtual ~CairoBaseWidget() {} + ~CairoBaseWidget() override {} protected: /** diff --git a/dgl/NanoVG.hpp b/dgl/NanoVG.hpp index e18074f5..bae8f27a 100644 --- a/dgl/NanoVG.hpp +++ b/dgl/NanoVG.hpp @@ -940,7 +940,7 @@ public: /** Destructor. */ - virtual ~NanoBaseWidget() {} + ~NanoBaseWidget() override {} protected: /** diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index 629d831f..d69ca210 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -238,11 +238,11 @@ public: // FIXME this should not be needed inline void loadFromMemory(const char* rdata, uint w, uint h, ImageFormat fmt = kImageFormatBGRA) - { loadFromMemory(rdata, Size(w, h), fmt); }; + { loadFromMemory(rdata, Size(w, h), fmt); } inline void draw(const GraphicsContext& context) - { drawAt(context, Point(0, 0)); }; + { drawAt(context, Point(0, 0)); } inline void drawAt(const GraphicsContext& context, int x, int y) - { drawAt(context, Point(x, y)); }; + { drawAt(context, Point(x, y)); } /** Constructor using raw image data, specifying an OpenGL image format. diff --git a/dgl/SubWidget.hpp b/dgl/SubWidget.hpp index eab84b74..eefb4c5a 100644 --- a/dgl/SubWidget.hpp +++ b/dgl/SubWidget.hpp @@ -47,7 +47,7 @@ public: /** Destructor. */ - virtual ~SubWidget(); + ~SubWidget() override; /** Check if this widget contains the point defined by @a x and @a y. diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 5e53784e..a9438552 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -54,7 +54,7 @@ public: /** Destructor. */ - virtual ~TopLevelWidget(); + ~TopLevelWidget() override; /** Get the application associated with this top-level widget's window. diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index e22a656b..1526d302 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -89,7 +89,7 @@ public: /** Destructor. */ - virtual ~UI(); + ~UI() override; /* -------------------------------------------------------------------------------------------------------- * Host state */ diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 5d56586b..ce031e12 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -199,7 +199,7 @@ public: puglBackendEnter(pData->view); } - ~PluginWindow() + ~PluginWindow() override { if (pData->view != nullptr) puglBackendLeave(pData->view); From e3341cb4225dbd0c8d974b0defa1ab6c5339c7f3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 21 May 2022 20:09:29 +0100 Subject: [PATCH 362/504] Tweak travesty headers as needed by hosts for plugin scanning Signed-off-by: falkTX --- distrho/src/travesty/base.h | 14 ++++++++++++++ distrho/src/travesty/events.h | 12 ++++++++++++ distrho/src/travesty/factory.h | 22 ++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index ebfb7497..ab5245d6 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -207,4 +207,18 @@ uint32_t v3_cpp_obj_unref(T** obj) return static_cast(static_cast(*obj))->unref(obj); } +template static inline +v3_result v3_cpp_obj_initialize(T** obj, v3_funknown** context) +{ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3))->initialize(obj, context); +} + +template static inline +v3_result v3_cpp_obj_terminate(T** obj) +{ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3))->terminate(obj); +} + #endif diff --git a/distrho/src/travesty/events.h b/distrho/src/travesty/events.h index 73798d9f..9c83bf84 100644 --- a/distrho/src/travesty/events.h +++ b/distrho/src/travesty/events.h @@ -143,4 +143,16 @@ struct v3_event_list { static constexpr const v3_tuid v3_event_list_iid = V3_ID(0x3A2C4214, 0x346349FE, 0xB2C4F397, 0xB9695A44); +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_event_list_cpp : v3_funknown { + v3_event_list list; +}; + +#endif + #include "align_pop.h" diff --git a/distrho/src/travesty/factory.h b/distrho/src/travesty/factory.h index 1b14f4ba..055b707d 100644 --- a/distrho/src/travesty/factory.h +++ b/distrho/src/travesty/factory.h @@ -122,4 +122,26 @@ struct v3_plugin_factory_cpp : v3_funknown { v3_plugin_factory_3 v3; }; +template<> inline +constexpr v3_plugin_factory_2* v3_cpp_obj(v3_plugin_factory_2** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*7)); +} + +template<> inline +constexpr v3_plugin_factory_3* v3_cpp_obj(v3_plugin_factory_3** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*8)); +} + #endif From 98b395642457012419f87ac4a655bd07326d8024 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 25 May 2022 03:09:39 +0100 Subject: [PATCH 363/504] Implement VST3 getBusArrangement, cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 120 ++++++++++++++++++++++++------ 1 file changed, 99 insertions(+), 21 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index f6ccabb8..3b9d9c8b 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -499,7 +499,7 @@ public: { #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 int32_t numChannels; - v3_bus_flags flags; + uint32_t flags; v3_bus_types busType; v3_str_128 busName = {}; @@ -522,7 +522,7 @@ public: { numChannels = inputBuses.numSidechain; busType = V3_AUX; - flags = static_cast(0); + flags = 0; break; } // fall-through @@ -575,7 +575,7 @@ public: { numChannels = outputBuses.numSidechain; busType = V3_AUX; - flags = static_cast(0); + flags = 0; break; } // fall-through @@ -1007,17 +1007,73 @@ public: return V3_NOT_IMPLEMENTED; } - v3_result getBusArrangement(const int32_t direction, const int32_t /*idx*/, v3_speaker_arrangement*) + v3_result getBusArrangement(const int32_t busDirection, const int32_t busId, v3_speaker_arrangement* const speaker) const noexcept { - switch (direction) + DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_INT_RETURN(busId >= 0, busId, V3_INVALID_ARG); + + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + const uint32_t ubusId = static_cast(busId); + #endif + + if (busDirection == V3_INPUT) { - case V3_INPUT: - case V3_OUTPUT: - // TODO - return V3_NOT_IMPLEMENTED; + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; i 0 + for (uint32_t i=0; i %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_midi_mapping midi_mapping; static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; *iface = &midi_mapping_ptr; return V3_OK; - } +#else + d_stdout("query_interface_edit_controller => %p %s %p | reject unused", self, tuid2str(iid), iface); + *iface = nullptr; + return V3_NO_INTERFACE; #endif + } -#if DPF_VST3_USES_SEPARATE_CONTROLLER if (v3_tuid_match(iid, v3_connection_point_iid)) { +#if DPF_VST3_USES_SEPARATE_CONTROLLER d_stdout("query_interface_edit_controller => %p %s %p | OK convert %p", self, tuid2str(iid), iface, controller->connectionComp2Ctrl.get()); @@ -2900,11 +2960,14 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ++controller->connectionComp2Ctrl->refcounter; *iface = &controller->connectionComp2Ctrl; return V3_OK; - } +#else + d_stdout("query_interface_edit_controller => %p %s %p | reject unwanted", self, tuid2str(iid), iface); + *iface = nullptr; + return V3_NO_INTERFACE; #endif + } d_stdout("query_interface_edit_controller => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); - *iface = nullptr; return V3_NO_INTERFACE; } @@ -3423,6 +3486,8 @@ struct dpf_audio_processor : v3_audio_processor_cpp { PluginVst3* const vst3 = processor->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); + d_stdout("dpf_audio_processor::setup_processing => %p %p | %d %f", self, setup, setup->max_block_size, setup->sample_rate); + d_nextBufferSize = setup->max_block_size; d_nextSampleRate = setup->sample_rate; return processor->vst3->setupProcessing(setup); @@ -3542,16 +3607,20 @@ struct dpf_component : v3_component_cpp { return V3_OK; } -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (v3_tuid_match(iid, v3_midi_mapping_iid)) { +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT d_stdout("query_interface_component => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_midi_mapping midi_mapping; static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; *iface = &midi_mapping_ptr; return V3_OK; - } +#else + d_stdout("query_interface_component => %p %s %p | reject unused", self, tuid2str(iid), iface); + *iface = nullptr; + return V3_NO_INTERFACE; #endif + } if (v3_tuid_match(iid, v3_audio_processor_iid)) { @@ -3566,9 +3635,9 @@ struct dpf_component : v3_component_cpp { return V3_OK; } -#if DPF_VST3_USES_SEPARATE_CONTROLLER if (v3_tuid_match(iid, v3_connection_point_iid)) { +#if DPF_VST3_USES_SEPARATE_CONTROLLER d_stdout("query_interface_component => %p %s %p | OK convert %p", self, tuid2str(iid), iface, component->connectionComp2Ctrl.get()); @@ -3578,10 +3647,16 @@ struct dpf_component : v3_component_cpp { ++component->connectionComp2Ctrl->refcounter; *iface = &component->connectionComp2Ctrl; return V3_OK; - } #else + d_stdout("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); + *iface = nullptr; + return V3_NO_INTERFACE; +#endif + } + if (v3_tuid_match(iid, v3_edit_controller_iid)) { +#if !DPF_VST3_USES_SEPARATE_CONTROLLER d_stdout("query_interface_component => %p %s %p | OK convert %p", self, tuid2str(iid), iface, component->controller.get()); @@ -3593,11 +3668,14 @@ struct dpf_component : v3_component_cpp { ++component->controller->refcounter; *iface = &component->controller; return V3_OK; - } +#else + d_stdout("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); + *iface = nullptr; + return V3_NO_INTERFACE; #endif + } d_stdout("query_interface_component => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); - *iface = nullptr; return V3_NO_INTERFACE; } From 1357e37f2c260e4bee11f5bb673ea85c3af52a06 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 25 May 2022 03:23:15 +0100 Subject: [PATCH 364/504] Make sure speaker pointer is not null Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 3b9d9c8b..6e5665ed 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1011,6 +1011,7 @@ public: { DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_INT_RETURN(busId >= 0, busId, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(speaker != nullptr, V3_INVALID_ARG); #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 const uint32_t ubusId = static_cast(busId); @@ -1071,7 +1072,7 @@ public: return V3_OK; } #endif // DISTRHO_PLUGIN_NUM_OUTPUTS - d_stdout("invalid bus %d", busId); + d_stdout("invalid bus arrangement %d", busId); return V3_INVALID_ARG; } } @@ -2683,7 +2684,7 @@ struct dpf_comp2ctrl_connection_point : v3_connection_point_cpp { #if DISTRHO_PLUGIN_HAS_UI // -------------------------------------------------------------------------------------------------------------------- -// dpf_comp2ctrl_connection_point +// dpf_ctrl2view_connection_point struct dpf_ctrl2view_connection_point : v3_connection_point_cpp { ScopedPointer& vst3; From 4bcb4d4dc8d665dc7119f74f3a826c99c5798191 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 25 May 2022 05:05:09 +0100 Subject: [PATCH 365/504] More tweaks to getBusArrangement, add stereo tag to example meter Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 48 +++++++++++++++++++++---- examples/Meters/ExamplePluginMeters.cpp | 13 +++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 6e5665ed..00ede581 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1027,19 +1027,37 @@ public: if (port.busId != ubusId) continue; + v3_speaker_arrangement arr; + switch (port.groupId) { case kPortGroupMono: - *speaker = V3_SPEAKER_M; + arr = V3_SPEAKER_M; break; case kPortGroupStereo: - *speaker = V3_SPEAKER_L | V3_SPEAKER_R; + arr = V3_SPEAKER_L | V3_SPEAKER_R; break; default: - *speaker = 0; + if (inputBuses.audio != 0 && ubusId == 0) + { + arr = 0x0; + for (uint32_t j=0; j Date: Wed, 25 May 2022 16:15:05 +0100 Subject: [PATCH 366/504] Fix standard LV2 port group definitions Signed-off-by: falkTX --- distrho/src/DistrhoPluginLV2export.cpp | 88 ++++++++++++-------------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 3255b966..a42a50f9 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -472,20 +472,23 @@ void lv2_generate_ttl(const char* const basename) if (port.hints & kAudioPortIsSidechain) pluginString += " lv2:portProperty lv2:isSideChain;\n"; - switch (port.groupId) + if (port.groupId != kPortGroupNone) { - case kPortGroupNone: - break; - case kPortGroupMono: - pluginString += " pg:group pg:MonoGroup ;\n"; - break; - case kPortGroupStereo: - pluginString += " pg:group pg:StereoGroup ;\n"; - break; - default: pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; - break; + + switch (port.groupId) + { + case kPortGroupMono: + pluginString += " lv2:designation pg:center ;\n"; + break; + case kPortGroupStereo: + if (i == 1) + pluginString += " lv2:designation pg:right ;\n"; + else + pluginString += " lv2:designation pg:left ;\n"; + break; + } } // set ranges @@ -562,20 +565,23 @@ void lv2_generate_ttl(const char* const basename) if (port.hints & kAudioPortIsSidechain) pluginString += " lv2:portProperty lv2:isSideChain;\n"; - switch (port.groupId) + if (port.groupId != kPortGroupNone) { - case kPortGroupNone: - break; - case kPortGroupMono: - pluginString += " pg:group pg:MonoGroup ;\n"; - break; - case kPortGroupStereo: - pluginString += " pg:group pg:StereoGroup ;\n"; - break; - default: pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" + plugin.getPortGroupSymbolForId(port.groupId) + "> ;\n"; - break; + + switch (port.groupId) + { + case kPortGroupMono: + pluginString += " lv2:designation pg:center ;\n"; + break; + case kPortGroupStereo: + if (i == 1) + pluginString += " lv2:designation pg:right ;\n"; + else + pluginString += " lv2:designation pg:left ;\n"; + break; + } } // set ranges @@ -901,21 +907,9 @@ void lv2_generate_ttl(const char* const basename) // group const uint32_t groupId = plugin.getParameterGroupId(i); - switch (groupId) - { - case kPortGroupNone: - break; - case kPortGroupMono: - pluginString += " pg:group pg:MonoGroup ;\n"; - break; - case kPortGroupStereo: - pluginString += " pg:group pg:StereoGroup ;\n"; - break; - default: + if (groupId != kPortGroupNone) pluginString += " pg:group <" DISTRHO_PLUGIN_URI "#portGroup_" + plugin.getPortGroupSymbolForId(groupId) + "> ;\n"; - break; - } } // ! designated @@ -1155,13 +1149,6 @@ void lv2_generate_ttl(const char* const basename) DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.groupId != kPortGroupNone); DISTRHO_SAFE_ASSERT_CONTINUE(portGroup.symbol.isNotEmpty()); - switch (portGroup.groupId) - { - case kPortGroupMono: - case kPortGroupStereo: - continue; - } - pluginString += "\n<" DISTRHO_PLUGIN_URI "#portGroup_" + portGroup.symbol + ">\n"; isInput = isOutput = false; @@ -1185,19 +1172,28 @@ void lv2_generate_ttl(const char* const basename) } pluginString += " a "; + if (isInput && !isOutput) pluginString += "pg:InputGroup"; else if (isOutput && !isInput) pluginString += "pg:OutputGroup"; else pluginString += "pg:Group"; + + switch (portGroup.groupId) + { + case kPortGroupMono: + pluginString += " , pg:MonoGroup"; + break; + case kPortGroupStereo: + pluginString += " , pg:StereoGroup"; + break; + } + pluginString += " ;\n"; -#if 0 - pluginString += " rdfs:label \"" + portGroup.name + "\" ;\n"; -#else + // pluginString += " rdfs:label \"" + portGroup.name + "\" ;\n"; pluginString += " lv2:name \"" + portGroup.name + "\" ;\n"; -#endif pluginString += " lv2:symbol \"" + portGroup.symbol + "\" .\n"; } } From f9e6b101a1b0da27bcfd873c10ed395ba41d8c46 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 25 May 2022 16:52:07 +0100 Subject: [PATCH 367/504] Return invalid-arg if port group is not mono or stereo Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 00ede581..cebf2f45 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1038,6 +1038,8 @@ public: arr = V3_SPEAKER_L | V3_SPEAKER_R; break; default: + return V3_INVALID_ARG; + /* if (inputBuses.audio != 0 && ubusId == 0) { arr = 0x0; @@ -1054,6 +1056,7 @@ public: { arr = 1ull << (inputBuses.numMainAudio + inputBuses.numSidechain + ubusId + 33ull); } + */ break; } @@ -1085,6 +1088,8 @@ public: arr = V3_SPEAKER_L | V3_SPEAKER_R; break; default: + return V3_INVALID_ARG; + /* if (outputBuses.audio != 0 && ubusId == 0) { arr = 0x0; @@ -1101,6 +1106,7 @@ public: { arr = 1ull << (outputBuses.numMainAudio + outputBuses.numSidechain + ubusId + 33ull); } + */ break; } From 414513de66a7fe2b0647a92030ffd507b77cc4e7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 25 May 2022 19:24:37 +0100 Subject: [PATCH 368/504] Do not export DllMain for static windows builds Signed-off-by: falkTX --- distrho/src/DistrhoUtils.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index 7aaed1a2..364461ff 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -30,7 +30,7 @@ # include #endif -#if defined(DISTRHO_OS_WINDOWS) && !DISTRHO_IS_STANDALONE +#if defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD) && !DISTRHO_IS_STANDALONE static HINSTANCE hInstance = nullptr; DISTRHO_PLUGIN_EXPORT @@ -50,22 +50,24 @@ const char* getBinaryFilename() { static String filename; +#ifndef STATIC_BUILD if (filename.isNotEmpty()) return filename; -#ifdef DISTRHO_OS_WINDOWS -# if DISTRHO_IS_STANDALONE +# ifdef DISTRHO_OS_WINDOWS +# if DISTRHO_IS_STANDALONE constexpr const HINSTANCE hInstance = nullptr; -# endif +# endif CHAR filenameBuf[MAX_PATH]; filenameBuf[0] = '\0'; GetModuleFileNameA(hInstance, filenameBuf, sizeof(filenameBuf)); filename = filenameBuf; -#elif !defined(STATIC_BUILD) +# else Dl_info info; dladdr((void*)getBinaryFilename, &info); char filenameBuf[PATH_MAX]; filename = realpath(info.dli_fname, filenameBuf); +# endif #endif return filename; From 01fc1622154ed931ccb4e15ebf3dbb9625747c63 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 25 May 2022 19:28:36 +0100 Subject: [PATCH 369/504] Do not set -fno-gnu-unique for BSD Signed-off-by: falkTX --- Makefile.base.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.base.mk b/Makefile.base.mk index 908171e7..d4bcdcfb 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -173,8 +173,10 @@ BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections endif ifneq ($(MACOS_OR_WINDOWS),true) +ifneq ($(BSD),true) BASE_FLAGS += -fno-gnu-unique endif +endif ifeq ($(WINDOWS),true) # Assume we want posix From c0da2f0c438fb56d902d394ad62e3d6625d1bf8c Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 26 May 2022 14:45:12 +0100 Subject: [PATCH 370/504] Update carla plugin wrapper Signed-off-by: falkTX --- distrho/DistrhoUIMain.cpp | 2 +- distrho/src/DistrhoPluginCarla.cpp | 42 ++++++++++++++---------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index 9b898bc7..656eb30a 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -34,7 +34,7 @@ # error unsupported format #endif -#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && !DISTRHO_PLUGIN_TARGET_JACK && !DISTRHO_PLUGIN_TARGET_VST2 && !DISTRHO_PLUGIN_TARGET_VST3 +#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) # ifdef DISTRHO_PLUGIN_TARGET_DSSI # define DISTRHO_IS_STANDALONE 1 # else diff --git a/distrho/src/DistrhoPluginCarla.cpp b/distrho/src/DistrhoPluginCarla.cpp index 5f375f03..4878fad6 100644 --- a/distrho/src/DistrhoPluginCarla.cpp +++ b/distrho/src/DistrhoPluginCarla.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -31,11 +31,13 @@ START_NAMESPACE_DISTRHO #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT -static const writeMidiFunc writeMidiCallback = nullptr; +static constexpr const writeMidiFunc writeMidiCallback = nullptr; #endif #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST -static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; +static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif +// TODO +static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; #if DISTRHO_PLUGIN_HAS_UI // ----------------------------------------------------------------------- @@ -53,7 +55,12 @@ class UICarla public: UICarla(const NativeHostDescriptor* const host, PluginExporter* const plugin) : fHost(host), - fUI(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, plugin->getInstancePointer()) + fUI(this, 0, plugin->getSampleRate(), + editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, + nullptr, // window size + nullptr, // TODO file request + nullptr, // bundle path + plugin->getInstancePointer()) { fUI.setWindowTitle(host->uiName); @@ -75,7 +82,7 @@ public: bool carla_idle() { - return fUI.idle(); + return fUI.plugin_idle(); } void carla_setParameterValue(const uint32_t index, const float value) @@ -129,11 +136,6 @@ protected: } #endif - void handleSetSize(const uint width, const uint height) - { - fUI.setWindowSize(width, height); - } - // --------------------------------------------- private: @@ -172,11 +174,6 @@ private: } #endif - static void setSizeCallback(void* ptr, uint width, uint height) - { - handlePtr->handleSetSize(width, height); - } - #undef handlePtr CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UICarla) @@ -191,7 +188,7 @@ class PluginCarla : public NativePluginClass public: PluginCarla(const NativeHostDescriptor* const host) : NativePluginClass(host), - fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), + fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), fScalePointsCache(nullptr) { #if DISTRHO_PLUGIN_HAS_UI @@ -367,7 +364,8 @@ protected: } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override + void process(const float* const* const inBuffer, float** const outBuffer, const uint32_t frames, + const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override { MidiEvent realMidiEvents[midiEventCount]; @@ -391,7 +389,8 @@ protected: fPlugin.run(const_cast(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount); } #else - void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override + void process(const float* const* const inBuffer, float** const outBuffer, const uint32_t frames, + const NativeMidiEvent* const, const uint32_t) override { fPlugin.run(const_cast(inBuffer), outBuffer, frames); } @@ -498,10 +497,7 @@ private: void createUiIfNeeded() { if (fUiPtr == nullptr) - { - d_lastUiSampleRate = getSampleRate(); fUiPtr = new UICarla(getHostHandle(), &fPlugin); - } } #endif @@ -539,8 +535,8 @@ private: public: static NativePluginHandle _instantiate(const NativeHostDescriptor* host) { - d_lastBufferSize = host->get_buffer_size(host->handle); - d_lastSampleRate = host->get_sample_rate(host->handle); + d_nextBufferSize = host->get_buffer_size(host->handle); + d_nextSampleRate = host->get_sample_rate(host->handle); return new PluginCarla(host); } From 4f906d2980b6442bbd5fad153d4dcff886e54797 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 27 May 2022 00:34:24 +0100 Subject: [PATCH 371/504] Split OpenGL includes into its own dedicated header Signed-off-by: falkTX --- dgl/OpenGL-include.hpp | 112 +++++++++++++++++++++++++++++++++++++++++ dgl/OpenGL.hpp | 97 ++--------------------------------- 2 files changed, 117 insertions(+), 92 deletions(-) create mode 100644 dgl/OpenGL-include.hpp diff --git a/dgl/OpenGL-include.hpp b/dgl/OpenGL-include.hpp new file mode 100644 index 00000000..4f4bb0c1 --- /dev/null +++ b/dgl/OpenGL-include.hpp @@ -0,0 +1,112 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_OPENGL_INCLUDE_HPP_INCLUDED +#define DGL_OPENGL_INCLUDE_HPP_INCLUDED + +#include "../distrho/src/DistrhoDefines.h" + +// -------------------------------------------------------------------------------------------------------------------- +// Fix OpenGL includes for Windows, based on glfw code (part 1) + +#undef DGL_CALLBACK_DEFINED +#undef DGL_WINGDIAPI_DEFINED + +#ifdef DISTRHO_OS_WINDOWS + +#ifndef APIENTRY +# define APIENTRY __stdcall +#endif // APIENTRY + +/* We need WINGDIAPI defined */ +#ifndef WINGDIAPI +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) +# define WINGDIAPI __declspec(dllimport) +# elif defined(__LCC__) +# define WINGDIAPI __stdcall +# else +# define WINGDIAPI extern +# endif +# define DGL_WINGDIAPI_DEFINED +#endif // WINGDIAPI + +/* Some files also need CALLBACK defined */ +#ifndef CALLBACK +# if defined(_MSC_VER) +# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) +# define CALLBACK __stdcall +# else +# define CALLBACK +# endif +# else +# define CALLBACK __stdcall +# endif +# define DGL_CALLBACK_DEFINED +#endif // CALLBACK + +#endif // DISTRHO_OS_WINDOWS + +// -------------------------------------------------------------------------------------------------------------------- +// OpenGL includes + +#ifdef DISTRHO_OS_MAC +# ifdef DGL_USE_OPENGL3 +# include +# include +# else +# include +# endif +#else +# ifndef DISTRHO_OS_WINDOWS +# define GL_GLEXT_PROTOTYPES +# endif +# ifndef __GLEW_H__ +# include +# include +# endif +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Missing OpenGL defines + +#if defined(GL_BGR_EXT) && !defined(GL_BGR) +# define GL_BGR GL_BGR_EXT +#endif + +#if defined(GL_BGRA_EXT) && !defined(GL_BGRA) +# define GL_BGRA GL_BGRA_EXT +#endif + +#ifndef GL_CLAMP_TO_BORDER +# define GL_CLAMP_TO_BORDER 0x812D +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// Fix OpenGL includes for Windows, based on glfw code (part 2) + +#ifdef DGL_CALLBACK_DEFINED +# undef CALLBACK +# undef DGL_CALLBACK_DEFINED +#endif + +#ifdef DGL_WINGDIAPI_DEFINED +# undef WINGDIAPI +# undef DGL_WINGDIAPI_DEFINED +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#endif diff --git a/dgl/OpenGL.hpp b/dgl/OpenGL.hpp index d69ca210..91da7e3c 100644 --- a/dgl/OpenGL.hpp +++ b/dgl/OpenGL.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -20,96 +20,7 @@ #include "ImageBase.hpp" #include "ImageBaseWidgets.hpp" -// ----------------------------------------------------------------------- -// Fix OpenGL includes for Windows, based on glfw code (part 1) - -#undef DGL_CALLBACK_DEFINED -#undef DGL_WINGDIAPI_DEFINED - -#ifdef DISTRHO_OS_WINDOWS - -#ifndef APIENTRY -# define APIENTRY __stdcall -#endif // APIENTRY - -/* We need WINGDIAPI defined */ -#ifndef WINGDIAPI -# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) -# define WINGDIAPI __declspec(dllimport) -# elif defined(__LCC__) -# define WINGDIAPI __stdcall -# else -# define WINGDIAPI extern -# endif -# define DGL_WINGDIAPI_DEFINED -#endif // WINGDIAPI - -/* Some files also need CALLBACK defined */ -#ifndef CALLBACK -# if defined(_MSC_VER) -# if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) -# define CALLBACK __stdcall -# else -# define CALLBACK -# endif -# else -# define CALLBACK __stdcall -# endif -# define DGL_CALLBACK_DEFINED -#endif // CALLBACK - -/* Most GL/glu.h variants on Windows need wchar_t */ -#include - -#endif // DISTRHO_OS_WINDOWS - -// ----------------------------------------------------------------------- -// OpenGL includes - -#ifdef DISTRHO_OS_MAC -# ifdef DGL_USE_OPENGL3 -# include -# include -# else -# include -# endif -#else -# ifndef DISTRHO_OS_WINDOWS -# define GL_GLEXT_PROTOTYPES -# endif -# ifndef __GLEW_H__ -# include -# include -# endif -#endif - -// ----------------------------------------------------------------------- -// Missing OpenGL defines - -#if defined(GL_BGR_EXT) && !defined(GL_BGR) -# define GL_BGR GL_BGR_EXT -#endif - -#if defined(GL_BGRA_EXT) && !defined(GL_BGRA) -# define GL_BGRA GL_BGRA_EXT -#endif - -#ifndef GL_CLAMP_TO_BORDER -# define GL_CLAMP_TO_BORDER 0x812D -#endif - -// ----------------------------------------------------------------------- -// Fix OpenGL includes for Windows, based on glfw code (part 2) - -#ifdef DGL_CALLBACK_DEFINED -# undef CALLBACK -# undef DGL_CALLBACK_DEFINED -#endif - -#ifdef DGL_WINGDIAPI_DEFINED -# undef WINGDIAPI -# undef DGL_WINGDIAPI_DEFINED -#endif +#include "OpenGL-include.hpp" START_NAMESPACE_DGL @@ -120,6 +31,8 @@ START_NAMESPACE_DGL */ struct OpenGLGraphicsContext : GraphicsContext { +#ifdef DGL_USE_OPENGL3 +#endif }; // ----------------------------------------------------------------------- @@ -305,4 +218,4 @@ typedef ImageBaseSwitch OpenGLImageSwitch; END_NAMESPACE_DGL -#endif +#endif // DGL_OPENGL_HPP_INCLUDED From d76a11216152c36295596c12ef29a94b9769c2ad Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 27 May 2022 00:35:07 +0100 Subject: [PATCH 372/504] Import ResizeHandle widget for tests, set Demo min size Signed-off-by: falkTX --- tests/Demo.cpp | 153 +----------------------- tests/widgets/ResizeHandle.hpp | 207 +++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+), 150 deletions(-) create mode 100644 tests/widgets/ResizeHandle.hpp diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 41759d24..fc96cc27 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -23,6 +23,7 @@ #ifdef DGL_OPENGL #include "widgets/ExampleTextWidget.hpp" #endif +#include "widgets/ResizeHandle.hpp" #include "demo_res/DemoArtwork.cpp" #include "images_res/CatPics.cpp" @@ -250,155 +251,6 @@ private: #endif }; -// -------------------------------------------------------------------------------------------------------------------- -// Resize handle widget - -class ResizeHandle : public TopLevelWidget -{ - Rectangle area; - Line l1, l2, l3; - uint baseSize; - - // event handling state - bool resizing; - Point lastResizePoint; - Size resizingSize; - -public: - explicit ResizeHandle(TopLevelWidget* const tlw) - : TopLevelWidget(tlw->getWindow()), - baseSize(16), - resizing(false) - { - resetArea(); - } - - explicit ResizeHandle(Window& window) - : TopLevelWidget(window), - baseSize(16), - resizing(false) - { - resetArea(); - } - - void setBaseSize(const uint size) - { - baseSize = std::max(16u, size); - resetArea(); - } - -protected: - void onDisplay() override - { - const GraphicsContext& context(getGraphicsContext()); - const double offset = getScaleFactor(); - - // draw white lines, 1px wide - Color(1.0f, 1.0f, 1.0f).setFor(context); - l1.draw(context, 1); - l2.draw(context, 1); - l3.draw(context, 1); - - // draw black lines, offset by 1px and 2px wide - Color(0.0f, 0.0f, 0.0f).setFor(context); - Line l1b(l1), l2b(l2), l3b(l3); - l1b.moveBy(offset, offset); - l2b.moveBy(offset, offset); - l3b.moveBy(offset, offset); - l1b.draw(context, 1); - l2b.draw(context, 1); - l3b.draw(context, 1); - } - - bool onMouse(const MouseEvent& ev) override - { - if (ev.button != 1) - return false; - - if (ev.press && area.contains(ev.pos)) - { - resizing = true; - resizingSize = Size(getWidth(), getHeight()); - lastResizePoint = ev.pos; - return true; - } - - if (resizing && ! ev.press) - { - resizing = false; - return true; - } - - return false; - } - - bool onMotion(const MotionEvent& ev) override - { - if (! resizing) - return false; - - const Size offset(ev.pos.getX() - lastResizePoint.getX(), - ev.pos.getY() - lastResizePoint.getY()); - - resizingSize += offset; - lastResizePoint = ev.pos; - - // TODO min width, min height - const uint minWidth = 16; - const uint minHeight = 16; - - if (resizingSize.getWidth() < minWidth) - resizingSize.setWidth(minWidth); - if (resizingSize.getHeight() < minHeight) - resizingSize.setHeight(minHeight); - - setSize(resizingSize.getWidth(), resizingSize.getHeight()); - return true; - } - - void onResize(const ResizeEvent& ev) override - { - TopLevelWidget::onResize(ev); - resetArea(); - } - -private: - void resetArea() - { - const double scaleFactor = getScaleFactor(); - const uint margin = 0.0 * scaleFactor; - const uint size = baseSize * scaleFactor; - - area = Rectangle(getWidth() - size - margin, - getHeight() - size - margin, - size, size); - - recreateLines(area.getX(), area.getY(), size); - } - - void recreateLines(const uint x, const uint y, const uint size) - { - uint linesize = size; - uint offset = 0; - - // 1st line, full diagonal size - l1.setStartPos(x + size, y); - l1.setEndPos(x, y + size); - - // 2nd line, bit more to the right and down, cropped - offset += size / 3; - linesize -= size / 3; - l2.setStartPos(x + linesize + offset, y + offset); - l2.setEndPos(x + offset, y + linesize + offset); - - // 3rd line, even more right and down - offset += size / 3; - linesize -= size / 3; - l3.setStartPos(x + linesize + offset, y + offset); - l3.setEndPos(x + offset, y + linesize + offset); - } -}; - // -------------------------------------------------------------------------------------------------------------------- // Main Demo Window, having a left-side tab-like widget and main area for current widget @@ -535,6 +387,7 @@ void createAndShowExampleWidgetStandaloneWindow(Application& app) { ExampleWidgetStandaloneWindow swin(app); const double scaleFactor = swin.getScaleFactor(); + swin.setGeometryConstraints(128 * scaleFactor, 128 * scaleFactor); swin.setResizable(true); swin.setSize(600 * scaleFactor, 500 * scaleFactor); swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); diff --git a/tests/widgets/ResizeHandle.hpp b/tests/widgets/ResizeHandle.hpp new file mode 100644 index 00000000..99a164a1 --- /dev/null +++ b/tests/widgets/ResizeHandle.hpp @@ -0,0 +1,207 @@ +/* + * Resize handle for DPF + * Copyright (C) 2021-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include "../../dgl/Color.hpp" +#include "../../dgl/TopLevelWidget.hpp" + +START_NAMESPACE_DGL + +/** Resize handle for DPF windows, will sit on bottom-right. */ +class ResizeHandle : public TopLevelWidget +{ +public: + /** Constructor for placing this handle on top of a window. */ + explicit ResizeHandle(Window& window) + : TopLevelWidget(window), + handleSize(16), + hasCursor(false), + isResizing(false) + { + resetArea(); + } + + /** Overloaded constructor, will fetch the window from an existing top-level widget. */ + explicit ResizeHandle(TopLevelWidget* const tlw) + : TopLevelWidget(tlw->getWindow()), + handleSize(16), + hasCursor(false), + isResizing(false) + { + resetArea(); + } + + /** Set the handle size, minimum 16. + * Scale factor is automatically applied on top of this size as needed */ + void setHandleSize(const uint size) + { + handleSize = std::max(16u, size); + resetArea(); + } + +protected: + void onDisplay() override + { + // TODO implement gl3 stuff in DPF +#ifndef DGL_USE_OPENGL3 + const GraphicsContext& context(getGraphicsContext()); + const double lineWidth = 1.0 * getScaleFactor(); + + #if defined(DGL_OPENGL) && !defined(DGL_USE_OPENGL3) + glMatrixMode(GL_MODELVIEW); + #endif + + // draw white lines, 1px wide + Color(1.0f, 1.0f, 1.0f).setFor(context); + l1.draw(context, lineWidth); + l2.draw(context, lineWidth); + l3.draw(context, lineWidth); + + // draw black lines, offset by 1px and 1px wide + Color(0.0f, 0.0f, 0.0f).setFor(context); + Line l1b(l1), l2b(l2), l3b(l3); + l1b.moveBy(lineWidth, lineWidth); + l2b.moveBy(lineWidth, lineWidth); + l3b.moveBy(lineWidth, lineWidth); + l1b.draw(context, lineWidth); + l2b.draw(context, lineWidth); + l3b.draw(context, lineWidth); +#endif + } + + bool onMouse(const MouseEvent& ev) override + { + if (ev.button != 1) + return false; + + if (ev.press && area.contains(ev.pos)) + { + isResizing = true; + resizingSize = Size(getWidth(), getHeight()); + lastResizePoint = ev.pos; + return true; + } + + if (isResizing && ! ev.press) + { + isResizing = false; + recheckCursor(ev.pos); + return true; + } + + return false; + } + + bool onMotion(const MotionEvent& ev) override + { + if (! isResizing) + { + recheckCursor(ev.pos); + return false; + } + + const Size offset(ev.pos.getX() - lastResizePoint.getX(), + ev.pos.getY() - lastResizePoint.getY()); + + resizingSize += offset; + lastResizePoint = ev.pos; + + // TODO keepAspectRatio + bool keepAspectRatio; + const Size minSize(getWindow().getGeometryConstraints(keepAspectRatio)); + const uint minWidth = minSize.getWidth(); + const uint minHeight = minSize.getHeight(); + + if (resizingSize.getWidth() < minWidth) + resizingSize.setWidth(minWidth); + if (resizingSize.getWidth() > 16384) + resizingSize.setWidth(16384); + if (resizingSize.getHeight() < minHeight) + resizingSize.setHeight(minHeight); + if (resizingSize.getHeight() > 16384) + resizingSize.setHeight(16384); + + setSize(resizingSize.getWidth(), resizingSize.getHeight()); + return true; + } + + void onResize(const ResizeEvent& ev) override + { + TopLevelWidget::onResize(ev); + resetArea(); + } + +private: + Rectangle area; + Line l1, l2, l3; + uint handleSize; + + // event handling state + bool hasCursor, isResizing; + Point lastResizePoint; + Size resizingSize; + + void recheckCursor(const Point& pos) + { + const bool shouldHaveCursor = area.contains(pos); + + if (shouldHaveCursor == hasCursor) + return; + + hasCursor = shouldHaveCursor; + setCursor(shouldHaveCursor ? kMouseCursorDiagonal : kMouseCursorArrow); + } + + void resetArea() + { + const double scaleFactor = getScaleFactor(); + const uint margin = 0.0 * scaleFactor; + const uint size = handleSize * scaleFactor; + + area = Rectangle(getWidth() - size - margin, + getHeight() - size - margin, + size, size); + + recreateLines(area.getX(), area.getY(), size); + } + + void recreateLines(const uint x, const uint y, const uint size) + { + uint linesize = size; + uint offset = 0; + + // 1st line, full diagonal size + l1.setStartPos(x + size, y); + l1.setEndPos(x, y + size); + + // 2nd line, bit more to the right and down, cropped + offset += size / 3; + linesize -= size / 3; + l2.setStartPos(x + linesize + offset, y + offset); + l2.setEndPos(x + offset, y + linesize + offset); + + // 3rd line, even more right and down + offset += size / 3; + linesize -= size / 3; + l3.setStartPos(x + linesize + offset, y + offset); + l3.setEndPos(x + offset, y + linesize + offset); + } + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResizeHandle) +}; + +END_NAMESPACE_DGL From 32916adb820e2e8c4b11917d7635c84138770484 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 27 May 2022 02:23:54 +0100 Subject: [PATCH 373/504] Add handy mouse button enum Signed-off-by: falkTX --- dgl/Base.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index bb0538c9..c49740ad 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -130,6 +130,29 @@ enum CrossingMode { kCrossingUngrab ///< Crossing due to a grab release }; +/** + A mouse button. + + Mouse button numbers start from 1, and are ordered: primary, secondary, middle. + So, on a typical right-handed mouse, the button numbers are: + + Left: 1 + Right: 2 + Middle (often a wheel): 3 + + Higher button numbers are reported in the same order they are represented on the system. + There is no universal standard here, but buttons 4 and 5 are typically a pair of buttons or a rocker, + which are usually bound to "back" and "forward" operations. + + Note that these numbers may differ from those used on the underlying + platform, since they are manipulated to provide a consistent portable API. +*/ +enum MouseButton { + kMouseButtonLeft = 1, + kMouseButtonRight, + kMouseButtonMiddle, +}; + /** A mouse cursor type. From 487d918d5c5209c3ba1d1a5837d08791389452df Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 27 May 2022 02:24:40 +0100 Subject: [PATCH 374/504] Reorder/organize the Widget event docs Signed-off-by: falkTX --- dgl/Widget.hpp | 69 ++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/dgl/Widget.hpp b/dgl/Widget.hpp index 463f7c9f..38a49b1f 100644 --- a/dgl/Widget.hpp +++ b/dgl/Widget.hpp @@ -56,17 +56,16 @@ public: /** Base event data. These are the fields present on all Widget events. - - @a mod Currently active keyboard modifiers, @see Modifier. - @a mod Event flags, @see EventFlag. - @a time Event timestamp (if any). */ struct BaseEvent { + /** Currently active keyboard modifiers. @see Modifier */ uint mod; + /** Event flags. @see EventFlag */ uint flags; + /** Event timestamp (if any). */ uint time; - /** Constructor */ + /** Constructor for default/null values */ BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {} /** Destuctor */ virtual ~BaseEvent() noexcept {} @@ -86,17 +85,17 @@ public: Alternatively, the raw @a keycode can be used to work directly with physical keys, but note that this value is not portable and differs between platforms and hardware. - @a press True if the key was pressed, false if released. - @a key Unicode point of the key pressed. - @a keycode Raw keycode. @see onKeyboard */ struct KeyboardEvent : BaseEvent { + /** True if the key was pressed, false if released. */ bool press; + /** Unicode point of the key pressed. */ uint key; + /** Raw keycode. */ uint keycode; - /** Constructor */ + /** Constructor for default/null values */ KeyboardEvent() noexcept : BaseEvent(), press(false), @@ -112,9 +111,9 @@ public: */ struct DISTRHO_DEPRECATED_BY("KeyboardEvent") SpecialEvent : BaseEvent { bool press; - Key key; + Key key; - /** Constructor */ + /** Constructor for default/null values */ SpecialEvent() noexcept : BaseEvent(), press(false), @@ -131,17 +130,17 @@ public: so there is not necessarily a direct correspondence between text events and physical key presses. For example, with some input methods a sequence of several key presses will generate a single character. - @a keycode Raw key code. - @a character Unicode character code. - @a string UTF-8 string. @see onCharacterInput */ struct CharacterInputEvent : BaseEvent { + /** Raw key code. */ uint keycode; + /** Unicode character code. */ uint character; + /** UTF-8 string. */ char string[8]; - /** Constructor */ + /** Constructor for default/null values */ CharacterInputEvent() noexcept : BaseEvent(), keycode(0), @@ -155,20 +154,19 @@ public: /** Mouse press or release event. - - @a button The button number starting from 1 (1 = left, 2 = middle, 3 = right). - @a press True if the button was pressed, false if released. - @a pos The widget-relative coordinates of the pointer. - @a absolutePos The absolute coordinates of the pointer. @see onMouse */ struct MouseEvent : BaseEvent { + /** The button number starting from 1. @see MouseButton */ uint button; + /** True if the button was pressed, false if released. */ bool press; + /** The widget-relative coordinates of the pointer. */ Point pos; + /** The absolute coordinates of the pointer. */ Point absolutePos; - /** Constructor */ + /** Constructor for default/null values */ MouseEvent() noexcept : BaseEvent(), button(0), @@ -179,16 +177,15 @@ public: /** Mouse motion event. - - @a pos The widget-relative coordinates of the pointer. - @a absolutePos The absolute coordinates of the pointer. @see onMotion */ struct MotionEvent : BaseEvent { + /** The widget-relative coordinates of the pointer. */ Point pos; + /** The absolute coordinates of the pointer. */ Point absolutePos; - /** Constructor */ + /** Constructor for default/null values */ MotionEvent() noexcept : BaseEvent(), pos(0.0, 0.0), @@ -204,19 +201,19 @@ public: Some systems and devices support finer resolution and/or higher values for fast scrolls, so programs should handle any value gracefully. - @a pos The widget-relative coordinates of the pointer. - @a absolutePos The absolute coordinates of the pointer. - @a delta The scroll distance. - @a direction The direction of the scroll or "smooth". @see onScroll */ struct ScrollEvent : BaseEvent { + /** The widget-relative coordinates of the pointer. */ Point pos; + /** The absolute coordinates of the pointer. */ Point absolutePos; + /** The scroll distance. */ Point delta; + /** The direction of the scroll or "smooth". */ ScrollDirection direction; - /** Constructor */ + /** Constructor for default/null values */ ScrollEvent() noexcept : BaseEvent(), pos(0.0, 0.0), @@ -227,15 +224,15 @@ public: /** Resize event. - @a size The new widget size. - @a oldSize The previous size, may be null. @see onResize */ struct ResizeEvent { + /** The new widget size. */ Size size; + /** The previous size, can be null. */ Size oldSize; - /** Constructor */ + /** Constructor for default/null values */ ResizeEvent() noexcept : size(0, 0), oldSize(0, 0) {} @@ -243,15 +240,15 @@ public: /** Widget position changed event. - @a pos The new absolute position of the widget. - @a oldPos The previous absolute position of the widget. @see onPositionChanged */ struct PositionChangedEvent { + /** The new absolute position of the widget. */ Point pos; + /** The previous absolute position of the widget. */ Point oldPos; - /** Constructor */ + /** Constructor for default/null values */ PositionChangedEvent() noexcept : pos(0, 0), oldPos(0, 0) {} From cc1db59a5be2fd1c8418df5e3432f0ced37329ba Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 27 May 2022 02:26:26 +0100 Subject: [PATCH 375/504] Start updating pugl, WIP Signed-off-by: falkTX --- dgl/src/Window.cpp | 19 +- dgl/src/WindowPrivateData.cpp | 105 ++++---- dgl/src/WindowPrivateData.hpp | 7 +- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 352 ++++++++++----------------- dgl/src/pugl.hpp | 93 +++---- distrho/src/DistrhoUIPrivateData.hpp | 4 +- pugl-updates-notes.txt | 9 + 8 files changed, 242 insertions(+), 349 deletions(-) create mode 100644 pugl-updates-notes.txt diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index e77790e0..5ac3d8a7 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -189,7 +189,7 @@ void Window::setOffsetY(const int y) void Window::setOffset(const int x, const int y) { - puglSetWindowOffset(pData->view, x, y); + puglSetPosition(pData->view, x, y); } void Window::setOffset(const Point& offset) @@ -289,7 +289,7 @@ void Window::setSize(uint width, uint height) } else { - puglSetWindowSize(pData->view, width, height); + puglSetSizeAndDefault(pData->view, width, height); } } @@ -326,10 +326,7 @@ bool Window::setClipboard(const char* const mimeType, const void* const data, co const void* Window::getClipboard(const char*& mimeType, size_t& dataSize) { - DISTRHO_SAFE_ASSERT_RETURN(!pData->ignoreEvents, nullptr); - pData->ignoreEvents = true; - const void* const clipboard = puglGetClipboard(pData->view, &mimeType, &dataSize); - pData->ignoreEvents = false; + const void* const clipboard = nullptr; // puglGetClipboard(pData->view, &mimeType, &dataSize); return clipboard; } @@ -400,10 +397,10 @@ void Window::repaint(const Rectangle& rect) noexcept return; PuglRect prect = { - static_cast(rect.getX()), - static_cast(rect.getY()), - static_cast(rect.getWidth()), - static_cast(rect.getHeight()), + static_cast(rect.getX()), + static_cast(rect.getY()), + static_cast(rect.getWidth()), + static_cast(rect.getHeight()), }; if (pData->autoScaling) { diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index b1bfc7c5..54a13673 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -52,39 +52,63 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -static double getDesktopScaleFactor(const PuglView* const view) +static double getScaleFactorFromParent(const PuglView* const view) { // allow custom scale for testing if (const char* const scale = getenv("DPF_SCALE_FACTOR")) return std::max(1.0, std::atof(scale)); if (view != nullptr) - return puglGetDesktopScaleFactor(view); + return puglGetScaleFactorFromParent(view); return 1.0; } +static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView* const transientParentView) +{ + DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr); + + if (PuglView* const view = puglNewView(world)) + { + puglSetTransientParent(view, puglGetNativeWindow(transientParentView)); + return view; + } + + return nullptr; +} + +static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintptr_t parentWindowHandle) +{ + DISTRHO_SAFE_ASSERT_RETURN(world != nullptr, nullptr); + + if (PuglView* const view = puglNewView(world)) + { + puglSetParentWindow(view, parentWindowHandle); + return view; + } + + return nullptr; +} + // ----------------------------------------------------------------------- Window::PrivateData::PrivateData(Application& a, Window* const s) : app(a), appData(a.pData), self(s), - view(puglNewView(appData->world)), - transientParentView(nullptr), + view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), topLevelWidgets(), isClosed(true), isVisible(false), isEmbed(false), usesSizeRequest(false), - scaleFactor(getDesktopScaleFactor(view)), + scaleFactor(getScaleFactorFromParent(view)), autoScaling(false), autoScaleFactor(1.0), minWidth(0), minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -98,8 +122,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c : app(a), appData(a.pData), self(s), - view(puglNewView(appData->world)), - transientParentView(ppData->view), + view(puglNewViewWithTransientParent(appData->world, ppData->view)), topLevelWidgets(), isClosed(true), isVisible(false), @@ -112,15 +135,12 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), #endif modal(ppData) { - puglSetTransientFor(view, puglGetNativeWindow(transientParentView)); - initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } @@ -130,30 +150,25 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, : app(a), appData(a.pData), self(s), - view(puglNewView(appData->world)), - transientParentView(nullptr), + view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), topLevelWidgets(), isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), usesSizeRequest(false), - scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), + scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), autoScaling(false), autoScaleFactor(1.0), minWidth(0), minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), #endif modal() { - if (isEmbed) - puglSetParentWindow(view, parentWindowHandle); - initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); } @@ -164,21 +179,19 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, : app(a), appData(a.pData), self(s), - view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), - transientParentView(nullptr), + view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), topLevelWidgets(), isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0 && view != nullptr), isEmbed(parentWindowHandle != 0), usesSizeRequest(isVST3), - scaleFactor(scale != 0.0 ? scale : getDesktopScaleFactor(view)), + scaleFactor(scale != 0.0 ? scale : getScaleFactorFromParent(view)), autoScaling(false), autoScaleFactor(1.0), minWidth(0), minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - ignoreEvents(false), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -230,11 +243,11 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo } puglSetMatchingBackendForCurrentBuild(view); + puglSetHandle(view, this); - puglClearMinSize(view); - puglSetWindowSize(view, width, height); + // FIXME? + // puglClearMinSize(view); - puglSetHandle(view, this); puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); #if DGL_USE_RGBA @@ -249,6 +262,9 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo #endif // PUGL_SAMPLES ?? puglSetEventFunc(view, puglEventCallback); + + // setting default size triggers system-level calls, do it last + puglSetSizeHint(view, PUGL_DEFAULT_SIZE, width, height); } bool Window::PrivateData::initPost() @@ -314,8 +330,8 @@ void Window::PrivateData::show() appData->oneWindowShown(); // FIXME - PuglRect rect = puglGetFrame(view); - puglSetWindowSize(view, static_cast(rect.width), static_cast(rect.height)); +// PuglRect rect = puglGetFrame(view); +// puglSetWindowSize(view, static_cast(rect.width), static_cast(rect.height)); #if defined(DISTRHO_OS_WINDOWS) puglWin32ShowCentered(view); @@ -378,11 +394,7 @@ void Window::PrivateData::focus() if (! isEmbed) puglRaiseWindow(view); -#if defined(HAVE_X11) && !defined(DISTRHO_OS_MAC) && !defined(DISTRHO_OS_WINDOWS) - puglX11GrabFocus(view); -#else puglGrabFocus(view); -#endif } // ----------------------------------------------------------------------- @@ -393,10 +405,7 @@ void Window::PrivateData::setResizable(const bool resizable) DGL_DBG("Window setResizable called\n"); - puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); -#ifdef DISTRHO_OS_WINDOWS - puglWin32SetWindowResizable(view, resizable); -#endif + puglSetResizable(view, resizable); } // ----------------------------------------------------------------------- @@ -795,8 +804,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< View must be drawn, a #PuglEventExpose case PUGL_EXPOSE: - if (pData->ignoreEvents) - break; // unused x, y, width, height (double) pData->onPuglExpose(); break; @@ -810,8 +817,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_FOCUS_IN: ///< Keyboard focus left view, a #PuglEventFocus case PUGL_FOCUS_OUT: - if (pData->ignoreEvents) - break; pData->onPuglFocus(event->type == PUGL_FOCUS_IN, static_cast(event->focus.mode)); break; @@ -821,8 +826,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Key released, a #PuglEventKey case PUGL_KEY_RELEASE: { - if (pData->ignoreEvents) - break; // unused x, y, xRoot, yRoot (double) Widget::KeyboardEvent ev; ev.mod = event->key.state; @@ -846,8 +849,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Character entered, a #PuglEventText case PUGL_TEXT: { - if (pData->ignoreEvents) - break; // unused x, y, xRoot, yRoot (double) Widget::CharacterInputEvent ev; ev.mod = event->text.state; @@ -872,13 +873,11 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Mouse button released, a #PuglEventButton case PUGL_BUTTON_RELEASE: { - if (pData->ignoreEvents) - break; Widget::MouseEvent ev; ev.mod = event->button.state; ev.flags = event->button.flags; ev.time = static_cast(event->button.time * 1000.0 + 0.5); - ev.button = event->button.button; + ev.button = event->button.button + 1; ev.press = event->type == PUGL_BUTTON_PRESS; ev.pos = Point(event->button.x, event->button.y); ev.absolutePos = ev.pos; @@ -889,8 +888,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Pointer moved, a #PuglEventMotion case PUGL_MOTION: { - if (pData->ignoreEvents) - break; Widget::MotionEvent ev; ev.mod = event->motion.state; ev.flags = event->motion.flags; @@ -904,8 +901,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Scrolled, a #PuglEventScroll case PUGL_SCROLL: { - if (pData->ignoreEvents) - break; Widget::ScrollEvent ev; ev.mod = event->scroll.state; ev.flags = event->scroll.flags; @@ -924,8 +919,6 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Timer triggered, a #PuglEventTimer case PUGL_TIMER: - if (pData->ignoreEvents) - break; if (IdleCallback* const idleCallback = reinterpret_cast(event->timer.id)) idleCallback->idleCallback(); break; @@ -937,6 +930,14 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Recursive loop left, a #PuglEventLoopLeave case PUGL_LOOP_LEAVE: break; + + ///< Data offered from clipboard, a #PuglDataOfferEvent + case PUGL_DATA_OFFER: + break; + + ///< Data available from clipboard, a #PuglDataEvent + case PUGL_DATA: + break; } return PUGL_SUCCESS; diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 36d2ce43..6cb8a4d6 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -45,7 +45,7 @@ struct Window::PrivateData : IdleCallback { PuglView* view; /** Pugl view instance of the transient parent window. */ - PuglView* const transientParentView; +// PuglView* const transientParentView; /** Reserved space for graphics context. */ mutable uint8_t graphicsContext[sizeof(void*)]; @@ -80,9 +80,6 @@ struct Window::PrivateData : IdleCallback { /** Whether to ignore idle callback requests, useful for temporary windows. */ bool ignoreIdleCallbacks; - /** Whether to ignore pugl events (except create and destroy), used for puglGetClipboard. */ - bool ignoreEvents; - /** Render to a picture file when non-null, automatically free+unset after saving. */ char* filenameToRenderInto; diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index b1d9703e..9691a681 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit b1d9703ecbdb0a033fe0b9acdf58b90f7d81a8e5 +Subproject commit 9691a6810283ffd5a99b1cc974fc638e80f94979 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index ae99221c..3f697d3d 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -16,10 +16,24 @@ #include "pugl.hpp" +// -------------------------------------------------------------------------------------------------------------------- +// include base headers + +#ifdef DGL_CAIRO +# include +#endif +#ifdef DGL_OPENGL +# include "../OpenGL-include.hpp" +#endif +#ifdef DGL_VULKAN +# include +#endif + /* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */ #include #include #include +#include #include #include @@ -57,9 +71,10 @@ # endif #else # include +# include # include # include -# include +// # include # include # include # include @@ -68,7 +83,7 @@ # include # ifdef HAVE_XCURSOR # include -# include +// # include # endif # ifdef HAVE_XRANDR # include @@ -78,15 +93,12 @@ # include # endif # ifdef DGL_CAIRO -# include # include # endif # ifdef DGL_OPENGL -# include # include # endif # ifdef DGL_VULKAN -# include # include # endif #endif @@ -101,6 +113,8 @@ #ifndef DISTRHO_OS_MAC START_NAMESPACE_DGL +#else +USE_NAMESPACE_DGL #endif // -------------------------------------------------------------------------------------------------------------------- @@ -113,9 +127,6 @@ START_NAMESPACE_DGL # define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView) # define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow) # endif -# ifndef __MAC_10_9 -# define NSModalResponseOK NSOKButton -# endif # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" # import "pugl-upstream/src/mac.m" @@ -159,7 +170,7 @@ START_NAMESPACE_DGL #include "pugl-upstream/src/implementation.c" // -------------------------------------------------------------------------------------------------------------------- -// expose backend enter +// DGL specific, expose backend enter bool puglBackendEnter(PuglView* const view) { @@ -167,109 +178,39 @@ bool puglBackendEnter(PuglView* const view) } // -------------------------------------------------------------------------------------------------------------------- -// expose backend leave - -void puglBackendLeave(PuglView* const view) -{ - view->backend->leave(view, nullptr); -} - -// -------------------------------------------------------------------------------------------------------------------- -// clear minimum size to 0 - -void puglClearMinSize(PuglView* const view) -{ - view->minWidth = 0; - view->minHeight = 0; -} - -// -------------------------------------------------------------------------------------------------------------------- -// missing in pugl, directly returns transient parent +// DGL specific, expose backend leave -PuglNativeView puglGetTransientParent(const PuglView* const view) +bool puglBackendLeave(PuglView* const view) { - return view->transientParent; + return view->backend->leave(view, nullptr) == PUGL_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- -// missing in pugl, directly returns title char* pointer +// DGL specific, assigns backend that matches current DGL build -const char* puglGetWindowTitle(const PuglView* const view) +void puglSetMatchingBackendForCurrentBuild(PuglView* const view) { - return view->title; +#ifdef DGL_CAIRO + puglSetBackend(view, puglCairoBackend()); +#endif +#ifdef DGL_OPENGL + puglSetBackend(view, puglGlBackend()); +#endif +#ifdef DGL_VULKAN + puglSetBackend(view, puglVulkanBackend()); +#endif + if (view->backend == nullptr) + puglSetBackend(view, puglStubBackend()); } // -------------------------------------------------------------------------------------------------------------------- -// get global scale factor - -double puglGetDesktopScaleFactor(const PuglView* const view) -{ -#if defined(DISTRHO_OS_MAC) - if (NSWindow* const window = view->impl->window ? view->impl->window - : [view->impl->wrapperView window]) - return [window screen].backingScaleFactor; - return [NSScreen mainScreen].backingScaleFactor; -#elif defined(DISTRHO_OS_WINDOWS) - if (const HMODULE Shcore = LoadLibraryA("Shcore.dll")) - { - typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*); - typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); - -# if defined(__GNUC__) && (__GNUC__ >= 9) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-function-type" -# endif - const PFN_GetProcessDpiAwareness GetProcessDpiAwareness - = (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness"); - const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor - = (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor"); -# if defined(__GNUC__) && (__GNUC__ >= 9) -# pragma GCC diagnostic pop -# endif - - DWORD dpiAware = 0; - if (GetProcessDpiAwareness && GetScaleFactorForMonitor - && GetProcessDpiAwareness(NULL, &dpiAware) == 0 && dpiAware != 0) - { - const HMONITOR hMon = MonitorFromWindow(view->impl->hwnd, MONITOR_DEFAULTTOPRIMARY); - - DWORD scaleFactor = 0; - if (GetScaleFactorForMonitor(hMon, &scaleFactor) == 0 && scaleFactor != 0) - { - FreeLibrary(Shcore); - return static_cast(scaleFactor) / 100.0; - } - } - - FreeLibrary(Shcore); - } -#elif defined(HAVE_X11) - XrmInitialize(); - - if (char* const rms = XResourceManagerString(view->world->impl->display)) - { - if (const XrmDatabase sdb = XrmGetStringDatabase(rms)) - { - char* type = nullptr; - XrmValue ret; - - if (XrmGetResource(sdb, "Xft.dpi", "String", &type, &ret) - && ret.addr != nullptr - && type != nullptr - && std::strncmp("String", type, 6) == 0) - { - if (const double dpi = std::atof(ret.addr)) - return dpi / 96; - } - } - } -#else - // unused - (void)view; -#endif +// clear minimum size to 0 - return 1.0; -} +// void puglClearMinSize(PuglView* const view) +// { +// view->sizeHints[PUGL_MIN_SIZE].width = 0; +// view->sizeHints[PUGL_MIN_SIZE].height = 0; +// } // -------------------------------------------------------------------------------------------------------------------- // bring view window into the foreground, aka "raise" window @@ -284,48 +225,55 @@ void puglRaiseWindow(PuglView* const view) SetForegroundWindow(view->impl->hwnd); SetActiveWindow(view->impl->hwnd); #else - XRaiseWindow(view->impl->display, view->impl->win); + XRaiseWindow(view->world->impl->display, view->impl->win); #endif } // -------------------------------------------------------------------------------------------------------------------- -// set backend that matches current build +// get scale factor from parent window if possible, fallback to puglGetScaleFactor -void puglSetMatchingBackendForCurrentBuild(PuglView* const view) +double puglGetScaleFactorFromParent(const PuglView* const view) { -#ifdef DGL_CAIRO - puglSetBackend(view, puglCairoBackend()); -#endif -#ifdef DGL_OPENGL - puglSetBackend(view, puglGlBackend()); -#endif -#ifdef DGL_VULKAN - puglSetBackend(view, puglVulkanBackend()); + const PuglNativeView parent = view->parent ? view->parent : view->transientParent ? view->transientParent : 0; +#if defined(DISTRHO_OS_MAC) + NSWindow* const window = parent != 0 ? [(NSView*)parent window] + : view->impl->window ? view->impl->window : [view->impl->wrapperView window]); + NSScreen* const screen = window != nullptr ? [window screen] : [NSScreen mainScreen]; + return [screen backingScaleFactor]; +#elif defined(DISTRHO_OS_WINDOWS) + const HWND hwnd = parent != 0 ? (HWND)parent : view->impl->hwnd; + return puglWinGetViewScaleFactor(hwnd); +#else + return puglGetScaleFactor(view); + // unused + (void)parent; #endif - if (view->backend == nullptr) - puglSetBackend(view, puglStubBackend()); } // -------------------------------------------------------------------------------------------------------------------- -// Combine puglSetMinSize and puglSetAspectRatio +// Combined puglSetSizeHint using PUGL_MIN_SIZE and PUGL_FIXED_ASPECT PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect) { - view->minWidth = (int)width; - view->minHeight = (int)height; - - if (aspect) { - view->minAspectX = (int)width; - view->minAspectY = (int)height; - view->maxAspectX = (int)width; - view->maxAspectY = (int)height; + view->sizeHints[PUGL_MIN_SIZE].width = width; + view->sizeHints[PUGL_MIN_SIZE].height = height; + + if (aspect) + { + view->sizeHints[PUGL_FIXED_ASPECT].width = width; + view->sizeHints[PUGL_FIXED_ASPECT].height = height; } #if defined(DISTRHO_OS_MAC) - puglSetMinSize(view, width, height); + if (view->impl->window) + { + PuglStatus status; + + if ((status = updateSizeHint(view, PUGL_MIN_SIZE)) != PUGL_SUCCESS) + return status; - if (aspect) { - puglSetAspectRatio(view, width, height, width, height); + if (aspect && (status = updateSizeHint(view, PUGL_FIXED_ASPECT)) != PUGL_SUCCESS) + return status; } #elif defined(DISTRHO_OS_WINDOWS) // nothing @@ -333,92 +281,90 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co if (const PuglStatus status = updateSizeHints(view)) return status; - XFlush(view->impl->display); + XFlush(view->world->impl->display); #endif return PUGL_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- -// set window offset without changing size +// set view as resizable (or not) during runtime -PuglStatus puglSetWindowOffset(PuglView* const view, const int x, const int y) +void puglSetResizable(PuglView* const view, const bool resizable) { - // TODO custom setFrame version - PuglRect rect = puglGetFrame(view); - rect.x = x; - rect.y = y; - return puglSetFrame(view, rect); + puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); + +#if defined(DISTRHO_OS_MAC) + if (PuglWindow* const window = view->impl->window) + { + const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) + | (resizable ? NSResizableWindowMask : 0x0); + [window setStyleMask:style]; + } + // FIXME use [view setAutoresizingMask:NSViewNotSizable] ? +#elif defined(DISTRHO_OS_WINDOWS) + if (const HWND hwnd = view->impl->hwnd) + { + const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX) + : GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX); + SetWindowLong(hwnd, GWL_STYLE, winFlags); + } +#else + updateSizeHints(view); +#endif } // -------------------------------------------------------------------------------------------------------------------- -// set window size with default size and without changing frame x/y position +// set window size while also changing default -PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height) +PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) { - view->defaultWidth = width; - view->defaultHeight = height; - view->frame.width = width; - view->frame.height = height; + if (width > INT16_MAX || height > INT16_MAX) + return PUGL_BAD_PARAMETER; + + view->sizeHints[PUGL_DEFAULT_SIZE].width = view->frame.width = static_cast(width); + view->sizeHints[PUGL_DEFAULT_SIZE].height = view->frame.height = static_cast(height); #if defined(DISTRHO_OS_MAC) - // replace setFrame with setFrameSize + // mostly matches upstream pugl, simplified PuglInternals* const impl = view->impl; const PuglRect frame = view->frame; const NSRect framePx = rectToNsRect(frame); const NSRect framePt = nsRectToPoints(view, framePx); - if (impl->window) + if (PuglWindow* const window = view->impl->window) { - // Resize window to fit new content rect const NSRect screenPt = rectToScreen(viewScreen(view), framePt); - const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; - - [impl->window setFrame:winFrame display:NO]; + const NSRect winFrame = [window frameRectForContentRect:screenPt]; + [window setFrame:winFrame display:NO]; } - // Resize views const NSSize sizePx = NSMakeSize(frame.width, frame.height); const NSSize sizePt = [impl->drawView convertSizeFromBacking:sizePx]; - - [impl->wrapperView setFrameSize:(impl->window ? sizePt : framePt.size)]; + [impl->wrapperView setFrameSize:sizePt]; [impl->drawView setFrameSize:sizePt]; #elif defined(DISTRHO_OS_WINDOWS) - // matches upstream pugl, except we add SWP_NOMOVE flag - if (view->impl->hwnd) + // matches upstream pugl, except we re-enter context after resize + if (const HWND hwnd = view->impl->hwnd) { - const PuglRect frame = view->frame; - - RECT rect = { (long)frame.x, - (long)frame.y, - (long)frame.x + (long)frame.width, - (long)frame.y + (long)frame.height }; + const RECT rect = adjustedWindowRect(view, view->frame.x, view->frame.y, + static_cast(width), static_cast(height)); - AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), FALSE, puglWinGetWindowExFlags(view)); - - if (SetWindowPos(view->impl->hwnd, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER)) - { - // make sure to return context back to ourselves - view->backend->enter(view, nullptr); - return PUGL_SUCCESS; - } + if (!SetWindowPos(hwnd, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE)) + return PUGL_UNKNOWN_ERROR; - return PUGL_UNKNOWN_ERROR; + // make sure to return context back to ourselves + puglBackendEnter(view); } #else - // matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow - if (view->impl->win) + // matches upstream pugl, all in one + if (const Window window = view->impl->win) { - Display* const display = view->impl->display; + Display* const display = view->world->impl->display; - if (! XResizeWindow(display, view->impl->win, width, height)) + if (! XResizeWindow(display, window, width, height)) return PUGL_UNKNOWN_ERROR; if (const PuglStatus status = updateSizeHints(view)) @@ -456,6 +402,10 @@ void puglFallbackOnResize(PuglView* const view) glViewport(0, 0, static_cast(view->frame.width), static_cast(view->frame.height)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); +#else + return; + // unused + (void)view; #endif } @@ -603,67 +553,33 @@ void puglWin32ShowCentered(PuglView* const view) SetFocus(impl->hwnd); } -// -------------------------------------------------------------------------------------------------------------------- -// win32 specific, set or unset WS_SIZEBOX style flag - -void puglWin32SetWindowResizable(PuglView* const view, const bool resizable) -{ - PuglInternals* impl = view->impl; - DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,); - - const int winFlags = resizable ? GetWindowLong(impl->hwnd, GWL_STYLE) | WS_SIZEBOX - : GetWindowLong(impl->hwnd, GWL_STYLE) & ~WS_SIZEBOX; - SetWindowLong(impl->hwnd, GWL_STYLE, winFlags); -} - // -------------------------------------------------------------------------------------------------------------------- #elif defined(HAVE_X11) -// -------------------------------------------------------------------------------------------------------------------- -// X11 specific, safer way to grab focus - -PuglStatus puglX11GrabFocus(const PuglView* const view) -{ - const PuglInternals* const impl = view->impl; - - XWindowAttributes wa; - std::memset(&wa, 0, sizeof(wa)); - - DISTRHO_SAFE_ASSERT_RETURN(XGetWindowAttributes(impl->display, impl->win, &wa), PUGL_UNKNOWN_ERROR); - - if (wa.map_state == IsViewable) - { - XRaiseWindow(impl->display, impl->win); - XSetInputFocus(impl->display, impl->win, RevertToPointerRoot, CurrentTime); - XSync(impl->display, False); - } - - return PUGL_SUCCESS; -} - // -------------------------------------------------------------------------------------------------------------------- // X11 specific, set dialog window type and pid hints void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone) { - const PuglInternals* const impl = view->impl; + const PuglInternals* const impl = view->impl; + Display* const display = view->world->impl->display; const pid_t pid = getpid(); - const Atom _nwp = XInternAtom(impl->display, "_NET_WM_PID", False); - XChangeProperty(impl->display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); + const Atom _nwp = XInternAtom(display, "_NET_WM_PID", False); + XChangeProperty(display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); - const Atom _wt = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE", False); + const Atom _wt = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); Atom _wts[2]; int numAtoms = 0; if (! isStandalone) - _wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False); + _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); - _wts[numAtoms++] = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False); + _wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); - XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms); + XChangeProperty(display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 30613ea6..7ebbc736 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -19,7 +19,7 @@ #include "../Base.hpp" -/* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */ +/* we will include all header files used in pugl.h in their C++ friendly form, then pugl stuff in custom namespace */ #include #ifdef DISTRHO_PROPER_CPP11_SUPPORT # include @@ -29,75 +29,58 @@ # include #endif +// hidden api #define PUGL_API #define PUGL_DISABLE_DEPRECATED +#define PUGL_NO_INCLUDE_GL_H #define PUGL_NO_INCLUDE_GLU_H +// do not set extern "C" +// #define __cplusplus_backup __cplusplus +// #undef __cplusplus + +// give warning if defined as something else +// #define PUGL_BEGIN_DECLS +// #define PUGL_END_DECLS + // -------------------------------------------------------------------------------------------------------------------- -#ifndef DISTRHO_OS_MAC START_NAMESPACE_DGL -#else -USE_NAMESPACE_DGL -#endif #include "pugl-upstream/include/pugl/pugl.h" -// -------------------------------------------------------------------------------------------------------------------- - -PUGL_BEGIN_DECLS +// DGL specific, expose backend enter +bool puglBackendEnter(PuglView* view); -// expose backend enter -PUGL_API bool -puglBackendEnter(PuglView* view); +// DGL specific, expose backend leave +bool puglBackendLeave(PuglView* view); -// expose backend leave -PUGL_API void -puglBackendLeave(PuglView* view); +// DGL specific, assigns backend that matches current DGL build +void puglSetMatchingBackendForCurrentBuild(PuglView* view); // clear minimum size to 0 -PUGL_API void -puglClearMinSize(PuglView* view); - -// missing in pugl, directly returns transient parent -PUGL_API PuglNativeView -puglGetTransientParent(const PuglView* view); - -// missing in pugl, directly returns title char* pointer -PUGL_API const char* -puglGetWindowTitle(const PuglView* view); - -// get global scale factor -PUGL_API double -puglGetDesktopScaleFactor(const PuglView* view); +void puglClearMinSize(PuglView* view); // bring view window into the foreground, aka "raise" window -PUGL_API void -puglRaiseWindow(PuglView* view); +void puglRaiseWindow(PuglView* view); -// DGL specific, assigns backend that matches current DGL build -PUGL_API void -puglSetMatchingBackendForCurrentBuild(PuglView* view); +// get scale factor from parent window if possible, fallback to puglGetScaleFactor +double puglGetScaleFactorFromParent(const PuglView* view); -// Combine puglSetMinSize and puglSetAspectRatio -PUGL_API PuglStatus -puglSetGeometryConstraints(PuglView* view, uint width, uint height, bool aspect); +// combined puglSetSizeHint using PUGL_MIN_SIZE, PUGL_MIN_ASPECT and PUGL_MAX_ASPECT +PuglStatus puglSetGeometryConstraints(PuglView* view, uint width, uint height, bool aspect); -// set window offset without changing size -PUGL_API PuglStatus -puglSetWindowOffset(PuglView* view, int x, int y); +// set view as resizable (or not) during runtime +void puglSetResizable(PuglView* view, bool resizable); -// set window size with default size and without changing frame x/y position -PUGL_API PuglStatus -puglSetWindowSize(PuglView* view, uint width, uint height); +// set window size while also changing default +PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height); // DGL specific, build-specific drawing prepare -PUGL_API void -puglOnDisplayPrepare(PuglView* view); +void puglOnDisplayPrepare(PuglView* view); // DGL specific, build-specific fallback resize -PUGL_API void -puglFallbackOnResize(PuglView* view); +void puglFallbackOnResize(PuglView* view); #if defined(DISTRHO_OS_MAC) @@ -127,28 +110,18 @@ puglWin32RestoreWindow(PuglView* view); PUGL_API void puglWin32ShowCentered(PuglView* view); -// win32 specific, set or unset WS_SIZEBOX style flag -PUGL_API void -puglWin32SetWindowResizable(PuglView* view, bool resizable); - #elif defined(HAVE_X11) -// X11 specific, safer way to grab focus -PUGL_API PuglStatus -puglX11GrabFocus(const PuglView* view); - // X11 specific, set dialog window type and pid hints -PUGL_API void -puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); +void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); #endif -PUGL_END_DECLS - // -------------------------------------------------------------------------------------------------------------------- -#ifndef DISTRHO_OS_MAC END_NAMESPACE_DGL -#endif + +// #define __cplusplus __cplusplus_backup +// #undef __cplusplus_backup #endif // DGL_PUGL_HPP_INCLUDED diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index ce031e12..c0985344 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -234,7 +234,7 @@ public: #ifdef DISTRHO_PLUGIN_TARGET_VST3 void setSizeForVST3(const uint width, const uint height) { - puglSetWindowSize(pData->view, width, height); + puglSetSizeAndDefault(pData->view, width, height); } #endif diff --git a/pugl-updates-notes.txt b/pugl-updates-notes.txt new file mode 100644 index 00000000..3430c529 --- /dev/null +++ b/pugl-updates-notes.txt @@ -0,0 +1,9 @@ + +puglClearMinSize needed? +clipboard todo + +puglSetWindowSize was used on first show, still needed? + +pugl namespace details finalized + +window starts centered for screen or parent finalized From 9b228b0c5b741917aaf60989d46d5ffcaa5fce73 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 27 May 2022 23:14:57 +0100 Subject: [PATCH 376/504] Adjust clipboard API following latest pugl changes Signed-off-by: falkTX --- dgl/Base.hpp | 48 +++++++++++++++++++--------- dgl/TopLevelWidget.hpp | 2 +- dgl/Window.hpp | 41 +++++++++++++++++------- dgl/src/TopLevelWidget.cpp | 8 ++--- dgl/src/Window.cpp | 38 +++++++++++++++++++--- dgl/src/WindowPrivateData.cpp | 13 ++++++++ dgl/src/WindowPrivateData.hpp | 4 +++ dgl/src/pugl.cpp | 8 ++--- dgl/src/pugl.hpp | 33 +++++-------------- distrho/DistrhoUI.hpp | 19 ++++++++++- distrho/src/DistrhoUI.cpp | 12 ++++++- distrho/src/DistrhoUIPrivateData.hpp | 15 +++++++++ pugl-updates-notes.txt | 9 +++--- 13 files changed, 179 insertions(+), 71 deletions(-) diff --git a/dgl/Base.hpp b/dgl/Base.hpp index c49740ad..1ecdb64d 100644 --- a/dgl/Base.hpp +++ b/dgl/Base.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -159,15 +159,15 @@ enum MouseButton { This is a portable subset of mouse cursors that exist on X11, MacOS, and Windows. */ enum MouseCursor { - kMouseCursorArrow, ///< Default pointing arrow - kMouseCursorCaret, ///< Caret (I-Beam) for text entry - kMouseCursorCrosshair, ///< Cross-hair - kMouseCursorHand, ///< Hand with a pointing finger - kMouseCursorNotAllowed, ///< Operation not allowed - kMouseCursorLeftRight, ///< Left/right arrow for horizontal resize - kMouseCursorUpDown, ///< Up/down arrow for vertical resize - kMouseCursorDiagonal, ///< Top-left to bottom-right arrow for diagonal resize - kMouseCursorAntiDiagonal ///< Bottom-left to top-right arrow for diagonal resize + kMouseCursorArrow, ///< Default pointing arrow + kMouseCursorCaret, ///< Caret (I-Beam) for text entry + kMouseCursorCrosshair, ///< Cross-hair + kMouseCursorHand, ///< Hand with a pointing finger + kMouseCursorNotAllowed, ///< Operation not allowed + kMouseCursorLeftRight, ///< Left/right arrow for horizontal resize + kMouseCursorUpDown, ///< Up/down arrow for vertical resize + kMouseCursorDiagonal, ///< Top-left to bottom-right arrow for diagonal resize + kMouseCursorAntiDiagonal ///< Bottom-left to top-right arrow for diagonal resize }; /** @@ -178,11 +178,29 @@ enum MouseCursor { while a smooth scroll is for those with arbitrary scroll direction freedom, like some touchpads. */ enum ScrollDirection { - kScrollUp, ///< Scroll up - kScrollDown, ///< Scroll down - kScrollLeft, ///< Scroll left - kScrollRight, ///< Scroll right - kScrollSmooth ///< Smooth scroll in any direction + kScrollUp, ///< Scroll up + kScrollDown, ///< Scroll down + kScrollLeft, ///< Scroll left + kScrollRight, ///< Scroll right + kScrollSmooth ///< Smooth scroll in any direction +}; + +/** + A clipboard data offer. + @see Window::onClipboardDataOffer +*/ +struct ClipboardDataOffer { + /** + The id of this data offer. + @note The value 0 is reserved for null/invalid. + */ + uint32_t id; + + /** + The type of this data offer. + Usually a MIME type, but may also be another platform-specific type identifier. + */ + const char* type; }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index a9438552..47015a62 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -101,8 +101,8 @@ public: void repaint(const Rectangle& rect) noexcept; // TODO group stuff after here, convenience functions present in Window class + const void* getClipboard(size_t& dataSize); bool setClipboard(const char* mimeType, const void* data, size_t dataSize); - const void* getClipboard(const char*& mimeType, size_t& dataSize); bool setCursor(MouseCursor cursor); bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); bool removeIdleCallback(IdleCallback* callback); diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 51a7d2a3..5e80b1a9 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -23,6 +23,8 @@ # include "../distrho/extra/FileBrowserDialog.hpp" #endif +#include + START_NAMESPACE_DGL class Application; @@ -298,6 +300,16 @@ public: */ void setIgnoringKeyRepeat(bool ignore) noexcept; + /** + Get the clipboard contents. + + This gets the system clipboard contents, + which may have been set with setClipboard() or copied from another application. + + returns the clipboard contents, or null. + */ + const void* getClipboard(size_t& dataSize); + /** Set the clipboard contents. @@ -309,16 +321,6 @@ public: */ bool setClipboard(const char* mimeType, const void* data, size_t dataSize); - /** - Get the clipboard contents. - - This gets the system clipboard contents, - which may have been set with setClipboard() or copied from another application. - - returns the clipboard contents, or null. - */ - const void* getClipboard(const char*& mimeType, size_t& dataSize); - /** Set the mouse cursor. @@ -450,6 +452,23 @@ public: inline void exec(bool blockWait = false) { runAsModal(blockWait); } protected: + /** + Get the types available for the data in a clipboard. + Must only be called within the context of onClipboardDataOffer. + */ + std::vector getClipboardDataOfferTypes(); + + /** + A function called when clipboard has data present, possibly with several datatypes. + While handling this event, the data types can be investigated with getClipboardDataOfferTypes() to decide whether to accept the offer. + + Reimplement and return a non-zero id to accept the clipboard data offer for a particular type. + Applications must ignore any type they do not recognize. + + The default implementation does nothing. + */ + virtual uint32_t onClipboardDataOffer(); + /** A function called when the window is attempted to be closed. Returning true closes the window, which is the default behaviour. diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index f577a1c9..ab1fa4bf 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -60,14 +60,14 @@ void TopLevelWidget::setSize(const Size& size) pData->window.setSize(size); } -bool TopLevelWidget::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) +const void* TopLevelWidget::getClipboard(size_t& dataSize) { - return pData->window.setClipboard(mimeType, data, dataSize); + return pData->window.getClipboard(dataSize); } -const void* TopLevelWidget::getClipboard(const char*& mimeType, size_t& dataSize) +bool TopLevelWidget::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) { - return pData->window.getClipboard(mimeType, dataSize); + return pData->window.setClipboard(mimeType, data, dataSize); } bool TopLevelWidget::setCursor(const MouseCursor cursor) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index 5ac3d8a7..dee9d189 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -319,15 +319,20 @@ void Window::setIgnoringKeyRepeat(const bool ignore) noexcept puglSetViewHint(pData->view, PUGL_IGNORE_KEY_REPEAT, ignore); } -bool Window::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) +const void* Window::getClipboard(size_t& dataSize) { - return puglSetClipboard(pData->view, mimeType, data, dataSize) == PUGL_SUCCESS; + if (pData->clipboardTypeIndex == 0) + { + dataSize = 0; + return nullptr; + } + + return puglGetClipboard(pData->view, pData->clipboardTypeIndex, &dataSize); } -const void* Window::getClipboard(const char*& mimeType, size_t& dataSize) +bool Window::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) { - const void* const clipboard = nullptr; // puglGetClipboard(pData->view, &mimeType, &dataSize); - return clipboard; + return puglSetClipboard(pData->view, mimeType, data, dataSize) == PUGL_SUCCESS; } bool Window::setCursor(const MouseCursor cursor) @@ -466,6 +471,29 @@ void Window::setGeometryConstraints(uint minimumWidth, } } +std::vector Window::getClipboardDataOfferTypes() +{ + std::vector offerTypes; + + if (const uint32_t numTypes = puglGetNumClipboardTypes(pData->view)) + { + offerTypes.reserve(numTypes); + + for (uint32_t i=0; iview, i) }; + offerTypes.push_back(offer); + } + } + + return offerTypes; +} + +uint32_t Window::onClipboardDataOffer() +{ + return 0; +} + bool Window::onClose() { return true; diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 54a13673..2885704d 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -109,6 +109,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + clipboardTypeIndex(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -135,6 +136,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + clipboardTypeIndex(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -163,6 +165,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + clipboardTypeIndex(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -192,6 +195,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + clipboardTypeIndex(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -753,6 +757,13 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) #endif } +uint32_t Window::PrivateData::onClipboardDataOffer() +{ + DGL_DBG("onClipboardDataOffer\n"); + + return clipboardTypeIndex = self->onClipboardDataOffer(); +} + #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); #endif @@ -933,6 +944,8 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Data offered from clipboard, a #PuglDataOfferEvent case PUGL_DATA_OFFER: + if (const uint32_t offerId = pData->onClipboardDataOffer()) + puglAcceptOffer(view, &event->offer, offerId - 1); break; ///< Data available from clipboard, a #PuglDataEvent diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 6cb8a4d6..07a94163 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -80,6 +80,9 @@ struct Window::PrivateData : IdleCallback { /** Whether to ignore idle callback requests, useful for temporary windows. */ bool ignoreIdleCallbacks; + /** The type index returned by the last onClipboardDataOffer call. */ + uint32_t clipboardTypeIndex; + /** Render to a picture file when non-null, automatically free+unset after saving. */ char* filenameToRenderInto; @@ -182,6 +185,7 @@ struct Window::PrivateData : IdleCallback { void onPuglMouse(const Widget::MouseEvent& ev); void onPuglMotion(const Widget::MotionEvent& ev); void onPuglScroll(const Widget::ScrollEvent& ev); + uint32_t onClipboardDataOffer(); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 3f697d3d..2ff3bf9f 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -476,8 +476,8 @@ void puglMacOSShowCentered(PuglView* const view) const NSRect ourFrame = [view->impl->window frame]; const NSRect transientFrame = [transientWindow frame]; - const int x = transientFrame.origin.x + transientFrame.size.width / 2 - ourFrame.size.width / 2; - const int y = transientFrame.origin.y + transientFrame.size.height / 2 + ourFrame.size.height / 2; + const int x = transientFrame.origin.x + (transientFrame.size.width - ourFrame.size.width) / 2; + const int y = transientFrame.origin.y + (transientFrame.size.height - ourFrame.size.height) / 2; [view->impl->window setFrameTopLeftPoint:NSMakePoint(x, y)]; } @@ -543,8 +543,8 @@ void puglWin32ShowCentered(PuglView* const view) if (GetMonitorInfo(MonitorFromWindow(impl->hwnd, MONITOR_DEFAULTTOPRIMARY), &mInfo)) SetWindowPos(impl->hwnd, HWND_TOP, - mInfo.rcWork.left + (mInfo.rcWork.right - view->frame.width) / 2, - mInfo.rcWork.top + (mInfo.rcWork.bottom - view->frame.height) / 2, + mInfo.rcWork.left + (mInfo.rcWork.right - mInfo.rcWork.left - view->frame.width) / 2, + mInfo.rcWork.top + (mInfo.rcWork.bottom - mInfo.rcWork.top - view->frame.height) / 2, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); else ShowWindow(impl->hwnd, SW_NORMAL); diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 7ebbc736..610ebe82 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -35,20 +35,12 @@ #define PUGL_NO_INCLUDE_GL_H #define PUGL_NO_INCLUDE_GLU_H -// do not set extern "C" -// #define __cplusplus_backup __cplusplus -// #undef __cplusplus - -// give warning if defined as something else -// #define PUGL_BEGIN_DECLS -// #define PUGL_END_DECLS - -// -------------------------------------------------------------------------------------------------------------------- - START_NAMESPACE_DGL #include "pugl-upstream/include/pugl/pugl.h" +// -------------------------------------------------------------------------------------------------------------------- + // DGL specific, expose backend enter bool puglBackendEnter(PuglView* view); @@ -85,30 +77,24 @@ void puglFallbackOnResize(PuglView* view); #if defined(DISTRHO_OS_MAC) // macOS specific, allow standalone window to gain focus -PUGL_API void -puglMacOSActivateApp(); +void puglMacOSActivateApp(); // macOS specific, add another view's window as child -PUGL_API PuglStatus -puglMacOSAddChildWindow(PuglView* view, PuglView* child); +PuglStatus puglMacOSAddChildWindow(PuglView* view, PuglView* child); // macOS specific, remove another view's window as child -PUGL_API PuglStatus -puglMacOSRemoveChildWindow(PuglView* view, PuglView* child); +PuglStatus puglMacOSRemoveChildWindow(PuglView* view, PuglView* child); // macOS specific, center view based on parent coordinates (if there is one) -PUGL_API void -puglMacOSShowCentered(PuglView* view); +void puglMacOSShowCentered(PuglView* view); #elif defined(DISTRHO_OS_WINDOWS) // win32 specific, call ShowWindow with SW_RESTORE -PUGL_API void -puglWin32RestoreWindow(PuglView* view); +void puglWin32RestoreWindow(PuglView* view); // win32 specific, center view based on parent coordinates (if there is one) -PUGL_API void -puglWin32ShowCentered(PuglView* view); +void puglWin32ShowCentered(PuglView* view); #elif defined(HAVE_X11) @@ -121,7 +107,4 @@ void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); END_NAMESPACE_DGL -// #define __cplusplus __cplusplus_backup -// #undef __cplusplus_backup - #endif // DGL_PUGL_HPP_INCLUDED diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 1526d302..3b0bd007 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -296,6 +296,23 @@ protected: virtual void uiScaleFactorChanged(double scaleFactor); #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + /** + Get the types available for the data in a clipboard. + Must only be called within the context of uiClipboardDataOffer. + */ + std::vector getClipboardDataOfferTypes(); + + /** + Window clipboard data offer function, called when clipboard has data present, possibly with several datatypes. + While handling this event, the data types can be investigated with getClipboardDataOfferTypes() to decide whether to accept the offer. + + Reimplement and return a non-zero id to accept the clipboard data offer for a particular type. + UIs must ignore any type they do not recognize. + + The default implementation does nothing. + */ + virtual uint32_t uiClipboardDataOffer(); + /** Windows focus function, called when the window gains or loses the keyboard focus. This function is for plugin UIs to be able to override Window::onFocus(bool, CrossingMode). diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 5e98c289..f3b5c642 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -338,6 +338,16 @@ void UI::uiScaleFactorChanged(double) } #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +std::vector UI::getClipboardDataOfferTypes() +{ + return uiData->window->getClipboardDataOfferTypes(); +} + +uint32_t UI::uiClipboardDataOffer() +{ + return 0; +} + void UI::uiFocus(bool, DGL_NAMESPACE::CrossingMode) { } diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index c0985344..a2cf3084 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -238,7 +238,22 @@ public: } #endif + std::vector getClipboardDataOfferTypes() + { + return Window::getClipboardDataOfferTypes(); + } + protected: + uint32_t onClipboardDataOffer() override + { + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, 0); + + if (initializing) + return 0; + + return ui->uiClipboardDataOffer(); + } + void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); diff --git a/pugl-updates-notes.txt b/pugl-updates-notes.txt index 3430c529..8e22c015 100644 --- a/pugl-updates-notes.txt +++ b/pugl-updates-notes.txt @@ -1,9 +1,10 @@ puglClearMinSize needed? -clipboard todo - puglSetWindowSize was used on first show, still needed? +transientParentView needed? remove from WindowPrivateData -pugl namespace details finalized +update distrhoui.cpp get scale factor to match new parent request setup and pugl -window starts centered for screen or parent finalized +clipboard todo + +pugl namespace details finalized From a1fc530dd0fe2ece95e6f046690b0c82d5ccf170 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 00:51:32 +0100 Subject: [PATCH 377/504] Make new clipboard things work on X11 Signed-off-by: falkTX --- dgl/src/Window.cpp | 10 +--- dgl/src/WindowPrivateData.cpp | 90 ++++++++++++++++++++++++++++++++--- dgl/src/WindowPrivateData.hpp | 11 ++++- dgl/src/pugl-upstream | 2 +- 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index dee9d189..e44ffd18 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -321,18 +321,12 @@ void Window::setIgnoringKeyRepeat(const bool ignore) noexcept const void* Window::getClipboard(size_t& dataSize) { - if (pData->clipboardTypeIndex == 0) - { - dataSize = 0; - return nullptr; - } - - return puglGetClipboard(pData->view, pData->clipboardTypeIndex, &dataSize); + return pData->getClipboard(dataSize); } bool Window::setClipboard(const char* const mimeType, const void* const data, const size_t dataSize) { - return puglSetClipboard(pData->view, mimeType, data, dataSize) == PUGL_SUCCESS; + return puglSetClipboard(pData->view, mimeType != nullptr ? mimeType : "text/plain", data, dataSize) == PUGL_SUCCESS; } bool Window::setCursor(const MouseCursor cursor) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 2885704d..522da6b5 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -109,7 +109,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - clipboardTypeIndex(0), + waitingForClipboard(false), + clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -136,7 +137,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - clipboardTypeIndex(0), + waitingForClipboard(false), + clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -165,7 +167,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - clipboardTypeIndex(0), + waitingForClipboard(false), + clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -195,7 +198,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - clipboardTypeIndex(0), + waitingForClipboard(false), + clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED fileBrowserHandle(nullptr), @@ -757,11 +761,59 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) #endif } +const void* Window::PrivateData::getClipboard(size_t& dataSize) +{ + clipboardTypeId = 0; + waitingForClipboard = true; + + if (puglPaste(view) != PUGL_SUCCESS) + { + dataSize = 0; + waitingForClipboard = false; + return nullptr; + } + + // wait for type request + while (waitingForClipboard && clipboardTypeId == 0) + puglUpdate(appData->world, 0.03); + + if (clipboardTypeId == 0) + { + dataSize = 0; + waitingForClipboard = false; + return nullptr; + } + + // wait for actual data + while (waitingForClipboard) + puglUpdate(appData->world, 0.03); + + if (clipboardTypeId == 0) + { + dataSize = 0; + return nullptr; + } + + return puglGetClipboard(view, clipboardTypeId - 1, &dataSize); +} + uint32_t Window::PrivateData::onClipboardDataOffer() { DGL_DBG("onClipboardDataOffer\n"); - return clipboardTypeIndex = self->onClipboardDataOffer(); + if ((clipboardTypeId = self->onClipboardDataOffer()) != 0) + return clipboardTypeId; + + waitingForClipboard = false; + return 0; +} + +void Window::PrivateData::onClipboardData(const uint32_t typeId) +{ + if (clipboardTypeId != typeId) + clipboardTypeId = 0; + + waitingForClipboard = false; } #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) @@ -777,6 +829,29 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu } #endif + if (pData->waitingForClipboard) + { + switch (event->type) + { + case PUGL_UPDATE: + case PUGL_EXPOSE: + case PUGL_FOCUS_IN: + case PUGL_FOCUS_OUT: + case PUGL_KEY_PRESS: + case PUGL_KEY_RELEASE: + case PUGL_TEXT: + case PUGL_POINTER_IN: + case PUGL_POINTER_OUT: + case PUGL_BUTTON_PRESS: + case PUGL_BUTTON_RELEASE: + case PUGL_MOTION: + case PUGL_SCROLL: + return PUGL_SUCCESS; + default: + break; + } + } + switch (event->type) { ///< No event @@ -944,12 +1019,13 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< Data offered from clipboard, a #PuglDataOfferEvent case PUGL_DATA_OFFER: - if (const uint32_t offerId = pData->onClipboardDataOffer()) - puglAcceptOffer(view, &event->offer, offerId - 1); + if (const uint32_t offerTypeId = pData->onClipboardDataOffer()) + puglAcceptOffer(view, &event->offer, offerTypeId - 1); break; ///< Data available from clipboard, a #PuglDataEvent case PUGL_DATA: + pData->onClipboardData(event->data.typeIndex + 1); break; } diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 07a94163..f4912cab 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -80,8 +80,11 @@ struct Window::PrivateData : IdleCallback { /** Whether to ignore idle callback requests, useful for temporary windows. */ bool ignoreIdleCallbacks; - /** The type index returned by the last onClipboardDataOffer call. */ - uint32_t clipboardTypeIndex; + /** Whether we are waiting to receive clipboard data, ignoring some events in the process. */ + bool waitingForClipboard; + + /** The type id returned by the last onClipboardDataOffer call. */ + uint32_t clipboardTypeId; /** Render to a picture file when non-null, automatically free+unset after saving. */ char* filenameToRenderInto; @@ -185,7 +188,11 @@ struct Window::PrivateData : IdleCallback { void onPuglMouse(const Widget::MouseEvent& ev); void onPuglMotion(const Widget::MotionEvent& ev); void onPuglScroll(const Widget::ScrollEvent& ev); + + // clipboard related handling + const void* getClipboard(size_t& dataSize); uint32_t onClipboardDataOffer(); + void onClipboardData(uint32_t typeId); // Pugl event handling entry point static PuglStatus puglEventCallback(PuglView* view, const PuglEvent* event); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 9691a681..7bb0bd67 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 9691a6810283ffd5a99b1cc974fc638e80f94979 +Subproject commit 7bb0bd672489ac0d0851dfd1b4980b33b7a9f48d From bbe215891be46171d05fbbbee222994793bd2213 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 01:46:17 +0100 Subject: [PATCH 378/504] Fix macOS build, adjust cairo include --- dgl/src/pugl.cpp | 9 ++------- dgl/src/pugl.hpp | 4 ++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 2ff3bf9f..c62e0f11 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -20,7 +20,7 @@ // include base headers #ifdef DGL_CAIRO -# include +# include #endif #ifdef DGL_OPENGL # include "../OpenGL-include.hpp" @@ -42,7 +42,6 @@ # include # include # ifdef DGL_CAIRO -# include # include # endif # ifdef DGL_OPENGL @@ -50,7 +49,6 @@ # endif # ifdef DGL_VULKAN # import -# include # include # endif #elif defined(DISTRHO_OS_WINDOWS) @@ -59,7 +57,6 @@ # include # include # ifdef DGL_CAIRO -# include # include # endif # ifdef DGL_OPENGL @@ -113,8 +110,6 @@ #ifndef DISTRHO_OS_MAC START_NAMESPACE_DGL -#else -USE_NAMESPACE_DGL #endif // -------------------------------------------------------------------------------------------------------------------- @@ -237,7 +232,7 @@ double puglGetScaleFactorFromParent(const PuglView* const view) const PuglNativeView parent = view->parent ? view->parent : view->transientParent ? view->transientParent : 0; #if defined(DISTRHO_OS_MAC) NSWindow* const window = parent != 0 ? [(NSView*)parent window] - : view->impl->window ? view->impl->window : [view->impl->wrapperView window]); + : view->impl->window ? view->impl->window : [view->impl->wrapperView window]; NSScreen* const screen = window != nullptr ? [window screen] : [NSScreen mainScreen]; return [screen backingScaleFactor]; #elif defined(DISTRHO_OS_WINDOWS) diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 610ebe82..1e48a324 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -35,7 +35,9 @@ #define PUGL_NO_INCLUDE_GL_H #define PUGL_NO_INCLUDE_GLU_H +#ifndef DISTRHO_OS_MAC START_NAMESPACE_DGL +#endif #include "pugl-upstream/include/pugl/pugl.h" @@ -105,6 +107,8 @@ void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); // -------------------------------------------------------------------------------------------------------------------- +#ifndef DISTRHO_OS_MAC END_NAMESPACE_DGL +#endif #endif // DGL_PUGL_HPP_INCLUDED From 114031ee99257b7c02cfc66cf01d56bef3c6d315 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 03:02:08 +0100 Subject: [PATCH 379/504] Allow to skip building file browser, even if enabled Signed-off-by: falkTX --- distrho/src/DistrhoUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index f3b5c642..2fe43a1b 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -17,7 +17,7 @@ #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" -#if !defined(DGL_FILE_BROWSER_DISABLED) && !defined(DISTRHO_OS_MAC) +#if !defined(DGL_FILE_BROWSER_DISABLED) && !defined(DISTRHO_UI_FILE_BROWSER) && !defined(DISTRHO_OS_MAC) # define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION # define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) # define DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE Plugin From fdbc69f0ba38c212ee160e70c61cce710959e4f9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 03:49:45 +0100 Subject: [PATCH 380/504] Make the default onClipboardDataOffer more friendly Signed-off-by: falkTX --- dgl/Window.hpp | 7 +++++-- dgl/src/Window.cpp | 9 +++++++++ distrho/DistrhoUI.hpp | 2 +- distrho/src/DistrhoUI.cpp | 9 +++++++++ pugl-updates-notes.txt | 4 ---- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 5e80b1a9..bc9df247 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -306,7 +306,10 @@ public: This gets the system clipboard contents, which may have been set with setClipboard() or copied from another application. - returns the clipboard contents, or null. + Returns the clipboard contents, or null. + + @note By default only "text/plain" mimetype is supported and returned. + Override onClipboardDataOffer for supporting other types. */ const void* getClipboard(size_t& dataSize); @@ -465,7 +468,7 @@ protected: Reimplement and return a non-zero id to accept the clipboard data offer for a particular type. Applications must ignore any type they do not recognize. - The default implementation does nothing. + The default implementation accepts the "text/plain" mimetype. */ virtual uint32_t onClipboardDataOffer(); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index e44ffd18..b65261ff 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -485,6 +485,15 @@ std::vector Window::getClipboardDataOfferTypes() uint32_t Window::onClipboardDataOffer() { + std::vector offers(getClipboardDataOfferTypes()); + + for (std::vector::iterator it=offers.begin(), end=offers.end(); it != end;++it) + { + const ClipboardDataOffer offer = *it; + if (std::strcmp(offer.type, "text/plain") == 0) + return offer.id; + } + return 0; } diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 3b0bd007..7b0dafda 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -309,7 +309,7 @@ protected: Reimplement and return a non-zero id to accept the clipboard data offer for a particular type. UIs must ignore any type they do not recognize. - The default implementation does nothing. + The default implementation accepts the "text/plain" mimetype. */ virtual uint32_t uiClipboardDataOffer(); diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 2fe43a1b..9a5ef855 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -345,6 +345,15 @@ std::vector UI::getClipboardDataOfferTypes() uint32_t UI::uiClipboardDataOffer() { + std::vector offers(uiData->window->getClipboardDataOfferTypes()); + + for (std::vector::iterator it=offers.begin(), end=offers.end(); it != end;++it) + { + const ClipboardDataOffer offer = *it; + if (std::strcmp(offer.type, "text/plain") == 0) + return offer.id; + } + return 0; } diff --git a/pugl-updates-notes.txt b/pugl-updates-notes.txt index 8e22c015..e33b6a0e 100644 --- a/pugl-updates-notes.txt +++ b/pugl-updates-notes.txt @@ -4,7 +4,3 @@ puglSetWindowSize was used on first show, still needed? transientParentView needed? remove from WindowPrivateData update distrhoui.cpp get scale factor to match new parent request setup and pugl - -clipboard todo - -pugl namespace details finalized From 4f3d99304f8d9cc583aa737e40a1716dff37e021 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 03:56:17 +0100 Subject: [PATCH 381/504] Fix no namespace build Signed-off-by: falkTX --- distrho/src/DistrhoUI.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 9a5ef855..8d0f3403 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -345,11 +345,11 @@ std::vector UI::getClipboardDataOfferTypes() uint32_t UI::uiClipboardDataOffer() { - std::vector offers(uiData->window->getClipboardDataOfferTypes()); + std::vector offers(uiData->window->getClipboardDataOfferTypes()); - for (std::vector::iterator it=offers.begin(), end=offers.end(); it != end;++it) + for (std::vector::iterator it=offers.begin(), end=offers.end(); it != end;++it) { - const ClipboardDataOffer offer = *it; + const DGL_NAMESPACE::ClipboardDataOffer offer = *it; if (std::strcmp(offer.type, "text/plain") == 0) return offer.id; } From 6c832980f6090a8bf1d2a4abf522b3ad3c709908 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 14:49:02 +0100 Subject: [PATCH 382/504] Cleanup file dialog namespaces, add DISTRHO_UI_FILE_BROWSER Signed-off-by: falkTX --- dgl/Application.hpp | 12 +- dgl/FileBrowserDialog.hpp | 28 +++++ dgl/Window.hpp | 20 +-- dgl/src/WindowPrivateData.hpp | 7 +- dgl/src/pugl.cpp | 6 +- distrho/DistrhoUI.hpp | 16 ++- distrho/DistrhoUI_macOS.mm | 2 +- distrho/extra/FileBrowserDialog.hpp | 110 +--------------- ...erDialog.cpp => FileBrowserDialogImpl.cpp} | 29 +++-- distrho/extra/FileBrowserDialogImpl.hpp | 117 ++++++++++++++++++ distrho/src/DistrhoPluginChecks.h | 19 ++- distrho/src/DistrhoUI.cpp | 49 ++++---- distrho/src/DistrhoUIPrivateData.hpp | 51 ++------ examples/Info/DistrhoPluginInfo.h | 1 + pugl-updates-notes.txt | 1 - 15 files changed, 254 insertions(+), 214 deletions(-) create mode 100644 dgl/FileBrowserDialog.hpp rename distrho/extra/{FileBrowserDialog.cpp => FileBrowserDialogImpl.cpp} (97%) create mode 100644 distrho/extra/FileBrowserDialogImpl.hpp diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 28404e39..37a91b76 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -19,6 +19,12 @@ #include "Base.hpp" +#ifdef DISTRHO_NAMESPACE +START_NAMESPACE_DISTRHO +class PluginApplication; +END_NAMESPACE_DISTRHO +#endif + START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- @@ -116,8 +122,10 @@ public: private: struct PrivateData; PrivateData* const pData; - friend class PluginApplication; friend class Window; + #ifdef DISTRHO_NAMESPACE + friend class DISTRHO_NAMESPACE::PluginApplication; + #endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Application) }; diff --git a/dgl/FileBrowserDialog.hpp b/dgl/FileBrowserDialog.hpp new file mode 100644 index 00000000..05c53e04 --- /dev/null +++ b/dgl/FileBrowserDialog.hpp @@ -0,0 +1,28 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED +#define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED + +#include "Base.hpp" + +START_NAMESPACE_DGL + +#include "../distrho/extra/FileBrowserDialogImpl.hpp" + +END_NAMESPACE_DGL + +#endif // DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED diff --git a/dgl/Window.hpp b/dgl/Window.hpp index bc9df247..2d5804f4 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -20,15 +20,20 @@ #include "Geometry.hpp" #ifndef DGL_FILE_BROWSER_DISABLED -# include "../distrho/extra/FileBrowserDialog.hpp" +# include "FileBrowserDialog.hpp" #endif #include +#ifdef DISTRHO_NAMESPACE +START_NAMESPACE_DISTRHO +class PluginWindow; +END_NAMESPACE_DISTRHO +#endif + START_NAMESPACE_DGL class Application; -class PluginWindow; class TopLevelWidget; // ----------------------------------------------------------------------- @@ -59,11 +64,6 @@ class DISTRHO_API Window struct PrivateData; public: -#ifndef DGL_FILE_BROWSER_DISABLED - typedef DISTRHO_NAMESPACE::FileBrowserHandle FileBrowserHandle; - typedef DISTRHO_NAMESPACE::FileBrowserOptions FileBrowserOptions; -#endif - /** Window graphics context as a scoped struct. This class gives graphics context drawing time to a window's widgets. @@ -400,7 +400,7 @@ public: This function does not block the event loop. */ - bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); + bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options = FileBrowserOptions()); #endif /** @@ -521,8 +521,10 @@ protected: private: PrivateData* const pData; friend class Application; - friend class PluginWindow; friend class TopLevelWidget; + #ifdef DISTRHO_NAMESPACE + friend class DISTRHO_NAMESPACE::PluginWindow; + #endif /** @internal */ explicit Window(Application& app, diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index f4912cab..c204f7a5 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -44,9 +44,6 @@ struct Window::PrivateData : IdleCallback { /** Pugl view instance. */ PuglView* view; - /** Pugl view instance of the transient parent window. */ -// PuglView* const transientParentView; - /** Reserved space for graphics context. */ mutable uint8_t graphicsContext[sizeof(void*)]; @@ -91,7 +88,7 @@ struct Window::PrivateData : IdleCallback { #ifndef DGL_FILE_BROWSER_DISABLED /** Handle for file browser dialog operations. */ - FileBrowserHandle fileBrowserHandle; + DGL_NAMESPACE::FileBrowserHandle fileBrowserHandle; #endif /** Modal window setup. */ @@ -168,7 +165,7 @@ struct Window::PrivateData : IdleCallback { #ifndef DGL_FILE_BROWSER_DISABLED // file handling - bool openFileBrowser(const FileBrowserOptions& options); + bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options); #endif static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index c62e0f11..b33eeb96 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -101,10 +101,12 @@ #endif #ifndef DGL_FILE_BROWSER_DISABLED +# define FILE_BROWSER_DIALOG_DGL_NAMESPACE +# include "../FileBrowserDialog.hpp" # ifdef DISTRHO_OS_MAC -# import "../../distrho/extra/FileBrowserDialog.cpp" +# import "../../distrho/extra/FileBrowserDialogImpl.cpp" # else -# include "../../distrho/extra/FileBrowserDialog.cpp" +# include "../../distrho/extra/FileBrowserDialogImpl.cpp" # endif #endif diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 7b0dafda..950077ba 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -48,16 +48,14 @@ typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget; typedef DGL_NAMESPACE::TopLevelWidget UIWidget; #endif -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER # include "extra/FileBrowserDialog.hpp" #endif -START_NAMESPACE_DGL -class PluginWindow; -END_NAMESPACE_DGL - START_NAMESPACE_DISTRHO +class PluginWindow; + /* ------------------------------------------------------------------------------------------------------------ * DPF UI */ @@ -185,7 +183,7 @@ public: void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); #endif -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER /** Open a file browser dialog with this window as transient parent.@n A few options can be specified to setup the dialog. @@ -198,7 +196,7 @@ public: @note This is exactly the same API as provided by the Window class, but redeclared here so that non-embed/DGL based UIs can still use file browser related functions. */ - bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions()); + bool openFileBrowser(const DISTRHO_NAMESPACE::FileBrowserOptions& options = FileBrowserOptions()); #endif #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS @@ -334,7 +332,7 @@ protected: virtual void uiReshape(uint width, uint height); #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER /** Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser(). This function is for plugin UIs to be able to override Window::onFileSelected(const char*). @@ -371,7 +369,7 @@ protected: private: struct PrivateData; PrivateData* const uiData; - friend class DGL_NAMESPACE::PluginWindow; + friend class PluginWindow; friend class UIExporter; #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI /** @internal */ diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index 24f24f37..dc44083c 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -26,7 +26,7 @@ # import # include # include -# ifndef DGL_FILE_BROWSER_DISABLED +# if DISTRHO_UI_FILE_BROWSER # import "extra/FileBrowserDialog.cpp" # endif diff --git a/distrho/extra/FileBrowserDialog.hpp b/distrho/extra/FileBrowserDialog.hpp index 2074e4cb..786f2804 100644 --- a/distrho/extra/FileBrowserDialog.hpp +++ b/distrho/extra/FileBrowserDialog.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -21,113 +21,7 @@ START_NAMESPACE_DISTRHO -// -------------------------------------------------------------------------------------------------------------------- -// File Browser Dialog stuff - -struct FileBrowserData; -typedef FileBrowserData* FileBrowserHandle; - -// -------------------------------------------------------------------------------------------------------------------- - -/** - File browser options, for customizing the file browser dialog.@n - By default the file browser dialog will be work as "open file" in the current working directory. -*/ -struct FileBrowserOptions { - /** Whether we are saving, opening files otherwise (default) */ - bool saving; - - /** Start directory, uses current working directory if null */ - const char* startDir; - - /** File browser dialog window title, uses "FileBrowser" if null */ - const char* title; - - // TODO file filter - - /** - File browser button state. - This allows to customize the behaviour of the file browse dialog buttons. - Note these are merely hints, not all systems support them. - */ - enum ButtonState { - kButtonInvisible, - kButtonVisibleUnchecked, - kButtonVisibleChecked, - }; - - /** - File browser buttons. - */ - struct Buttons { - /** Whether to list all files vs only those with matching file extension */ - ButtonState listAllFiles; - /** Whether to show hidden files */ - ButtonState showHidden; - /** Whether to show list of places (bookmarks) */ - ButtonState showPlaces; - - /** Constructor for default values */ - Buttons() - : listAllFiles(kButtonVisibleChecked), - showHidden(kButtonVisibleUnchecked), - showPlaces(kButtonVisibleChecked) {} - } buttons; - - /** Constructor for default values */ - FileBrowserOptions() - : saving(false), - startDir(nullptr), - title(nullptr), - buttons() {} -}; - -// -------------------------------------------------------------------------------------------------------------------- - -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -namespace DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE { -#endif - -/** - Create a new file browser dialog. - - @p isEmbed: Whether the window this dialog belongs to is an embed/child window (needed to close dialog on Windows) - @p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*) - @p scaleFactor: Scale factor to use (only used on X11) - @p options: Extra options, optional - By default the file browser dialog will be work as "open file" in the current working directory. -*/ -FileBrowserHandle fileBrowserCreate(bool isEmbed, - uintptr_t windowId, - double scaleFactor, - const FileBrowserOptions& options = FileBrowserOptions()); - -/** - Idle the file browser dialog handle.@n - Returns true if dialog was closed (with or without a file selection), - in which case the handle must not be used afterwards. - You can then call fileBrowserGetPath to know the selected file (or null if cancelled). -*/ -bool fileBrowserIdle(const FileBrowserHandle handle); - -/** - Close the file browser dialog, handle must not be used afterwards. -*/ -void fileBrowserClose(const FileBrowserHandle handle); - -/** - Get the path chosen by the user or null.@n - Should only be called after fileBrowserIdle returns true. -*/ -const char* fileBrowserGetPath(const FileBrowserHandle handle); - -// -------------------------------------------------------------------------------------------------------------------- - -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -} -#endif - -// -------------------------------------------------------------------------------------------------------------------- +#include "FileBrowserDialogImpl.hpp" END_NAMESPACE_DISTRHO diff --git a/distrho/extra/FileBrowserDialog.cpp b/distrho/extra/FileBrowserDialogImpl.cpp similarity index 97% rename from distrho/extra/FileBrowserDialog.cpp rename to distrho/extra/FileBrowserDialogImpl.cpp index df109341..a1b73737 100644 --- a/distrho/extra/FileBrowserDialog.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -14,7 +14,13 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "FileBrowserDialog.hpp" +#if !defined(DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED) && !defined(DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED) +# error bad include +#endif +#if !defined(FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE) && !defined(FILE_BROWSER_DIALOG_DGL_NAMESPACE) +# error bad usage +#endif + #include "ScopedPointer.hpp" #include "String.hpp" @@ -40,7 +46,11 @@ # include "sofd/libsofd.c" #endif +#ifdef FILE_BROWSER_DIALOG_DGL_NAMESPACE +START_NAMESPACE_DGL +#else START_NAMESPACE_DISTRHO +#endif // -------------------------------------------------------------------------------------------------------------------- @@ -260,12 +270,6 @@ struct FileBrowserData { // -------------------------------------------------------------------------------------------------------------------- -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -namespace DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE { -#endif - -// -------------------------------------------------------------------------------------------------------------------- - FileBrowserHandle fileBrowserCreate(const bool isEmbed, const uintptr_t windowId, const double scaleFactor, @@ -629,8 +633,11 @@ const char* fileBrowserGetPath(const FileBrowserHandle handle) // -------------------------------------------------------------------------------------------------------------------- -#ifdef DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE -} +#ifdef FILE_BROWSER_DIALOG_DGL_NAMESPACE +END_NAMESPACE_DGL +#else +END_NAMESPACE_DISTRHO #endif -END_NAMESPACE_DISTRHO +#undef FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE +#undef FILE_BROWSER_DIALOG_DGL_NAMESPACE diff --git a/distrho/extra/FileBrowserDialogImpl.hpp b/distrho/extra/FileBrowserDialogImpl.hpp new file mode 100644 index 00000000..e15cfef0 --- /dev/null +++ b/distrho/extra/FileBrowserDialogImpl.hpp @@ -0,0 +1,117 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED) && !defined(DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED) +# error bad include +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// File Browser Dialog stuff + +struct FileBrowserData; +typedef FileBrowserData* FileBrowserHandle; + +// -------------------------------------------------------------------------------------------------------------------- + +/** + File browser options, for customizing the file browser dialog.@n + By default the file browser dialog will be work as "open file" in the current working directory. +*/ +struct FileBrowserOptions { + /** Whether we are saving, opening files otherwise (default) */ + bool saving; + + /** Start directory, uses current working directory if null */ + const char* startDir; + + /** File browser dialog window title, uses "FileBrowser" if null */ + const char* title; + + // TODO file filter + + /** + File browser button state. + This allows to customize the behaviour of the file browse dialog buttons. + Note these are merely hints, not all systems support them. + */ + enum ButtonState { + kButtonInvisible, + kButtonVisibleUnchecked, + kButtonVisibleChecked, + }; + + /** + File browser buttons. + */ + struct Buttons { + /** Whether to list all files vs only those with matching file extension */ + ButtonState listAllFiles; + /** Whether to show hidden files */ + ButtonState showHidden; + /** Whether to show list of places (bookmarks) */ + ButtonState showPlaces; + + /** Constructor for default values */ + Buttons() + : listAllFiles(kButtonVisibleChecked), + showHidden(kButtonVisibleUnchecked), + showPlaces(kButtonVisibleChecked) {} + } buttons; + + /** Constructor for default values */ + FileBrowserOptions() + : saving(false), + startDir(nullptr), + title(nullptr), + buttons() {} +}; + +// -------------------------------------------------------------------------------------------------------------------- + +/** + Create a new file browser dialog. + + @p isEmbed: Whether the window this dialog belongs to is an embed/child window (needed to close dialog on Windows) + @p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*) + @p scaleFactor: Scale factor to use (only used on X11) + @p options: Extra options, optional + By default the file browser dialog will be work as "open file" in the current working directory. +*/ +FileBrowserHandle fileBrowserCreate(bool isEmbed, + uintptr_t windowId, + double scaleFactor, + const FileBrowserOptions& options = FileBrowserOptions()); + +/** + Idle the file browser dialog handle.@n + Returns true if dialog was closed (with or without a file selection), + in which case the handle must not be used afterwards. + You can then call fileBrowserGetPath to know the selected file (or null if cancelled). +*/ +bool fileBrowserIdle(const FileBrowserHandle handle); + +/** + Close the file browser dialog, handle must not be used afterwards. +*/ +void fileBrowserClose(const FileBrowserHandle handle); + +/** + Get the path chosen by the user or null.@n + Should only be called after fileBrowserIdle returns true. +*/ +const char* fileBrowserGetPath(const FileBrowserHandle handle); + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index d3d4b174..d2df9f89 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -90,6 +90,14 @@ # define DISTRHO_PLUGIN_WANT_TIMEPOS 0 #endif +#ifndef DISTRHO_UI_FILE_BROWSER +# if defined(DGL_FILE_BROWSER_DISABLED) || DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# define DISTRHO_UI_FILE_BROWSER 0 +# else +# define DISTRHO_UI_FILE_BROWSER 1 +# endif +#endif + #ifndef DISTRHO_UI_USER_RESIZABLE # define DISTRHO_UI_USER_RESIZABLE 0 #endif @@ -154,7 +162,16 @@ #endif // ----------------------------------------------------------------------- -// Disable UI if DGL or External UI is not available +// Disable file browser if using external UI + +#if DISTRHO_UI_FILE_BROWSER && DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# warning file browser APIs do not work for external UIs +# undef DISTRHO_UI_FILE_BROWSER 0 +# define DISTRHO_UI_FILE_BROWSER 0 +#endif + +// ----------------------------------------------------------------------- +// Disable UI if DGL or external UI is not available #if (defined(DGL_CAIRO) && ! defined(HAVE_CAIRO)) || (defined(DGL_OPENGL) && ! defined(HAVE_OPENGL)) # undef DISTRHO_PLUGIN_HAS_EMBED_UI diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 8d0f3403..87788eb1 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -17,26 +17,27 @@ #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" -#if !defined(DGL_FILE_BROWSER_DISABLED) && !defined(DISTRHO_UI_FILE_BROWSER) && !defined(DISTRHO_OS_MAC) +#if DISTRHO_UI_FILE_BROWSER && !defined(DISTRHO_OS_MAC) # define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION # define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) -# define DISTRHO_FILE_BROWSER_DIALOG_EXTRA_NAMESPACE Plugin -# define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_add_recent) -# define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_buttons) -# define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_cfg_filter_callback) -# define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_close) -# define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_configure) -# define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_filename) -# define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_free_recent) -# define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_handle_events) -# define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_load_recent) -# define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_at) -# define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_count) -# define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_recent_file) -# define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_save_recent) -# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_show) -# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(Plugin, x_fib_status) -# include "../extra/FileBrowserDialog.cpp" +# define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_add_recent) +# define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_buttons) +# define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_filter_callback) +# define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_close) +# define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_configure) +# define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_filename) +# define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_free_recent) +# define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_handle_events) +# define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_load_recent) +# define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_at) +# define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_count) +# define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_file) +# define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_save_recent) +# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_show) +# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_status) +# define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE +# include "../extra/FileBrowserDialog.hpp" +# include "../extra/FileBrowserDialogImpl.cpp" #endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -278,16 +279,10 @@ void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) } #endif -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER bool UI::openFileBrowser(const FileBrowserOptions& options) { -# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI - // TODO - return false; - (void)options; -# else - return getWindow().openFileBrowser(options); -# endif + return getWindow().openFileBrowser((DGL_NAMESPACE::FileBrowserOptions&)options); } #endif @@ -368,7 +363,7 @@ void UI::uiReshape(uint, uint) } #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#ifndef DGL_FILE_BROWSER_DISABLED +#if DISTRHO_UI_FILE_BROWSER void UI::uiFileBrowserSelected(const char*) { } diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index a2cf3084..0ae5a4fb 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -48,13 +48,7 @@ # define DISTRHO_UI_USER_RESIZABLE 0 #endif -// ----------------------------------------------------------------------- - -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI START_NAMESPACE_DISTRHO -#else -START_NAMESPACE_DGL -#endif // ----------------------------------------------------------------------- // Plugin Application, will set class name based on plugin details @@ -107,11 +101,11 @@ struct PluginApplication DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) }; #else -class PluginApplication : public Application +class PluginApplication : public DGL_NAMESPACE::Application { public: explicit PluginApplication() - : Application(DISTRHO_UI_IS_STANDALONE) + : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE) { const char* const className = ( #ifdef DISTRHO_PLUGIN_BRAND @@ -172,14 +166,14 @@ public: DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) }; #else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -class PluginWindow : public Window +class PluginWindow : public DGL_NAMESPACE::Window { - DISTRHO_NAMESPACE::UI* const ui; + UI* const ui; bool initializing; bool receivedReshapeDuringInit; public: - explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr, + explicit PluginWindow(UI* const uiPtr, PluginApplication& app, const uintptr_t parentWindowHandle, const uint width, @@ -238,7 +232,7 @@ public: } #endif - std::vector getClipboardDataOfferTypes() + std::vector getClipboardDataOfferTypes() { return Window::getClipboardDataOfferTypes(); } @@ -287,7 +281,7 @@ protected: ui->uiScaleFactorChanged(scaleFactor); } -# ifndef DGL_FILE_BROWSER_DISABLED +# if DISTRHO_UI_FILE_BROWSER void onFileSelected(const char* filename) override; # endif @@ -295,21 +289,6 @@ protected: }; #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI -END_NAMESPACE_DISTRHO -#else -END_NAMESPACE_DGL -#endif - -// ----------------------------------------------------------------------- - -START_NAMESPACE_DISTRHO - -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI -using DGL_NAMESPACE::PluginApplication; -using DGL_NAMESPACE::PluginWindow; -#endif - // ----------------------------------------------------------------------- // UI callbacks @@ -465,7 +444,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key); title[sizeof(title)-1u] = '\0'; - FileBrowserOptions opts; + DGL_NAMESPACE::FileBrowserOptions opts; opts.title = title; return window->openFileBrowser(opts); #endif @@ -473,14 +452,10 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) return false; } -END_NAMESPACE_DISTRHO - // ----------------------------------------------------------------------- // PluginWindow onFileSelected that require UI::PrivateData definitions -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) -START_NAMESPACE_DGL - +#if DISTRHO_UI_FILE_BROWSER inline void PluginWindow::onFileSelected(const char* const filename) { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); @@ -488,7 +463,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) if (initializing) return; -# if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE if (char* const key = ui->uiData->uiStateFileKeyRequest) { ui->uiData->uiStateFileKeyRequest = nullptr; @@ -502,14 +477,14 @@ inline void PluginWindow::onFileSelected(const char* const filename) std::free(key); return; } -# endif + #endif ui->uiFileBrowserSelected(filename); } - -END_NAMESPACE_DGL #endif // ----------------------------------------------------------------------- +END_NAMESPACE_DISTRHO + #endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED diff --git a/examples/Info/DistrhoPluginInfo.h b/examples/Info/DistrhoPluginInfo.h index 4c22069e..6a1096b1 100644 --- a/examples/Info/DistrhoPluginInfo.h +++ b/examples/Info/DistrhoPluginInfo.h @@ -26,6 +26,7 @@ #define DISTRHO_PLUGIN_NUM_INPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_WANT_TIMEPOS 1 +#define DISTRHO_UI_FILE_BROWSER 0 #define DISTRHO_UI_USER_RESIZABLE 1 #define DISTRHO_UI_USE_NANOVG 1 diff --git a/pugl-updates-notes.txt b/pugl-updates-notes.txt index e33b6a0e..3cf480ae 100644 --- a/pugl-updates-notes.txt +++ b/pugl-updates-notes.txt @@ -1,6 +1,5 @@ puglClearMinSize needed? puglSetWindowSize was used on first show, still needed? -transientParentView needed? remove from WindowPrivateData update distrhoui.cpp get scale factor to match new parent request setup and pugl From 638690ed6abeaf995492158b759028525769dcf0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 14:53:24 +0100 Subject: [PATCH 383/504] Fix DISTRHO_UI_FILE_BROWSER for macOS Signed-off-by: falkTX --- distrho/DistrhoUI_macOS.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index dc44083c..dcc77a3a 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -27,7 +27,9 @@ # include # include # if DISTRHO_UI_FILE_BROWSER -# import "extra/FileBrowserDialog.cpp" +# define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE +# include "extra/FileBrowserDialog.hpp" +# import "extra/FileBrowserDialogImpl.cpp" # endif // Declared in DistrhoUI.cpp but defined here because it uses Obj-C From a106c2b8cece8b5b25100d51ca7d86ebda649157 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 15:00:40 +0100 Subject: [PATCH 384/504] Fix no namespace build Signed-off-by: falkTX --- distrho/extra/FileBrowserDialogImpl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index a1b73737..bb15b3ac 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -48,6 +48,8 @@ #ifdef FILE_BROWSER_DIALOG_DGL_NAMESPACE START_NAMESPACE_DGL +using DISTRHO_NAMESPACE::ScopedPointer; +using DISTRHO_NAMESPACE::String; #else START_NAMESPACE_DISTRHO #endif From 7f65db5dd30ffd11525c5e1e15d6b3bb3e72f85c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 28 May 2022 15:17:01 +0100 Subject: [PATCH 385/504] Bring back Window::setTransientParent, used in Carla Signed-off-by: falkTX --- dgl/Window.hpp | 8 ++++++++ dgl/src/Window.cpp | 12 +++++------- distrho/src/DistrhoUIInternal.hpp | 8 +++----- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 2d5804f4..c32ba0fd 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -442,6 +442,14 @@ public: bool automaticallyScale = false, bool resizeNowIfAutoScaling = true); + /** + Set the transient parent of the window. + + Set this for transient children like dialogs, to have them properly associated with their parent window. + This should be not be called for embed windows, or after making the window visible. + */ + void setTransientParent(uintptr_t transientParentWindowHandle); + /** DEPRECATED Use isIgnoringKeyRepeat(). */ DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()") inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index b65261ff..a068038e 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -465,6 +465,11 @@ void Window::setGeometryConstraints(uint minimumWidth, } } +void Window::setTransientParent(const uintptr_t transientParentWindowHandle) +{ + puglSetTransientParent(pData->view, transientParentWindowHandle); +} + std::vector Window::getClipboardDataOfferTypes() { std::vector offerTypes; @@ -521,13 +526,6 @@ void Window::onFileSelected(const char*) } #endif -#if 0 -void Window::setTransientWinId(const uintptr_t winId) -{ - puglSetTransientFor(pData->view, winId); -} -#endif - // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index fb36f6e3..4c1c877d 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -312,14 +312,12 @@ public: uiData->window->setTitle(uiTitle); } - void setWindowTransientWinId(const uintptr_t winId) + void setWindowTransientWinId(const uintptr_t transientParentWindowHandle) { #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI - ui->setTransientWindowId(winId); -#elif 0 /* TODO */ - glWindow.setTransientWinId(winId); + ui->setTransientWindowId(transientParentWindowHandle); #else - (void)winId; + uiData->window->setTransientParent(transientParentWindowHandle); #endif } From 3de334d26cce42808314d53eddcee99a959dda85 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 01:13:23 +0100 Subject: [PATCH 386/504] Tweaks to special macOS builds Signed-off-by: falkTX --- distrho/DistrhoUI_macOS.mm | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index dcc77a3a..759e65e0 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -21,18 +21,20 @@ #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" -#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI - +#if DISTRHO_UI_FILE_BROWSER || DISTRHO_PLUGIN_HAS_EXTERNAL_UI # import +#endif + +// A few utils declared in DistrhoUI.cpp but defined here because they uses Obj-C +#if DISTRHO_UI_FILE_BROWSER +# define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE +# include "extra/FileBrowserDialog.hpp" +# import "extra/FileBrowserDialogImpl.cpp" +#endif + +#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # include # include -# if DISTRHO_UI_FILE_BROWSER -# define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE -# include "extra/FileBrowserDialog.hpp" -# import "extra/FileBrowserDialogImpl.cpp" -# endif - -// Declared in DistrhoUI.cpp but defined here because it uses Obj-C START_NAMESPACE_DISTRHO double getDesktopScaleFactor(const uintptr_t parentWindowHandle) { @@ -47,9 +49,7 @@ double getDesktopScaleFactor(const uintptr_t parentWindowHandle) return [NSScreen mainScreen].backingScaleFactor; } END_NAMESPACE_DISTRHO - #else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI - # include "../dgl/Base.hpp" # define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE # define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE) @@ -61,5 +61,4 @@ END_NAMESPACE_DISTRHO # define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate) # define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView) # import "src/pugl.mm" - #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI From e3e0bc2183a3c52299d55e1f379f0aa4a4f69013 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 01:16:38 +0100 Subject: [PATCH 387/504] Fix for the previous commmit Signed-off-by: falkTX --- distrho/DistrhoUI_macOS.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index 759e65e0..a554dba2 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -30,6 +30,7 @@ # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE # include "extra/FileBrowserDialog.hpp" # import "extra/FileBrowserDialogImpl.cpp" +# undef FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE #endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI From 00faae6785df59558790f29946b3ad0f29517c51 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 01:25:23 +0100 Subject: [PATCH 388/504] Set DISTRHO_UI_FILE_BROWSER macro for all example plugins Signed-off-by: falkTX --- examples/CairoUI/DistrhoPluginInfo.h | 1 + examples/EmbedExternalUI/DistrhoPluginInfo.h | 1 + examples/ExternalUI/DistrhoPluginInfo.h | 1 + examples/FileHandling/DistrhoPluginInfo.h | 1 + examples/Meters/DistrhoPluginInfo.h | 1 + examples/Parameters/DistrhoPluginInfo.h | 1 + examples/SendNote/DistrhoPluginInfo.h | 1 + examples/States/DistrhoPluginInfo.h | 1 + 8 files changed, 8 insertions(+) diff --git a/examples/CairoUI/DistrhoPluginInfo.h b/examples/CairoUI/DistrhoPluginInfo.h index ce9c04c0..6a332baa 100644 --- a/examples/CairoUI/DistrhoPluginInfo.h +++ b/examples/CairoUI/DistrhoPluginInfo.h @@ -117,4 +117,5 @@ */ #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" +#define DISTRHO_UI_FILE_BROWSER 0 #define DISTRHO_UI_USE_CAIRO 1 diff --git a/examples/EmbedExternalUI/DistrhoPluginInfo.h b/examples/EmbedExternalUI/DistrhoPluginInfo.h index b3cf1331..29deffed 100644 --- a/examples/EmbedExternalUI/DistrhoPluginInfo.h +++ b/examples/EmbedExternalUI/DistrhoPluginInfo.h @@ -27,6 +27,7 @@ #define DISTRHO_PLUGIN_IS_RT_SAFE 1 #define DISTRHO_PLUGIN_NUM_INPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 +#define DISTRHO_UI_FILE_BROWSER 0 #define DISTRHO_UI_USER_RESIZABLE 1 enum Parameters { diff --git a/examples/ExternalUI/DistrhoPluginInfo.h b/examples/ExternalUI/DistrhoPluginInfo.h index e8892cb4..82834d8c 100644 --- a/examples/ExternalUI/DistrhoPluginInfo.h +++ b/examples/ExternalUI/DistrhoPluginInfo.h @@ -27,6 +27,7 @@ #define DISTRHO_PLUGIN_IS_RT_SAFE 1 #define DISTRHO_PLUGIN_NUM_INPUTS 1 #define DISTRHO_PLUGIN_NUM_OUTPUTS 1 +#define DISTRHO_UI_FILE_BROWSER 0 enum Parameters { kParameterLevel = 0, diff --git a/examples/FileHandling/DistrhoPluginInfo.h b/examples/FileHandling/DistrhoPluginInfo.h index cf155408..d451e474 100644 --- a/examples/FileHandling/DistrhoPluginInfo.h +++ b/examples/FileHandling/DistrhoPluginInfo.h @@ -26,6 +26,7 @@ #define DISTRHO_PLUGIN_NUM_INPUTS 1 #define DISTRHO_PLUGIN_NUM_OUTPUTS 1 #define DISTRHO_PLUGIN_WANT_STATE 1 +#define DISTRHO_UI_FILE_BROWSER 1 #define DISTRHO_UI_USER_RESIZABLE 1 #define DISTRHO_UI_USE_NANOVG 1 diff --git a/examples/Meters/DistrhoPluginInfo.h b/examples/Meters/DistrhoPluginInfo.h index 9c101dba..7f5431f9 100644 --- a/examples/Meters/DistrhoPluginInfo.h +++ b/examples/Meters/DistrhoPluginInfo.h @@ -26,6 +26,7 @@ #define DISTRHO_PLUGIN_NUM_INPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_WANT_STATE 1 +#define DISTRHO_UI_FILE_BROWSER 0 #define DISTRHO_UI_USER_RESIZABLE 1 #define DISTRHO_UI_USE_NANOVG 1 diff --git a/examples/Parameters/DistrhoPluginInfo.h b/examples/Parameters/DistrhoPluginInfo.h index 72bd3450..1de8db5f 100644 --- a/examples/Parameters/DistrhoPluginInfo.h +++ b/examples/Parameters/DistrhoPluginInfo.h @@ -26,6 +26,7 @@ #define DISTRHO_PLUGIN_NUM_INPUTS 2 #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 +#define DISTRHO_UI_FILE_BROWSER 0 #define DISTRHO_UI_USER_RESIZABLE 1 #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/examples/SendNote/DistrhoPluginInfo.h b/examples/SendNote/DistrhoPluginInfo.h index 1ef76cdb..56df0006 100644 --- a/examples/SendNote/DistrhoPluginInfo.h +++ b/examples/SendNote/DistrhoPluginInfo.h @@ -28,5 +28,6 @@ #define DISTRHO_PLUGIN_NUM_OUTPUTS 1 #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 +#define DISTRHO_UI_FILE_BROWSER 0 #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/examples/States/DistrhoPluginInfo.h b/examples/States/DistrhoPluginInfo.h index 383313a0..6b8123d5 100644 --- a/examples/States/DistrhoPluginInfo.h +++ b/examples/States/DistrhoPluginInfo.h @@ -27,6 +27,7 @@ #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 #define DISTRHO_PLUGIN_WANT_STATE 1 +#define DISTRHO_UI_FILE_BROWSER 0 #define DISTRHO_UI_USER_RESIZABLE 1 // states and presets together require this in order to function From 18fa6249d85d7cc81762f2b83c433ee6a7e193bb Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 01:25:50 +0100 Subject: [PATCH 389/504] Remove imgui gain plugin example Signed-off-by: falkTX --- examples/ImGuiSimpleGain/CParamSmooth.hpp | 45 ------ examples/ImGuiSimpleGain/DistrhoPluginInfo.h | 145 ------------------ examples/ImGuiSimpleGain/Makefile | 41 ----- examples/ImGuiSimpleGain/PluginSimpleGain.cpp | 130 ---------------- examples/ImGuiSimpleGain/PluginSimpleGain.hpp | 135 ---------------- examples/ImGuiSimpleGain/UISimpleGain.cpp | 106 ------------- examples/ImGuiSimpleGain/UISimpleGain.hpp | 56 ------- 7 files changed, 658 deletions(-) delete mode 100644 examples/ImGuiSimpleGain/CParamSmooth.hpp delete mode 100644 examples/ImGuiSimpleGain/DistrhoPluginInfo.h delete mode 100644 examples/ImGuiSimpleGain/Makefile delete mode 100644 examples/ImGuiSimpleGain/PluginSimpleGain.cpp delete mode 100644 examples/ImGuiSimpleGain/PluginSimpleGain.hpp delete mode 100644 examples/ImGuiSimpleGain/UISimpleGain.cpp delete mode 100644 examples/ImGuiSimpleGain/UISimpleGain.hpp diff --git a/examples/ImGuiSimpleGain/CParamSmooth.hpp b/examples/ImGuiSimpleGain/CParamSmooth.hpp deleted file mode 100644 index 82c9488e..00000000 --- a/examples/ImGuiSimpleGain/CParamSmooth.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/** - * One-pole LPF for smooth parameter changes - * - * https://www.musicdsp.org/en/latest/Filters/257-1-pole-lpf-for-smooth-parameter-changes.html - */ - -#ifndef C_PARAM_SMOOTH_H -#define C_PARAM_SMOOTH_H - -#include - -#define TWO_PI 6.283185307179586476925286766559f - -class CParamSmooth { -public: - CParamSmooth(float smoothingTimeMs, float samplingRate) - : t(smoothingTimeMs) - { - setSampleRate(samplingRate); - } - - ~CParamSmooth() { } - - void setSampleRate(float samplingRate) { - if (samplingRate != fs) { - fs = samplingRate; - a = exp(-TWO_PI / (t * 0.001f * samplingRate)); - b = 1.0f - a; - z = 0.0f; - } - } - - inline void flush() { - z = 0.0f; - } - - inline float process(float in) { - return z = (in * b) + (z * a); - } -private: - float a, b, t, z; - double fs = 0.0; -}; - -#endif // #ifndef C_PARAM_SMOOTH_H diff --git a/examples/ImGuiSimpleGain/DistrhoPluginInfo.h b/examples/ImGuiSimpleGain/DistrhoPluginInfo.h deleted file mode 100644 index cbcbc84e..00000000 --- a/examples/ImGuiSimpleGain/DistrhoPluginInfo.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Simple Gain audio effect for DISTRHO Plugin Framework (DPF) - * SPDX-License-Identifier: MIT - * - * Copyright (C) 2021 Jean Pierre Cimalando - * Copyright (C) 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. - */ - -/** - The plugin name.@n - This is used to identify your plugin before a Plugin instance can be created. - @note This macro is required. - */ -#define DISTRHO_PLUGIN_NAME "ImGuiSimpleGain" - -/** - Number of audio inputs the plugin has. - @note This macro is required. - */ -#define DISTRHO_PLUGIN_NUM_INPUTS 2 - -/** - Number of audio outputs the plugin has. - @note This macro is required. - */ -#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 - -/** - The plugin URI when exporting in LV2 format. - @note This macro is required. - */ -#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/imguisimplegain" - -/** - Wherever the plugin has a custom %UI. - @see DISTRHO_UI_USE_NANOVG - @see UI - */ -#define DISTRHO_PLUGIN_HAS_UI 1 - -/** - Wherever the plugin processing is realtime-safe.@n - TODO - list rtsafe requirements - */ -#define DISTRHO_PLUGIN_IS_RT_SAFE 1 - -/** - Wherever the plugin is a synth.@n - @ref DISTRHO_PLUGIN_WANT_MIDI_INPUT is automatically enabled when this is too. - @see DISTRHO_PLUGIN_WANT_MIDI_INPUT - */ -#define DISTRHO_PLUGIN_IS_SYNTH 0 - -/** - Enable direct access between the %UI and plugin code. - @see UI::getPluginInstancePointer() - @note DO NOT USE THIS UNLESS STRICTLY NECESSARY!! - Try to avoid it at all costs! - */ -#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0 - -/** - Wherever the plugin introduces latency during audio or midi processing. - @see Plugin::setLatency(uint32_t) - */ -#define DISTRHO_PLUGIN_WANT_LATENCY 0 - -/** - Wherever the plugin wants MIDI input.@n - This is automatically enabled if @ref DISTRHO_PLUGIN_IS_SYNTH is true. - */ -#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 - -/** - Wherever the plugin wants MIDI output. - @see Plugin::writeMidiEvent(const MidiEvent&) - */ -#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 - -/** - Wherever the plugin provides its own internal programs. - @see Plugin::initProgramName(uint32_t, String&) - @see Plugin::loadProgram(uint32_t) - */ -#define DISTRHO_PLUGIN_WANT_PROGRAMS 0 - -/** - Wherever the plugin uses internal non-parameter data. - @see Plugin::initState(uint32_t, String&, String&) - @see Plugin::setState(const char*, const char*) - */ -#define DISTRHO_PLUGIN_WANT_STATE 0 - -/** - Wherever the plugin wants time position information from the host. - @see Plugin::getTimePosition() - */ -#define DISTRHO_PLUGIN_WANT_TIMEPOS 0 - -/** - Wherever 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. - */ -#define DISTRHO_UI_USE_NANOVG 0 - -/** - The %UI URI when exporting in LV2 format.@n - By default this is set to @ref DISTRHO_PLUGIN_URI with "#UI" as suffix. - */ -#define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" - -/** - Wherever the %UI uses a custom toolkit implementation based on OpenGL.@n - When enabled, the macros @ref DISTRHO_UI_CUSTOM_INCLUDE_PATH and @ref DISTRHO_UI_CUSTOM_WIDGET_TYPE are required. - */ -#define DISTRHO_UI_USE_CUSTOM 1 - -/** - The include path to the header file used by the custom toolkit implementation. - This path must be relative to dpf/distrho/DistrhoUI.hpp - @see DISTRHO_UI_USE_CUSTOM - */ -#define DISTRHO_UI_CUSTOM_INCLUDE_PATH "DearImGui.hpp" - -/** - The top-level-widget typedef to use for the custom toolkit. - This widget class MUST be a subclass of DGL TopLevelWindow class. - It is recommended that you keep this widget class inside the DGL namespace, - and define widget type as e.g. DGL_NAMESPACE::MyCustomTopLevelWidget. - @see DISTRHO_UI_USE_CUSTOM - */ -#define DISTRHO_UI_CUSTOM_WIDGET_TYPE DGL_NAMESPACE::ImGuiTopLevelWidget - -#define DISTRHO_UI_USER_RESIZABLE 1 diff --git a/examples/ImGuiSimpleGain/Makefile b/examples/ImGuiSimpleGain/Makefile deleted file mode 100644 index 5dc2401f..00000000 --- a/examples/ImGuiSimpleGain/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/make -f -# Makefile for DISTRHO Plugins # -# ---------------------------- # -# Created by falkTX, Christopher Arndt, and Patrick Desaulniers -# - -# -------------------------------------------------------------- -# Project name, used for binaries - -NAME = d_ImGuiSimpleGain - -# -------------------------------------------------------------- -# Files to build - -FILES_DSP = \ - PluginSimpleGain.cpp - -FILES_UI = \ - UISimpleGain.cpp \ - ../../../DPF-Widgets/opengl/DearImGui.cpp - -# -------------------------------------------------------------- -# Do some magic - -include ../../Makefile.plugins.mk - -# path to DPF-Widgets -BUILD_CXX_FLAGS += -I../../../DPF-Widgets/opengl/ - -# -------------------------------------------------------------- -# Enable all selected plugin types - -TARGETS += jack lv2_sep vst2 vst3 - -ifeq ($(HAVE_LIBLO),true) -TARGETS += dssi -endif # HAVE_LIBLO - -all: $(TARGETS) - -# -------------------------------------------------------------- diff --git a/examples/ImGuiSimpleGain/PluginSimpleGain.cpp b/examples/ImGuiSimpleGain/PluginSimpleGain.cpp deleted file mode 100644 index b3e9fe95..00000000 --- a/examples/ImGuiSimpleGain/PluginSimpleGain.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) - * - * SPDX-License-Identifier: MIT - * - * Copyright (C) 2021 Jean Pierre Cimalando - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "PluginSimpleGain.hpp" - -START_NAMESPACE_DISTRHO - -// ----------------------------------------------------------------------- - -PluginSimpleGain::PluginSimpleGain() - : Plugin(paramCount, 0, 0), // parameters, programs, states - fSampleRate(getSampleRate()), - fGainDB(0.0f), - fGainLinear(1.0f) -{ - fSmoothGain = new CParamSmooth(20.0f, fSampleRate); -} - -// ----------------------------------------------------------------------- -// Init - -void PluginSimpleGain::initParameter(uint32_t index, Parameter& parameter) -{ - DISTRHO_SAFE_ASSERT_RETURN(index == 0,); - - parameter.ranges.min = -90.0f; - parameter.ranges.max = 30.0f; - parameter.ranges.def = -0.0f; - parameter.hints = kParameterIsAutomatable; - parameter.name = "Gain"; - parameter.shortName = "Gain"; - parameter.symbol = "gain"; - parameter.unit = "dB"; -} - -// ----------------------------------------------------------------------- -// Internal data - -/** - Get the current value of a parameter. -*/ -float PluginSimpleGain::getParameterValue(uint32_t index) const -{ - DISTRHO_SAFE_ASSERT_RETURN(index == 0, 0.0f); - - return fGainDB; -} - -/** - Change a parameter value. -*/ -void PluginSimpleGain::setParameterValue(uint32_t index, float value) -{ - DISTRHO_SAFE_ASSERT_RETURN(index == 0,); - - fGainDB = value; - fGainLinear = DB_CO(CLAMP(value, -90.0, 30.0)); -} - -// ----------------------------------------------------------------------- -// Process - -void PluginSimpleGain::activate() -{ - fSmoothGain->flush(); -} - -void PluginSimpleGain::run(const float** inputs, float** outputs, uint32_t frames) -{ - // get the left and right audio inputs - const float* const inpL = inputs[0]; - const float* const inpR = inputs[1]; - - // get the left and right audio outputs - float* const outL = outputs[0]; - float* const outR = outputs[1]; - - // apply gain against all samples - for (uint32_t i=0; i < frames; ++i) - { - const float gain = fSmoothGain->process(fGainLinear); - outL[i] = inpL[i] * gain; - outR[i] = inpR[i] * gain; - } -} - -// ----------------------------------------------------------------------- - -/** - Optional callback to inform the plugin about a sample rate change. -*/ -void PluginSimpleGain::sampleRateChanged(double newSampleRate) -{ - fSampleRate = newSampleRate; - fSmoothGain->setSampleRate(newSampleRate); -} - -// ----------------------------------------------------------------------- - -Plugin* createPlugin() -{ - return new PluginSimpleGain(); -} - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DISTRHO diff --git a/examples/ImGuiSimpleGain/PluginSimpleGain.hpp b/examples/ImGuiSimpleGain/PluginSimpleGain.hpp deleted file mode 100644 index 63c33443..00000000 --- a/examples/ImGuiSimpleGain/PluginSimpleGain.hpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) - * - * SPDX-License-Identifier: MIT - * - * Copyright (C) 2021 Jean Pierre Cimalando - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef PLUGIN_SIMPLEGAIN_H -#define PLUGIN_SIMPLEGAIN_H - -#include "DistrhoPlugin.hpp" -#include "CParamSmooth.hpp" -#include "extra/ScopedPointer.hpp" - -START_NAMESPACE_DISTRHO - -#ifndef MIN -#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) -#endif - -#ifndef MAX -#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) -#endif - -#ifndef CLAMP -#define CLAMP(v, min, max) (MIN((max), MAX((min), (v)))) -#endif - -#ifndef DB_CO -#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f) -#endif - -// ----------------------------------------------------------------------- - -class PluginSimpleGain : public Plugin { -public: - enum Parameters { - paramGain = 0, - paramCount - }; - - PluginSimpleGain(); - -protected: - // ------------------------------------------------------------------- - // Information - - const char* getLabel() const noexcept override - { - return "SimpleGain"; - } - - const char* getDescription() const override - { - return "A simple audio volume gain plugin with ImGui for its GUI"; - } - - const char* getMaker() const noexcept override - { - return "Jean Pierre Cimalando, falkTX"; - } - - const char* getLicense() const noexcept override - { - return "MIT"; - } - - uint32_t getVersion() const noexcept override - { - return d_version(1, 0, 0); - } - - int64_t getUniqueId() const noexcept override - { - return d_cconst('d', 'I', 'm', 'G'); - } - - // ------------------------------------------------------------------- - // Init - - void initParameter(uint32_t index, Parameter& parameter) override; - - // ------------------------------------------------------------------- - // Internal data - - float getParameterValue(uint32_t index) const override; - void setParameterValue(uint32_t index, float value) override; - - // ------------------------------------------------------------------- - // Optional - - // Optional callback to inform the plugin about a sample rate change. - void sampleRateChanged(double newSampleRate) override; - - // ------------------------------------------------------------------- - // Process - - void activate() override; - void run(const float**, float** outputs, uint32_t frames) override; - - // ------------------------------------------------------------------- - -private: - double fSampleRate; - float fGainDB; - float fGainLinear; - ScopedPointer fSmoothGain; - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginSimpleGain) -}; - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DISTRHO - -#endif // #ifndef PLUGIN_SIMPLEGAIN_H diff --git a/examples/ImGuiSimpleGain/UISimpleGain.cpp b/examples/ImGuiSimpleGain/UISimpleGain.cpp deleted file mode 100644 index d7684436..00000000 --- a/examples/ImGuiSimpleGain/UISimpleGain.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) - * - * SPDX-License-Identifier: MIT - * - * Copyright (C) 2021 Jean Pierre Cimalando - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "UISimpleGain.hpp" - -START_NAMESPACE_DISTRHO - -// ----------------------------------------------------------------------- -// Init / Deinit - -UISimpleGain::UISimpleGain() - : UI(600, 400), - fGain(0.0f), - fResizeHandle(this) -{ - setGeometryConstraints(600, 400, true); - - // hide handle if UI is resizable - if (isResizable()) - fResizeHandle.hide(); -} - -// ----------------------------------------------------------------------- -// DSP/Plugin callbacks - -/** - A parameter has changed on the plugin side. - This is called by the host to inform the UI about parameter changes. -*/ -void UISimpleGain::parameterChanged(uint32_t index, float value) -{ - DISTRHO_SAFE_ASSERT_RETURN(index == 0,); - - fGain = value; - repaint(); -} - -// ----------------------------------------------------------------------- -// Widget callbacks - -/** - A function called to draw the view contents. -*/ -void UISimpleGain::onImGuiDisplay() -{ - const float width = getWidth(); - const float height = getHeight(); - const float margin = 20.0f * getScaleFactor(); - - ImGui::SetNextWindowPos(ImVec2(margin, margin)); - ImGui::SetNextWindowSize(ImVec2(width - 2 * margin, height - 2 * margin)); - - if (ImGui::Begin("Simple gain", nullptr, ImGuiWindowFlags_NoResize)) - { - static char aboutText[256] = "This is a demo plugin made with ImGui.\n"; - ImGui::InputTextMultiline("About", aboutText, sizeof(aboutText)); - - if (ImGui::SliderFloat("Gain (dB)", &fGain, -90.0f, 30.0f)) - { - if (ImGui::IsItemActivated()) - editParameter(0, true); - - setParameterValue(0, fGain); - } - - if (ImGui::IsItemDeactivated()) - { - editParameter(0, false); - } - } - ImGui::End(); -} - -// ----------------------------------------------------------------------- - -UI* createUI() -{ - return new UISimpleGain(); -} - -// ----------------------------------------------------------------------- - -END_NAMESPACE_DISTRHO diff --git a/examples/ImGuiSimpleGain/UISimpleGain.hpp b/examples/ImGuiSimpleGain/UISimpleGain.hpp deleted file mode 100644 index 03beabff..00000000 --- a/examples/ImGuiSimpleGain/UISimpleGain.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Simple Gain audio effect based on DISTRHO Plugin Framework (DPF) - * - * SPDX-License-Identifier: MIT - * - * Copyright (C) 2021 Jean Pierre Cimalando - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef UI_SIMPLEGAIN_H -#define UI_SIMPLEGAIN_H - -#include "DistrhoUI.hpp" -#include "../generic/ResizeHandle.hpp" - -START_NAMESPACE_DISTRHO - -class UISimpleGain : public UI -{ -public: - UISimpleGain(); - -protected: - // DSP/Plugin callbacks - void parameterChanged(uint32_t, float value) override; - - // Widget callbacks - void onImGuiDisplay() override; - -private: - float fGain; - ResizeHandle fResizeHandle; - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UISimpleGain) -}; - -END_NAMESPACE_DISTRHO - -#endif From 1f3fb01339a80c9dbfaa642c0c58121a57d923dc Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 01:41:54 +0100 Subject: [PATCH 390/504] Make sure to always include+build the correct file browser file Signed-off-by: falkTX --- dgl/src/pugl.cpp | 11 +++++------ distrho/DistrhoUI_macOS.mm | 6 ++++-- distrho/src/DistrhoPluginChecks.h | 2 +- distrho/src/DistrhoUI.cpp | 12 +++++++++++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index b33eeb96..0b61ae3b 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -102,12 +102,11 @@ #ifndef DGL_FILE_BROWSER_DISABLED # define FILE_BROWSER_DIALOG_DGL_NAMESPACE -# include "../FileBrowserDialog.hpp" -# ifdef DISTRHO_OS_MAC -# import "../../distrho/extra/FileBrowserDialogImpl.cpp" -# else -# include "../../distrho/extra/FileBrowserDialogImpl.cpp" -# endif +# define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED +START_NAMESPACE_DGL +# include "../../distrho/extra/FileBrowserDialogImpl.hpp" +END_NAMESPACE_DGL +# include "../../distrho/extra/FileBrowserDialogImpl.cpp" #endif #ifndef DISTRHO_OS_MAC diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index a554dba2..af0ce740 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -27,10 +27,12 @@ // A few utils declared in DistrhoUI.cpp but defined here because they uses Obj-C #if DISTRHO_UI_FILE_BROWSER +# define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE -# include "extra/FileBrowserDialog.hpp" +START_NAMESPACE_DISTRHO +# include "extra/FileBrowserDialogImpl.hpp" +END_NAMESPACE_DISTRHO # import "extra/FileBrowserDialogImpl.cpp" -# undef FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE #endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index d2df9f89..dd838b96 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 87788eb1..20e084f7 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -17,6 +17,12 @@ #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" +#ifdef DISTRHO_PROPER_CPP11_SUPPORT +# include +#else +# include +#endif + #if DISTRHO_UI_FILE_BROWSER && !defined(DISTRHO_OS_MAC) # define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION # define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) @@ -35,8 +41,12 @@ # define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_save_recent) # define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_show) # define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_status) +# define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE -# include "../extra/FileBrowserDialog.hpp" +START_NAMESPACE_DISTRHO +# include "../extra/FileBrowserDialogImpl.hpp" +END_NAMESPACE_DISTRHO +# include "../extra/FileBrowserDialogImpl.hpp" # include "../extra/FileBrowserDialogImpl.cpp" #endif From 51ca273f60c17c16b59afadd9c52dac491a82d9c Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 02:07:38 +0100 Subject: [PATCH 391/504] Experiment always building pugl on macOS cmake Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 66b69272..2e261ed2 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -468,9 +468,9 @@ function(dpf__add_dgl_cairo) if(NOT APPLE) target_sources(dgl-cairo PRIVATE "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") - else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm - #target_sources(dgl-opengl PRIVATE - # "${DPF_ROOT_DIR}/dgl/src/pugl.mm") + else() + target_sources(dgl-opengl PRIVATE + "${DPF_ROOT_DIR}/dgl/src/pugl.mm") endif() target_include_directories(dgl-cairo PUBLIC "${DPF_ROOT_DIR}/dgl") @@ -530,9 +530,9 @@ function(dpf__add_dgl_opengl) if(NOT APPLE) target_sources(dgl-opengl PRIVATE "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") - else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm - #target_sources(dgl-opengl PRIVATE - # "${DPF_ROOT_DIR}/dgl/src/pugl.mm") + else() + target_sources(dgl-opengl PRIVATE + "${DPF_ROOT_DIR}/dgl/src/pugl.mm") endif() target_include_directories(dgl-opengl PUBLIC "${DPF_ROOT_DIR}/dgl") From b139002d382e3ed8c36a55d1493e6b0229a03105 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 02:22:52 +0100 Subject: [PATCH 392/504] Revert previous commit, enable xcursor for cmake builds Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 2e261ed2..81c63e12 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -468,9 +468,9 @@ function(dpf__add_dgl_cairo) if(NOT APPLE) target_sources(dgl-cairo PRIVATE "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") - else() - target_sources(dgl-opengl PRIVATE - "${DPF_ROOT_DIR}/dgl/src/pugl.mm") + else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm + #target_sources(dgl-opengl PRIVATE + # "${DPF_ROOT_DIR}/dgl/src/pugl.mm") endif() target_include_directories(dgl-cairo PUBLIC "${DPF_ROOT_DIR}/dgl") @@ -530,9 +530,9 @@ function(dpf__add_dgl_opengl) if(NOT APPLE) target_sources(dgl-opengl PRIVATE "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") - else() - target_sources(dgl-opengl PRIVATE - "${DPF_ROOT_DIR}/dgl/src/pugl.mm") + else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm + #target_sources(dgl-opengl PRIVATE + # "${DPF_ROOT_DIR}/dgl/src/pugl.mm") endif() target_include_directories(dgl-opengl PUBLIC "${DPF_ROOT_DIR}/dgl") @@ -596,22 +596,22 @@ function(dpf__add_dgl_system_libs) target_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}") target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}") target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11") + if(X11_Xcursor_FOUND) + target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}") + target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR") + endif() if(X11_Xext_FOUND) target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}") target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT") endif() - if(X11_XSync_FOUND) - target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}") - target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC") - endif() if(X11_Xrandr_FOUND) target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}") target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR") endif() - #if(X11_Xcursor_FOUND) - # target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}") - # target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR") - #endif() + if(X11_XSync_FOUND) + target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}") + target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC") + endif() endif() if(MSVC) From bad716bc14e56c3a7c276b1a77bfdcefc4e8df37 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 02:28:42 +0100 Subject: [PATCH 393/504] Fix C++98 build Signed-off-by: falkTX --- distrho/src/DistrhoUI.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 20e084f7..2a0d1d37 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -17,6 +17,8 @@ #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" +#include + #ifdef DISTRHO_PROPER_CPP11_SUPPORT # include #else From 60f0de5a0da7612cfaacf37c2d3e5ee795f353e1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 12:32:18 +0100 Subject: [PATCH 394/504] Cleanup makefile Signed-off-by: falkTX --- Makefile.base.mk | 17 ++++++++++++----- Makefile.plugins.mk | 34 +++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index d4bcdcfb..9d0e556c 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -252,9 +252,6 @@ endif HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) -# Vulkan is not supported yet -# HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true) - ifeq ($(MACOS_OR_WINDOWS),true) HAVE_OPENGL = true else @@ -266,6 +263,9 @@ HAVE_XEXT = $(shell $(PKG_CONFIG) --exists xext && echo true) HAVE_XRANDR = $(shell $(PKG_CONFIG) --exists xrandr && echo true) endif +# Vulkan is not supported yet +# HAVE_VULKAN = $(shell $(PKG_CONFIG) --exists vulkan && echo true) + # --------------------------------------------------------------------------------------------------------------------- # Check for optional libraries @@ -278,7 +278,7 @@ ifeq ($(MACOS),true) HAVE_RTAUDIO = true else ifeq ($(WINDOWS),true) HAVE_RTAUDIO = true -else ifneq ($(HAIKU),true) +else HAVE_ALSA = $(shell $(PKG_CONFIG) --exists alsa && echo true) HAVE_PULSEAUDIO = $(shell $(PKG_CONFIG) --exists libpulse-simple && echo true) ifeq ($(HAVE_ALSA),true) @@ -289,7 +289,7 @@ endif endif endif -# backwards compat +# backwards compat, always available/enabled HAVE_JACK = true # --------------------------------------------------------------------------------------------------------------------- @@ -418,6 +418,13 @@ PULSEAUDIO_FLAGS = $(shell $(PKG_CONFIG) --cflags libpulse-simple) PULSEAUDIO_LIBS = $(shell $(PKG_CONFIG) --libs libpulse-simple) endif +ifeq ($(HAVE_JACK),true) +ifeq ($(STATIC_BUILD),true) +JACK_FLAGS = $(shell $(PKG_CONFIG) --cflags jack) +JACK_LIBS = $(shell $(PKG_CONFIG) --libs jack) +endif +endif + ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) SHARED_MEMORY_LIBS = -lrt endif diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 2af556c6..c0958d5f 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -38,6 +38,10 @@ ifeq ($(HAVE_ALSA),true) BASE_FLAGS += -DHAVE_ALSA endif +ifeq ($(HAVE_JACK),true) +BASE_FLAGS += -DHAVE_JACK +endif + ifeq ($(HAVE_LIBLO),true) BASE_FLAGS += -DHAVE_LIBLO endif @@ -46,10 +50,18 @@ ifeq ($(HAVE_PULSEAUDIO),true) BASE_FLAGS += -DHAVE_PULSEAUDIO endif -ifeq ($(STATIC_BUILD),true) -JACK_LIBS += $(shell $(PKG_CONFIG) --libs jack) +# always needed +ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifneq ($(STATIC_BUILD),true) +LINK_FLAGS += -ldl +endif endif +# --------------------------------------------------------------------------------------------------------------------- +# JACK/Standalone setup + +ifeq ($(SKIP_RTAUDIO_FALLBACK),true) + ifeq ($(MACOS),true) JACK_LIBS += -framework CoreAudio -framework CoreFoundation else ifeq ($(WINDOWS),true) @@ -58,28 +70,20 @@ JACK_LIBS += -lole32 -lwinmm JACK_LIBS += -ldsound # WASAPI # JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -else ifneq ($(HAIKU),true) -ifeq ($(HAVE_ALSA),true) +else ifeq ($(HAVE_ALSA),true) JACK_FLAGS += $(ALSA_FLAGS) JACK_LIBS += $(ALSA_LIBS) -endif -ifeq ($(HAVE_PULSEAUDIO),true) +else ifeq ($(HAVE_PULSEAUDIO),true) JACK_FLAGS += $(PULSEAUDIO_FLAGS) JACK_LIBS += $(PULSEAUDIO_LIBS) endif + ifeq ($(HAVE_RTAUDIO),true) +ifneq ($(HAIKU),true) JACK_LIBS += -lpthread -endif # !HAIKU endif - -# backwards compat -BASE_FLAGS += -DHAVE_JACK - -# always needed -ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) -ifneq ($(STATIC_BUILD),true) -LINK_FLAGS += -ldl endif + endif # --------------------------------------------------------------------------------------------------------------------- From cac6646370b6f83366c8289789f078733a14753a Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 12:58:32 +0100 Subject: [PATCH 395/504] Fix build Signed-off-by: falkTX --- Makefile.plugins.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index c0958d5f..fd158043 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -60,7 +60,7 @@ endif # --------------------------------------------------------------------------------------------------------------------- # JACK/Standalone setup -ifeq ($(SKIP_RTAUDIO_FALLBACK),true) +ifneq ($(SKIP_RTAUDIO_FALLBACK),true) ifeq ($(MACOS),true) JACK_LIBS += -framework CoreAudio -framework CoreFoundation @@ -70,12 +70,12 @@ JACK_LIBS += -lole32 -lwinmm JACK_LIBS += -ldsound # WASAPI # JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -else ifeq ($(HAVE_ALSA),true) -JACK_FLAGS += $(ALSA_FLAGS) -JACK_LIBS += $(ALSA_LIBS) else ifeq ($(HAVE_PULSEAUDIO),true) JACK_FLAGS += $(PULSEAUDIO_FLAGS) JACK_LIBS += $(PULSEAUDIO_LIBS) +else ifeq ($(HAVE_ALSA),true) +JACK_FLAGS += $(ALSA_FLAGS) +JACK_LIBS += $(ALSA_LIBS) endif ifeq ($(HAVE_RTAUDIO),true) From 982f27632ceab5af9d81ac37e952118ca9e800ea Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 13:45:08 +0100 Subject: [PATCH 396/504] Give up on fancy macOS namespace Signed-off-by: falkTX --- cmake/DPF-plugin.cmake | 25 +++++++++---------------- dgl/src/pugl.cpp | 10 +++++++--- distrho/DistrhoUI_macOS.mm | 21 +++------------------ distrho/src/DistrhoUI.cpp | 1 - 4 files changed, 19 insertions(+), 38 deletions(-) diff --git a/cmake/DPF-plugin.cmake b/cmake/DPF-plugin.cmake index 81c63e12..858c49c8 100644 --- a/cmake/DPF-plugin.cmake +++ b/cmake/DPF-plugin.cmake @@ -142,7 +142,7 @@ function(dpf_add_plugin NAME) if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) target_link_libraries("${NAME}-ui" PRIVATE "dl") endif() - # add the files containing Objective-C classes, recompiled under namespace + # add the files containing Objective-C classes dpf__add_plugin_specific_ui_sources("${NAME}-ui") else() add_library("${NAME}-ui" INTERFACE) @@ -468,9 +468,9 @@ function(dpf__add_dgl_cairo) if(NOT APPLE) target_sources(dgl-cairo PRIVATE "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") - else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm - #target_sources(dgl-opengl PRIVATE - # "${DPF_ROOT_DIR}/dgl/src/pugl.mm") + else() + target_sources(dgl-opengl PRIVATE + "${DPF_ROOT_DIR}/dgl/src/pugl.mm") endif() target_include_directories(dgl-cairo PUBLIC "${DPF_ROOT_DIR}/dgl") @@ -530,9 +530,9 @@ function(dpf__add_dgl_opengl) if(NOT APPLE) target_sources(dgl-opengl PRIVATE "${DPF_ROOT_DIR}/dgl/src/pugl.cpp") - else() # Note: macOS pugl will be built as part of DistrhoUI_macOS.mm - #target_sources(dgl-opengl PRIVATE - # "${DPF_ROOT_DIR}/dgl/src/pugl.mm") + else() + target_sources(dgl-opengl PRIVATE + "${DPF_ROOT_DIR}/dgl/src/pugl.mm") endif() target_include_directories(dgl-opengl PUBLIC "${DPF_ROOT_DIR}/dgl") @@ -556,19 +556,12 @@ endfunction() # dpf__add_plugin_specific_ui_sources # ------------------------------------------------------------------------------ # -# Compile plugin-specific UI sources into the target designated by the given -# name. There are some special considerations here: -# - On most platforms, sources can be compiled only once, as part of DGL; -# - On macOS, for any sources which define Objective-C interfaces, these must -# be recompiled for each plugin under a unique namespace. In this case, the -# name must be a plugin-specific identifier, and it will be used for computing -# the unique ID along with the project version. +# Compile system specific files, for now it is just Objective-C code +# function(dpf__add_plugin_specific_ui_sources NAME) if(APPLE) target_sources("${NAME}" PRIVATE "${DPF_ROOT_DIR}/distrho/DistrhoUI_macOS.mm") - string(SHA256 _hash "${NAME}:${PROJECT_VERSION}") - target_compile_definitions("${NAME}" PUBLIC "PUGL_NAMESPACE=${_hash}") endif() endfunction() diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 0b61ae3b..e2f6ca4a 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -119,9 +119,13 @@ START_NAMESPACE_DGL # ifndef DISTRHO_MACOS_NAMESPACE_MACRO # define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE # define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE) -# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView) -# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView) -# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow) +# define PuglCairoView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglCairoView) +# define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglOpenGLView) +# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView) +# define PuglVulkanView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglVulkanView) +# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow) +# define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindowDelegate) +# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView) # endif # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index af0ce740..15dfa6ac 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -14,9 +14,7 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef PUGL_NAMESPACE -# error PUGL_NAMESPACE must be set when compiling this file -#endif +// A few utils declared in DistrhoUI.cpp but defined here because they use Obj-C #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" @@ -25,14 +23,13 @@ # import #endif -// A few utils declared in DistrhoUI.cpp but defined here because they uses Obj-C #if DISTRHO_UI_FILE_BROWSER # define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE START_NAMESPACE_DISTRHO # include "extra/FileBrowserDialogImpl.hpp" END_NAMESPACE_DISTRHO -# import "extra/FileBrowserDialogImpl.cpp" +# include "extra/FileBrowserDialogImpl.cpp" #endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -52,16 +49,4 @@ double getDesktopScaleFactor(const uintptr_t parentWindowHandle) return [NSScreen mainScreen].backingScaleFactor; } END_NAMESPACE_DISTRHO -#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# include "../dgl/Base.hpp" -# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, SEP, PUGL_NS, INTERFACE) DGL_NS ## SEP ## PUGL_NS ## SEP ## INTERFACE -# define DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NS, PUGL_NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(DGL_NS, _, PUGL_NS, INTERFACE) -# define PuglCairoView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, CairoView) -# define PuglOpenGLView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, OpenGLView) -# define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, StubView) -# define PuglVulkanView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, VulkanView) -# define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, Window) -# define PuglWindowDelegate DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WindowDelegate) -# define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PUGL_NAMESPACE, WrapperView) -# import "src/pugl.mm" -#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI +#endif diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 2a0d1d37..932a9f7b 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -48,7 +48,6 @@ START_NAMESPACE_DISTRHO # include "../extra/FileBrowserDialogImpl.hpp" END_NAMESPACE_DISTRHO -# include "../extra/FileBrowserDialogImpl.hpp" # include "../extra/FileBrowserDialogImpl.cpp" #endif From 42c1387a1281a4846c2bec6d6b5df3f9259190c5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 2 Jun 2022 13:49:12 +0100 Subject: [PATCH 397/504] Cleanup Signed-off-by: falkTX --- Makefile.plugins.mk | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index fd158043..0aeea911 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -353,8 +353,6 @@ $(DPF_PATH)/build/libdgl-vulkan.a: # --------------------------------------------------------------------------------------------------------------------- -AS_PUGL_NAMESPACE = $(subst -,_,$(1)) - $(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoPluginMain.cpp ($*)" @@ -368,7 +366,7 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTR $(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUI_macOS.mm ($*)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DPUGL_NAMESPACE=$(call AS_PUGL_NAMESPACE,$*) -DGL_SILENCE_DEPRECATION -Wno-deprecated-declarations -I$(DPF_PATH)/dgl/src -I$(DPF_PATH)/dgl/src/pugl-upstream/include -ObjC++ -c -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -ObjC++ -c -o $@ $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @@ -378,7 +376,7 @@ $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUIMain.cpp (DSSI)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(LIBLO_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI -c -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI $(LIBLO_FLAGS) -c -o $@ # --------------------------------------------------------------------------------------------------------------------- # JACK From a65ac8545d7c7c6f549f403042caad6726aca199 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 5 Jun 2022 15:07:44 +0100 Subject: [PATCH 398/504] Also set __USE_MINGW_ANSI_STDIO on makefiles for mingw Signed-off-by: falkTX --- Makefile.base.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 9d0e556c..8f6ec0ce 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -180,7 +180,7 @@ endif ifeq ($(WINDOWS),true) # Assume we want posix -BASE_FLAGS += -posix -D__STDC_FORMAT_MACROS +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 From 48d37055d9cf00c231d1a72aa20d166268933c3a Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Jun 2022 21:17:01 +0100 Subject: [PATCH 399/504] Update pugl, get rid of puglMacOSActivateApp (no longer needed) Signed-off-by: falkTX --- dgl/src/ApplicationPrivateData.cpp | 5 ----- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 9 --------- dgl/src/pugl.hpp | 3 --- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index f54535fc..590ae3b5 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -61,11 +61,6 @@ Application::PrivateData::PrivateData(const bool standalone) puglSetWorldHandle(world, this); puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); - -#ifdef DISTRHO_OS_MAC - if (standalone) - puglMacOSActivateApp(); -#endif } Application::PrivateData::~PrivateData() diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 7bb0bd67..24a1418b 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 7bb0bd672489ac0d0851dfd1b4980b33b7a9f48d +Subproject commit 24a1418b464e43f7f79af0d815c177121473bf1e diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index e2f6ca4a..acda6628 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -411,15 +411,6 @@ void puglFallbackOnResize(PuglView* const view) #if defined(DISTRHO_OS_MAC) -// -------------------------------------------------------------------------------------------------------------------- -// macOS specific, allow standalone window to gain focus - -void puglMacOSActivateApp() -{ - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - [NSApp activateIgnoringOtherApps:YES]; -} - // -------------------------------------------------------------------------------------------------------------------- // macOS specific, add another view's window as child diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 1e48a324..1241e196 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -78,9 +78,6 @@ void puglFallbackOnResize(PuglView* view); #if defined(DISTRHO_OS_MAC) -// macOS specific, allow standalone window to gain focus -void puglMacOSActivateApp(); - // macOS specific, add another view's window as child PuglStatus puglMacOSAddChildWindow(PuglView* view, PuglView* child); From 176b1a4707225796ea3d290b379b3e404a2d94b8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Jun 2022 21:22:24 +0100 Subject: [PATCH 400/504] Fix build with new pugl Signed-off-by: falkTX --- dgl/src/Window.cpp | 2 +- dgl/src/WindowPrivateData.cpp | 4 ++-- dgl/src/pugl.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index a068038e..37c61d11 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -362,7 +362,7 @@ const GraphicsContext& Window::getGraphicsContext() const noexcept uintptr_t Window::getNativeWindowHandle() const noexcept { - return puglGetNativeWindow(pData->view); + return puglGetNativeView(pData->view); } double Window::getScaleFactor() const noexcept diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 522da6b5..963bf83f 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -70,7 +70,7 @@ static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView if (PuglView* const view = puglNewView(world)) { - puglSetTransientParent(view, puglGetNativeWindow(transientParentView)); + puglSetTransientParent(view, puglGetNativeView(transientParentView)); return view; } @@ -477,7 +477,7 @@ bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options) options2.title = puglGetWindowTitle(view); fileBrowserHandle = fileBrowserCreate(isEmbed, - puglGetNativeWindow(view), + puglGetNativeView(view), autoScaling ? autoScaleFactor : scaleFactor, options2); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index acda6628..119c576b 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -167,7 +167,8 @@ START_NAMESPACE_DGL # endif #endif -#include "pugl-upstream/src/implementation.c" +#include "pugl-upstream/src/common.c" +#include "pugl-upstream/src/internal.c" // -------------------------------------------------------------------------------------------------------------------- // DGL specific, expose backend enter From 772e4f5ae8e21ff2d13f5e5633436fbd283e00bc Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Jun 2022 21:25:26 +0100 Subject: [PATCH 401/504] Update Info plugin example resize handle to set mouse cursor Signed-off-by: falkTX --- examples/Info/ResizeHandle.hpp | 55 ++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/examples/Info/ResizeHandle.hpp b/examples/Info/ResizeHandle.hpp index c24c9ebd..3e949637 100644 --- a/examples/Info/ResizeHandle.hpp +++ b/examples/Info/ResizeHandle.hpp @@ -1,6 +1,6 @@ /* * Resize handle for DPF - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -19,6 +19,10 @@ #include "TopLevelWidget.hpp" #include "Color.hpp" +#if defined(DGL_OPENGL) && !defined(DGL_USE_OPENGL3) +#include "OpenGL-include.hpp" +#endif + START_NAMESPACE_DGL /** Resize handle for DPF windows, will sit on bottom-right. */ @@ -29,7 +33,8 @@ public: explicit ResizeHandle(Window& window) : TopLevelWidget(window), handleSize(16), - resizing(false) + hasCursor(false), + isResizing(false) { resetArea(); } @@ -38,12 +43,14 @@ public: explicit ResizeHandle(TopLevelWidget* const tlw) : TopLevelWidget(tlw->getWindow()), handleSize(16), - resizing(false) + hasCursor(false), + isResizing(false) { resetArea(); } - /** Set the handle size, minimum 16. */ + /** Set the handle size, minimum 16. + * Scale factor is automatically applied on top of this size as needed */ void setHandleSize(const uint size) { handleSize = std::max(16u, size); @@ -53,9 +60,15 @@ public: protected: void onDisplay() override { + // TODO implement gl3 stuff in DPF +#ifndef DGL_USE_OPENGL3 const GraphicsContext& context(getGraphicsContext()); const double lineWidth = 1.0 * getScaleFactor(); + #if defined(DGL_OPENGL) && !defined(DGL_USE_OPENGL3) + glMatrixMode(GL_MODELVIEW); + #endif + // draw white lines, 1px wide Color(1.0f, 1.0f, 1.0f).setFor(context); l1.draw(context, lineWidth); @@ -71,6 +84,7 @@ protected: l1b.draw(context, lineWidth); l2b.draw(context, lineWidth); l3b.draw(context, lineWidth); +#endif } bool onMouse(const MouseEvent& ev) override @@ -80,15 +94,16 @@ protected: if (ev.press && area.contains(ev.pos)) { - resizing = true; + isResizing = true; resizingSize = Size(getWidth(), getHeight()); lastResizePoint = ev.pos; return true; } - if (resizing && ! ev.press) + if (isResizing && ! ev.press) { - resizing = false; + isResizing = false; + recheckCursor(ev.pos); return true; } @@ -97,8 +112,11 @@ protected: bool onMotion(const MotionEvent& ev) override { - if (! resizing) + if (! isResizing) + { + recheckCursor(ev.pos); return false; + } const Size offset(ev.pos.getX() - lastResizePoint.getX(), ev.pos.getY() - lastResizePoint.getY()); @@ -106,9 +124,11 @@ protected: resizingSize += offset; lastResizePoint = ev.pos; - // TODO min width, min height - const uint minWidth = 16; - const uint minHeight = 16; + // TODO keepAspectRatio + bool keepAspectRatio; + const Size minSize(getWindow().getGeometryConstraints(keepAspectRatio)); + const uint minWidth = minSize.getWidth(); + const uint minHeight = minSize.getHeight(); if (resizingSize.getWidth() < minWidth) resizingSize.setWidth(minWidth); @@ -135,10 +155,21 @@ private: uint handleSize; // event handling state - bool resizing; + bool hasCursor, isResizing; Point lastResizePoint; Size resizingSize; + void recheckCursor(const Point& pos) + { + const bool shouldHaveCursor = area.contains(pos); + + if (shouldHaveCursor == hasCursor) + return; + + hasCursor = shouldHaveCursor; + setCursor(shouldHaveCursor ? kMouseCursorDiagonal : kMouseCursorArrow); + } + void resetArea() { const double scaleFactor = getScaleFactor(); From 3d0d31c5941e3156e9cdcbcfa59655737a1391de Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Jun 2022 21:27:58 +0100 Subject: [PATCH 402/504] Fix cairo include Fixes #373 Signed-off-by: falkTX --- dgl/Cairo.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp index cef76734..87970c99 100644 --- a/dgl/Cairo.hpp +++ b/dgl/Cairo.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -20,7 +20,7 @@ #include "ImageBase.hpp" #include "ImageBaseWidgets.hpp" -#include +#include START_NAMESPACE_DGL From 6ef3fce69a543f63549eae8fc64e5bf8aca8b35d Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Jun 2022 21:47:10 +0100 Subject: [PATCH 403/504] Sync get display factor code with pugl Signed-off-by: falkTX --- distrho/src/DistrhoUI.cpp | 35 +++++++++++++++++------------------ pugl-updates-notes.txt | 2 -- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 932a9f7b..cc3f53c4 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -108,22 +108,18 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) # endif DWORD dpiAware = 0; + DWORD scaleFactor = 100; if (GetProcessDpiAwareness && GetScaleFactorForMonitor - && GetProcessDpiAwareness(NULL, &dpiAware) == 0 && dpiAware != 0) + && GetProcessDpiAwareness(nullptr, &dpiAware) == 0 && dpiAware != 0) { const HMONITOR hMon = parentWindowHandle != 0 ? MonitorFromWindow((HWND)parentWindowHandle, MONITOR_DEFAULTTOPRIMARY) : MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY); - - DWORD scaleFactor = 0; - if (GetScaleFactorForMonitor(hMon, &scaleFactor) == 0 && scaleFactor != 0) - { - FreeLibrary(Shcore); - return static_cast(scaleFactor) / 100.0; - } + GetScaleFactorForMonitor(hMon, &scaleFactor); } FreeLibrary(Shcore); + return static_cast(scaleFactor) / 100.0; } #elif defined(HAVE_X11) ::Display* const display = XOpenDisplay(nullptr); @@ -131,28 +127,31 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) XrmInitialize(); + double dpi = 96.0; if (char* const rms = XResourceManagerString(display)) { - if (const XrmDatabase sdb = XrmGetStringDatabase(rms)) + if (const XrmDatabase db = XrmGetStringDatabase(rms)) { char* type = nullptr; - XrmValue ret; + XrmValue value = {}; - if (XrmGetResource(sdb, "Xft.dpi", "String", &type, &ret) - && ret.addr != nullptr + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type != nullptr - && std::strncmp("String", type, 6) == 0) + && std::strcmp(type, "String") == 0 + && value.addr != nullptr) { - if (const double dpi = std::atof(ret.addr)) - { - XCloseDisplay(display); - return dpi / 96; - } + char* end = nullptr; + const double xftDpi = std::strtod(value.addr, &end); + if (xftDpi > 0.0 && xftDpi < HUGE_VAL) + dpi = xftDpi; } + + XrmDestroyDatabase(db); } } XCloseDisplay(display); + return dpi / 96; #endif return 1.0; diff --git a/pugl-updates-notes.txt b/pugl-updates-notes.txt index 3cf480ae..254e1589 100644 --- a/pugl-updates-notes.txt +++ b/pugl-updates-notes.txt @@ -1,5 +1,3 @@ puglClearMinSize needed? puglSetWindowSize was used on first show, still needed? - -update distrhoui.cpp get scale factor to match new parent request setup and pugl From 9efc5ff6e3eb866e6ea893891f3fe3d5a7712d6e Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 17 Jun 2022 22:07:09 +0100 Subject: [PATCH 404/504] Ignore a compiler warning Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- distrho/src/DistrhoPluginJACK.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 24a1418b..b10e1377 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 24a1418b464e43f7f79af0d815c177121473bf1e +Subproject commit b10e137701cb34619f71e282fa3fa6e475c1a2b6 diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 20ed6e5d..446fe15d 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -987,7 +987,15 @@ int main(int argc, char* argv[]) if (const HMODULE user32 = LoadLibrary("user32.dll")) { typedef BOOL(WINAPI* SPDA)(void); - if (const SPDA SetProcessDPIAware = (SPDA)GetProcAddress(user32, "SetProcessDPIAware")) + #if defined(__GNUC__) && (__GNUC__ >= 9) + # pragma GCC diagnostic push + # pragma GCC diagnostic ignored "-Wcast-function-type" + #endif + const SPDA SetProcessDPIAware = (SPDA)GetProcAddress(user32, "SetProcessDPIAware"); + #if defined(__GNUC__) && (__GNUC__ >= 9) + # pragma GCC diagnostic pop + #endif + if (SetProcessDPIAware) SetProcessDPIAware(); FreeLibrary(user32); } From ac9b574f6f8294fbfaf0aab1c943ab33de7afce2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 18 Jun 2022 21:53:23 +0100 Subject: [PATCH 405/504] Fix incorrect offset of embed ui on windows and macOS --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index b10e1377..6731c86f 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit b10e137701cb34619f71e282fa3fa6e475c1a2b6 +Subproject commit 6731c86f3f17712c396316321fffcb415593ff69 From 0c04a07e562f502324bb417fb56dbaab7911b010 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 19 Jun 2022 08:46:24 +0100 Subject: [PATCH 406/504] Make macOS installer allowed to install without rosetta2 Signed-off-by: falkTX --- utils/plugin.pkg/package.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/plugin.pkg/package.xml.in b/utils/plugin.pkg/package.xml.in index dc8e24f2..3b4a8a68 100644 --- a/utils/plugin.pkg/package.xml.in +++ b/utils/plugin.pkg/package.xml.in @@ -2,7 +2,7 @@ @name@ - + From b8288d44ee2784fcaa29d318ce602e8831a68b8c Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Jun 2022 12:18:17 +0100 Subject: [PATCH 407/504] Simplify vst2 and vst3 transport code Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 7 ++++--- distrho/src/DistrhoPluginVST3.cpp | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 86a020bd..dd666c9b 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1100,9 +1100,8 @@ public: if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)hostCallback(audioMasterGetTime, 0, kWantVstTimeFlags)) { - fTimePosition.frame = vstTimeInfo->samplePos; - fTimePosition.playing = (vstTimeInfo->flags & kVstTransportPlaying); - fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0); + fTimePosition.frame = vstTimeInfo->samplePos; + fTimePosition.playing = vstTimeInfo->flags & kVstTransportPlaying; // ticksPerBeat is not possible with VST2 fTimePosition.bbt.ticksPerBeat = 1920.0; @@ -1119,6 +1118,7 @@ public: const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigNumerator; const double rest = std::fmod(barBeats, 1.0); + fTimePosition.bbt.valid = true; fTimePosition.bbt.bar = static_cast(ppqPos) / ppqPerBar + 1; fTimePosition.bbt.beat = static_cast(barBeats - rest + 0.5) + 1; fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; @@ -1134,6 +1134,7 @@ public: } else { + fTimePosition.bbt.valid = false; fTimePosition.bbt.bar = 1; fTimePosition.bbt.beat = 1; fTimePosition.bbt.tick = 0.0; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index cebf2f45..8af93a08 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1187,8 +1187,7 @@ public: #if DISTRHO_PLUGIN_WANT_TIMEPOS if (v3_process_context* const ctx = data->ctx) { - fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING; - fTimePosition.bbt.valid = ctx->state & (V3_PROCESS_CTX_TEMPO_VALID|V3_PROCESS_CTX_TIME_SIG_VALID); + fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING; // ticksPerBeat is not possible with VST3 fTimePosition.bbt.ticksPerBeat = 1920.0; @@ -1210,6 +1209,7 @@ public: const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * ctx->time_sig_numerator; const double rest = std::fmod(barBeats, 1.0); + fTimePosition.bbt.valid = true; fTimePosition.bbt.bar = static_cast(ppqPos) / ppqPerBar + 1; fTimePosition.bbt.beat = static_cast(barBeats - rest + 0.5) + 1; fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; @@ -1225,6 +1225,7 @@ public: } else { + fTimePosition.bbt.valid = false; fTimePosition.bbt.bar = 1; fTimePosition.bbt.beat = 1; fTimePosition.bbt.tick = 0.0; From 314c814ce16fc44b420c546c184755c57b61b52f Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Jun 2022 13:38:41 +0100 Subject: [PATCH 408/504] Deal with VST3 UI resize properly Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 2 +- distrho/src/DistrhoUIVST3.cpp | 54 ++++++++++++++----------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index dd666c9b..820eb76b 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -264,7 +264,7 @@ public: switch (value) { - // convert some VST special values to normal keys + // convert some VST2 special values to normal keys case 1: index = kKeyBackspace; break; case 2: index = '\t'; break; // 3 clear diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index c490570b..cbca49d5 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -33,7 +33,6 @@ /* TODO items: * - mousewheel event * - key down/up events - * - host-side resizing * - file request? */ @@ -263,6 +262,7 @@ public: fScaleFactor(scaleFactor), fIsResizingFromPlugin(false), fIsResizingFromHost(willResizeFromHost), + fNextPluginRect(), fUI(this, winId, sampleRate, editParameterCallback, setParameterCallback, @@ -394,14 +394,21 @@ public: v3_result getSize(v3_view_rect* const rect) const noexcept { - rect->left = rect->top = 0; - rect->right = fUI.getWidth(); - rect->bottom = fUI.getHeight(); - #ifdef DISTRHO_OS_MAC - const double scaleFactor = fUI.getScaleFactor(); - rect->right /= scaleFactor; - rect->bottom /= scaleFactor; - #endif + if (fIsResizingFromPlugin) + { + *rect = fNextPluginRect; + } + else + { + rect->left = rect->top = 0; + rect->right = fUI.getWidth(); + rect->bottom = fUI.getHeight(); + #ifdef DISTRHO_OS_MAC + const double scaleFactor = fUI.getScaleFactor(); + rect->right /= scaleFactor; + rect->bottom /= scaleFactor; + #endif + } d_stdout("getSize request returning %i %i", rect->right, rect->bottom); return V3_OK; @@ -421,12 +428,15 @@ public: if (fIsResizingFromPlugin) { - d_stdout("host->plugin onSize request %i %i (IGNORED, plugin resize active)", + d_stdout("host->plugin onSize request %i %i (plugin resize was active, unsetting now)", rect.right - rect.left, rect.bottom - rect.top); - return V3_OK; + fIsResizingFromPlugin = false; + } + else + { + d_stdout("host->plugin onSize request %i %i (OK)", rect.right - rect.left, rect.bottom - rect.top); } - d_stdout("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); return V3_OK; @@ -679,6 +689,7 @@ private: float fScaleFactor; bool fIsResizingFromPlugin; bool fIsResizingFromHost; + v3_view_rect fNextPluginRect; // for when plugin requests a new size // Plugin UI (after VST3 stuff so the UI can call into us during its constructor) UIExporter fUI; @@ -791,6 +802,7 @@ private: rect.left = rect.top = 0; rect.right = width; rect.bottom = height; + fNextPluginRect = rect; v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); } @@ -1439,23 +1451,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->getSize(rect); - // FIXME check if all this is really needed - const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - UIExporter tmpUI(nullptr, 0, view->sampleRate, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->instancePointer, lastScaleFactor); - rect->left = rect->top = 0; - view->nextWidth = tmpUI.getWidth(); - view->nextHeight = tmpUI.getHeight(); -#ifdef DISTRHO_OS_MAC - const double scaleFactor = tmpUI.getScaleFactor(); - view->nextWidth /= scaleFactor; - view->nextHeight /= scaleFactor; -#endif - rect->right = view->nextWidth; - rect->bottom = view->nextHeight; - d_stdout("dpf_plugin_view::get_size => %p | next size %u %u with scale factor %f", self, view->nextWidth, view->nextHeight, lastScaleFactor); - return V3_OK; + return V3_NOT_INITIALIZED; } static v3_result V3_API on_size(void* const self, v3_view_rect* const rect) From 93abcc869bb9e839d4c49db3585155af177d36c6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Jun 2022 23:47:44 +0100 Subject: [PATCH 409/504] Proper MIDI implementation for VST3, sort input events ourselves Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 425 ++++++++++++++++++++++-------- 1 file changed, 320 insertions(+), 105 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 8af93a08..ff4d18f3 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -42,7 +42,6 @@ * - have parameter outputs host-provided UI working in at least 1 host * - parameter groups via unit ids * - test parameter changes from DSP (aka requestParameterValueChange) - * - test receiving midi CC * - implement getParameterNormalized/setParameterNormalized for MIDI CC params ? * - fully implemented parameter stuff and verify * - float to int safe casting @@ -51,7 +50,6 @@ * - MIDI CC changes (need to store value to give to the host?) * - MIDI program changes * - MIDI sysex - * - append MIDI input events in a sorted way * == BUSES * - bus arrangements * - optional audio buses @@ -249,6 +247,288 @@ class PluginVst3 numCV(0) {} } inputBuses, outputBuses; + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + /* handy class for storing and sorting events and MIDI CC parameters + * only stores events for which a MIDI conversion is possible. + */ + struct InputEventsList { + enum Type { + NoteOn, + NoteOff, + SysexData, + PolyPressure, + CC_Normal, + CC_ChannelPressure, + CC_Pitchbend, + UI_MIDI // event from UI + }; + struct InputEventStorage { + Type type; + union { + v3_event_note_on noteOn; + v3_event_note_off noteOff; + v3_event_data sysexData; + v3_event_poly_pressure polyPressure; + uint8_t midi[3]; + }; + } eventListStorage[kMaxMidiEvents]; + + struct InputEventTiming { + int32_t sampleOffset; + const InputEventStorage* storage; + InputEventTiming* next; + } eventList[kMaxMidiEvents]; + + uint16_t numUsed; + int32_t firstSampleOffset; + int32_t lastSampleOffset; + InputEventTiming* firstEvent; + InputEventTiming* lastEvent; + + void init() + { + numUsed = 0; + firstSampleOffset = lastSampleOffset = 0; + firstEvent = nullptr; + } + + uint32_t convert(MidiEvent midiEvents[kMaxMidiEvents]) const noexcept + { + uint32_t count = 0; + + for (const InputEventTiming* event = firstEvent; event != nullptr; event = event->next) + { + MidiEvent& midiEvent(midiEvents[count++]); + midiEvent.frame = event->sampleOffset; + + const InputEventStorage& eventStorage(*event->storage); + + switch (eventStorage.type) + { + case NoteOn: + midiEvent.size = 3; + midiEvent.data[0] = 0x90 | (eventStorage.noteOn.channel & 0xf); + midiEvent.data[1] = eventStorage.noteOn.pitch; + midiEvent.data[2] = std::max(0, std::min(127, (int)(eventStorage.noteOn.velocity * 127))); + midiEvent.data[3] = 0; + break; + case NoteOff: + midiEvent.size = 3; + midiEvent.data[0] = 0x80 | (eventStorage.noteOff.channel & 0xf); + midiEvent.data[1] = eventStorage.noteOff.pitch; + midiEvent.data[2] = std::max(0, std::min(127, (int)(eventStorage.noteOff.velocity * 127))); + midiEvent.data[3] = 0; + break; + /* TODO + case SysexData: + break; + */ + case PolyPressure: + midiEvent.size = 3; + midiEvent.data[0] = 0xA0 | (eventStorage.polyPressure.channel & 0xf); + midiEvent.data[1] = eventStorage.polyPressure.pitch; + midiEvent.data[2] = std::max(0, std::min(127, (int)(eventStorage.polyPressure.pressure * 127))); + midiEvent.data[3] = 0; + break; + case CC_Normal: + midiEvent.size = 3; + midiEvent.data[0] = 0xB0 | (eventStorage.midi[0] & 0xf); + midiEvent.data[1] = eventStorage.midi[1]; + midiEvent.data[2] = eventStorage.midi[2]; + break; + case CC_ChannelPressure: + midiEvent.size = 2; + midiEvent.data[0] = 0xD0 | (eventStorage.midi[0] & 0xf); + midiEvent.data[1] = eventStorage.midi[1]; + midiEvent.data[2] = 0; + break; + case CC_Pitchbend: + midiEvent.size = 3; + midiEvent.data[0] = 0xE0 | (eventStorage.midi[0] & 0xf); + midiEvent.data[1] = eventStorage.midi[1]; + midiEvent.data[2] = eventStorage.midi[2]; + break; + case UI_MIDI: + midiEvent.size = 3; + midiEvent.data[0] = eventStorage.midi[0]; + midiEvent.data[1] = eventStorage.midi[1]; + midiEvent.data[2] = eventStorage.midi[2]; + break; + default: + midiEvent.size = 0; + break; + } + } + + return count; + } + + bool appendEvent(const v3_event& event) noexcept + { + // only save events that can be converted directly into MIDI + switch (event.type) + { + case V3_EVENT_NOTE_ON: + case V3_EVENT_NOTE_OFF: + // case V3_EVENT_DATA: + case V3_EVENT_POLY_PRESSURE: + break; + default: + return false; + } + + InputEventStorage& eventStorage(eventListStorage[numUsed]); + + switch (event.type) + { + case V3_EVENT_NOTE_ON: + eventStorage.type = NoteOn; + eventStorage.noteOn = event.note_on; + break; + case V3_EVENT_NOTE_OFF: + eventStorage.type = NoteOff; + eventStorage.noteOff = event.note_off; + break; + case V3_EVENT_DATA: + eventStorage.type = SysexData; + eventStorage.sysexData = event.data; + break; + case V3_EVENT_POLY_PRESSURE: + eventStorage.type = PolyPressure; + eventStorage.polyPressure = event.poly_pressure; + break; + default: + return false; + } + + eventList[numUsed].sampleOffset = event.sample_offset; + eventList[numUsed].storage = &eventStorage; + + return placeSorted(event.sample_offset); + } + + bool appendCC(const int32_t sampleOffset, v3_param_id paramId, const double value) noexcept + { + InputEventStorage& eventStorage(eventListStorage[numUsed]); + + const uint8_t cc = paramId % 130; + + switch (cc) + { + case 128: + eventStorage.type = CC_ChannelPressure; + eventStorage.midi[1] = std::max(0, std::min(127, (int)(value * 127))); + eventStorage.midi[2] = 0; + break; + case 129: + eventStorage.type = CC_Pitchbend; + eventStorage.midi[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f; + eventStorage.midi[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7; + break; + default: + eventStorage.type = CC_Normal; + eventStorage.midi[1] = cc; + eventStorage.midi[2] = std::max(0, std::min(127, (int)(value * 127))); + break; + } + + eventStorage.midi[0] = paramId / 130; + + eventList[numUsed].sampleOffset = sampleOffset; + eventList[numUsed].storage = &eventStorage; + + return placeSorted(sampleOffset); + } + + #if DISTRHO_PLUGIN_HAS_UI + // NOTE always runs first + bool appendFromUI(const uint8_t midiData[3]) + { + InputEventStorage& eventStorage(eventListStorage[numUsed]); + + eventStorage.type = UI_MIDI; + std::memcpy(eventStorage.midi, midiData, sizeof(uint8_t)*3); + + InputEventTiming* const event = &eventList[numUsed]; + + event->sampleOffset = 0; + event->storage = &eventStorage; + event->next = nullptr; + + if (numUsed == 0) + { + firstEvent = lastEvent = event; + } + else + { + lastEvent->next = event; + lastEvent = event; + } + + return ++numUsed == kMaxMidiEvents; + } + #endif + + private: + bool placeSorted(const int32_t sampleOffset) noexcept + { + InputEventTiming* const event = &eventList[numUsed]; + + // initialize + if (numUsed == 0) + { + firstSampleOffset = lastSampleOffset = sampleOffset; + firstEvent = lastEvent = event; + event->next = nullptr; + } + // push to the back + else if (sampleOffset >= lastSampleOffset) + { + lastSampleOffset = sampleOffset; + lastEvent->next = event; + lastEvent = event; + event->next = nullptr; + } + // push to the front + else if (sampleOffset < firstSampleOffset) + { + firstSampleOffset = sampleOffset; + event->next = firstEvent; + firstEvent = event; + } + // find place in between events + else + { + // keep reference out of the loop so we can check validity afterwards + InputEventTiming* event2 = firstEvent; + + // iterate all events + for (; event2 != nullptr; event2 = event2->next) + { + // if offset is higher than iterated event, stop and insert in-between + if (sampleOffset > event2->sampleOffset) + break; + + // if offset matches, find the last event with the same offset so we can push after it + if (sampleOffset == event2->sampleOffset) + { + event2 = event2->next; + for (; event2 != nullptr && sampleOffset == event2->sampleOffset; event2 = event2->next) {} + break; + } + } + + DISTRHO_SAFE_ASSERT_RETURN(event2 != nullptr, true); + + event->next = event2->next; + event2->next = event; + } + + return ++numUsed == kMaxMidiEvents; + } + } inputEventList; + #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT + public: PluginVst3(v3_host_application** const host) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), @@ -1285,7 +1565,8 @@ public: #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - uint32_t midiEventCount = 0; + bool canAppendMoreEvents = true; + inputEventList.init(); #if DISTRHO_PLUGIN_HAS_UI while (fNotesRingBuffer.isDataAvailableForReading()) @@ -1294,121 +1575,34 @@ public: if (! fNotesRingBuffer.readCustomData(midiData, 3)) break; - MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); - midiEvent.frame = 0; - midiEvent.size = 3; - std::memcpy(midiEvent.data, midiData, 3); - - if (midiEventCount == kMaxMidiEvents) + if (inputEventList.appendFromUI(midiData)) + { + canAppendMoreEvents = false; break; + } } #endif - if (v3_event_list** const eventptr = data->input_events) + if (canAppendMoreEvents) { - v3_event event; - for (uint32_t i = 0, count = v3_cpp_obj(eventptr)->get_event_count(eventptr); i < count; ++i) + if (v3_event_list** const eventptr = data->input_events) { - if (v3_cpp_obj(eventptr)->get_event(eventptr, i, &event) != V3_OK) - break; - - // check if event can be encoded as MIDI - switch (event.type) + v3_event event; + for (uint32_t i = 0, count = v3_cpp_obj(eventptr)->get_event_count(eventptr); i < count; ++i) { - case V3_EVENT_NOTE_ON: - case V3_EVENT_NOTE_OFF: - // case V3_EVENT_DATA: - case V3_EVENT_POLY_PRESSURE: - break; - // case V3_EVENT_NOTE_EXP_VALUE: - // case V3_EVENT_NOTE_EXP_TEXT: - // case V3_EVENT_CHORD: - // case V3_EVENT_SCALE: - // case V3_EVENT_LEGACY_MIDI_CC_OUT: - default: - continue; - } - - MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); - midiEvent.frame = event.sample_offset; + if (v3_cpp_obj(eventptr)->get_event(eventptr, i, &event) != V3_OK) + break; - // encode event as MIDI - switch (event.type) - { - case V3_EVENT_NOTE_ON: - midiEvent.size = 3; - midiEvent.data[0] = 0x90 | (event.note_on.channel & 0xf); - midiEvent.data[1] = event.note_on.pitch; - midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_on.velocity * 127))); - midiEvent.data[3] = 0; - break; - case V3_EVENT_NOTE_OFF: - midiEvent.size = 3; - midiEvent.data[0] = 0x80 | (event.note_off.channel & 0xf); - midiEvent.data[1] = event.note_off.pitch; - midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_off.velocity * 127))); - midiEvent.data[3] = 0; - break; - case V3_EVENT_POLY_PRESSURE: - midiEvent.size = 3; - midiEvent.data[0] = 0xA0 | (event.poly_pressure.channel & 0xf); - midiEvent.data[1] = event.poly_pressure.pitch; - midiEvent.data[2] = std::max(0, std::min(127, (int)(event.poly_pressure.pressure * 127))); - midiEvent.data[3] = 0; - break; - default: - midiEvent.size = 0; - break; + if (inputEventList.appendEvent(event)) + { + canAppendMoreEvents = false; + break; + } } - - if (midiEventCount == kMaxMidiEvents) - break; } } - - // TODO append parameter MIDI events in a sorted way - /* -#if DISTRHO_PLUGIN_WANT_PROGRAMS - if (rindex == 0) - continue; - --rindex; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); - midiEvent.frame = offset; - midiEvent.size = 3; - midiEvent.data[0] = (rindex / 130) & 0xf; - - switch (rindex) - { - case 128: // channel pressure - midiEvent.data[0] |= 0xD0; - midiEvent.data[1] = std::max(0, std::min(127, (int)(value * 127))); - midiEvent.data[2] = 0; - midiEvent.data[3] = 0; - break; - case 129: // pitchbend - midiEvent.data[0] |= 0xE0; - midiEvent.data[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f; - midiEvent.data[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7; - midiEvent.data[3] = 0; - break; - default: - midiEvent.data[0] |= 0xB0; - midiEvent.data[1] = rindex % 130; - midiEvent.data[2] = std::max(0, std::min(127, (int)(value * 127))); - midiEvent.data[3] = 0; - break; - } - - if (midiEventCount == kMaxMidiEvents) - break; - } -#endif - */ #endif - // if there are any parameter changes at frame 0, set them here if (v3_param_changes** const inparamsptr = data->input_params) { int32_t offset; @@ -1424,12 +1618,32 @@ public: #if DPF_VST3_HAS_INTERNAL_PARAMETERS if (rindex < kVst3InternalParameterCount) + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + // if there are any MIDI CC events as parameter changes, handle them here + if (canAppendMoreEvents && rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end) + { + for (int32_t j = 0, pcount = v3_cpp_obj(queue)->get_point_count(queue); j < pcount; ++j) + { + if (v3_cpp_obj(queue)->get_point(queue, j, &offset, &value) != V3_OK) + break; + + if (inputEventList.appendCC(offset, rindex, value)) + { + canAppendMoreEvents = false; + break; + } + } + } + #endif continue; + } #endif if (v3_cpp_obj(queue)->get_point_count(queue) <= 0) continue; + // if there are any parameter changes at frame 0, handle them here if (v3_cpp_obj(queue)->get_point(queue, 0, &offset, &value) != V3_OK) break; @@ -1442,6 +1656,7 @@ public: } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + const uint32_t midiEventCount = inputEventList.convert(fMidiEvents); fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount); #else fPlugin.run(inputs, outputs, data->nframes); From c7baf5cefe85be9a69f1e321c17fda9c6357f0f0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 20 Jun 2022 23:55:10 +0100 Subject: [PATCH 410/504] Correct offset for MIDI CC params Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index ff4d18f3..fbd9c0e0 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -411,6 +411,8 @@ class PluginVst3 { InputEventStorage& eventStorage(eventListStorage[numUsed]); + paramId -= kVst3InternalParameterMidiCC_start; + const uint8_t cc = paramId % 130; switch (cc) From e36355245414223d499421a87d88d8aef0b1078b Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 21 Jun 2022 18:05:43 +0100 Subject: [PATCH 411/504] Only build embed plugin example if DGL libs are available Signed-off-by: falkTX --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 15922367..8eae693c 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,6 @@ dgl: examples: dgl $(MAKE) all -C examples/CVPort - $(MAKE) all -C examples/EmbedExternalUI $(MAKE) all -C examples/FileHandling $(MAKE) all -C examples/Info $(MAKE) all -C examples/Latency @@ -34,6 +33,9 @@ examples: dgl ifeq ($(HAVE_CAIRO),true) $(MAKE) all -C examples/CairoUI endif +ifeq ($(HAVE_DGL),true) + $(MAKE) all -C examples/EmbedExternalUI +endif ifeq ($(CAN_GENERATE_TTL),true) gen: examples utils/lv2_ttl_generator From f0c16920f5a62300f5197b6a339dbc5fec12a882 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 25 Jun 2022 23:22:17 +0100 Subject: [PATCH 412/504] Leave warning note about TimePosition.frame not being monotonic Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 9fdc86aa..964acdc7 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -750,6 +750,8 @@ struct TimePosition { /** Current host transport position in frames. + @note This value is not always monotonic, + with some plugin hosts assigning it based on a source that can accumulate rounding errors. */ uint64_t frame; From d4a50515e433f2f6689d6f033c070570f70a6cb4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Jun 2022 09:29:53 +0100 Subject: [PATCH 413/504] Less verbose vst3 prints (related to params) --- distrho/src/DistrhoPluginVST3.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index fbd9c0e0..23a7b06f 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -3457,7 +3457,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static v3_result V3_API get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output) { - d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); + d_debug("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; @@ -3468,7 +3468,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static double V3_API normalised_parameter_to_plain(void* self, v3_param_id index, double normalised) { - d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); + d_debug("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; @@ -3479,7 +3479,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { static double V3_API plain_parameter_to_normalised(void* self, v3_param_id index, double plain) { - d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); + d_debug("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain); dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; From 5de60bb6d9f247eab95c350afa2d7d40d674ac46 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Jun 2022 09:31:23 +0100 Subject: [PATCH 414/504] Deal with vst3 hosts that only get size before attaching view --- distrho/src/DistrhoUIVST3.cpp | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index cbca49d5..5d8beb0e 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -249,7 +249,8 @@ public: const float scaleFactor, const double sampleRate, void* const instancePointer, - const bool willResizeFromHost) + const bool willResizeFromHost, + const bool needsResizeFromPlugin) : #if !DPF_VST3_USING_HOST_RUN_LOOP NativeIdleCallback(fUI), @@ -258,10 +259,11 @@ public: fHostApplication(host), fConnection(connection), fFrame(frame), - fReadyForPluginData(false), fScaleFactor(scaleFactor), + fReadyForPluginData(false), fIsResizingFromPlugin(false), fIsResizingFromHost(willResizeFromHost), + fNeedsResizeFromPlugin(needsResizeFromPlugin), fNextPluginRect(), fUI(this, winId, sampleRate, editParameterCallback, @@ -302,6 +304,11 @@ public: fUI.setWindowSizeForVST3(nextWidth, nextHeight); } } + else if (fNeedsResizeFromPlugin) + { + fNeedsResizeFromPlugin = false; + setSize(fUI.getWidth(), fUI.getHeight()); + } if (fConnection != nullptr) connect(fConnection); @@ -685,10 +692,11 @@ private: v3_plugin_frame** fFrame; // Temporary data - bool fReadyForPluginData; float fScaleFactor; + bool fReadyForPluginData; bool fIsResizingFromPlugin; bool fIsResizingFromHost; + bool fNeedsResizeFromPlugin; v3_view_rect fNextPluginRect; // for when plugin requests a new size // Plugin UI (after VST3 stuff so the UI can call into us during its constructor) @@ -1128,6 +1136,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { v3_plugin_frame** frame; v3_run_loop** runloop; uint32_t nextWidth, nextHeight; + bool sizeRequestedBeforeBeingAttached; dpf_plugin_view(v3_host_application** const host, void* const instance, const double sr) : refcounter(1), @@ -1137,7 +1146,8 @@ struct dpf_plugin_view : v3_plugin_view_cpp { frame(nullptr), runloop(nullptr), nextWidth(0), - nextHeight(0) + nextHeight(0), + sizeRequestedBeforeBeingAttached(false) { d_stdout("dpf_plugin_view() with hostApplication %p", hostApplication); @@ -1337,11 +1347,13 @@ struct dpf_plugin_view : v3_plugin_view_cpp { lastScaleFactor, view->sampleRate, view->instancePointer, - view->nextWidth > 0 && view->nextHeight > 0); + view->nextWidth > 0 && view->nextHeight > 0, + view->sizeRequestedBeforeBeingAttached); view->uivst3->postInit(view->nextWidth, view->nextHeight); view->nextWidth = 0; view->nextHeight = 0; + view->sizeRequestedBeforeBeingAttached = false; #if DPF_VST3_USING_HOST_RUN_LOOP // register a timer host run loop stuff @@ -1451,11 +1463,15 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->getSize(rect); + d_stdout("dpf_plugin_view::get_size => %p | V3_NOT_INITIALIZED", self); + view->sizeRequestedBeforeBeingAttached = true; return V3_NOT_INITIALIZED; } 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}", + 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); @@ -1488,6 +1504,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); dpf_plugin_view* const view = *static_cast(self); view->frame = frame; @@ -1495,7 +1512,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->setFrame(frame); - return V3_NOT_INITIALIZED; + return V3_OK; } static v3_result V3_API can_resize(void* const self) @@ -1517,6 +1534,8 @@ 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}", + self, rect->top, rect->left, rect->right, rect->bottom); dpf_plugin_view* const view = *static_cast(self); if (UIVst3* const uivst3 = view->uivst3) @@ -1536,7 +1555,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { minimumHeight /= scaleFactor; #endif applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); - return V3_TRUE; + return V3_NOT_INITIALIZED; } }; From 8823697715378af85d7f26421bb65d63f054bb55 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Jun 2022 09:32:53 +0100 Subject: [PATCH 415/504] Cleanup --- distrho/src/DistrhoUIVST3.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 5d8beb0e..5a52d376 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -1541,20 +1541,6 @@ struct dpf_plugin_view : v3_plugin_view_cpp { if (UIVst3* const uivst3 = view->uivst3) return uivst3->checkSizeConstraint(rect); - // FIXME check if all this is really needed - const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; - UIExporter tmpUI(nullptr, 0, view->sampleRate, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - view->instancePointer, lastScaleFactor); - uint minimumWidth, minimumHeight; - bool keepAspectRatio; - tmpUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); -#ifdef DISTRHO_OS_MAC - const double scaleFactor = tmpUI.getScaleFactor(); - minimumWidth /= scaleFactor; - minimumHeight /= scaleFactor; -#endif - applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); return V3_NOT_INITIALIZED; } }; From 144ec075cc4feed77bc8ca08d0d16901f04a3806 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Jun 2022 10:00:12 +0100 Subject: [PATCH 416/504] Improve behaviour for previous commit, wait until first idle --- distrho/src/DistrhoUIVST3.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 5a52d376..44469e71 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -306,7 +306,6 @@ public: } else if (fNeedsResizeFromPlugin) { - fNeedsResizeFromPlugin = false; setSize(fUI.getWidth(), fUI.getHeight()); } @@ -669,6 +668,12 @@ public: requestMorePluginData(); } + if (fNeedsResizeFromPlugin) + { + fNeedsResizeFromPlugin = false; + d_stdout("first resize forced behaviour is now stopped"); + } + if (fIsResizingFromHost) { fIsResizingFromHost = false; @@ -798,12 +803,21 @@ private: if (fIsResizingFromHost) { - d_stdout("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); - return; + 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); } - d_stdout("plugin->host setSize %u %u (OK)", width, height); - fIsResizingFromPlugin = true; v3_view_rect rect; From 33dd907dba563f3f07b61be282b28e95ca8a8ba0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 27 Jun 2022 17:13:23 +0100 Subject: [PATCH 417/504] Change VST2 dummy plugin to be pointer cleared on effClose Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 3 - distrho/src/DistrhoPluginVST2.cpp | 82 ++++++++++++++------------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 00fed5d6..9a94bb1b 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -954,9 +954,6 @@ private: static const PortGroupWithId sFallbackPortGroup; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) -#ifndef DISTRHO_PLUGIN_TARGET_VST3 /* there is no way around this for VST3 */ - DISTRHO_PREVENT_HEAP_ALLOCATION -#endif }; // ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 820eb76b..9a1cad45 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1386,33 +1386,38 @@ struct VstObject { #define vstObjectPtr (VstObject*)effect->object #define pluginPtr (vstObjectPtr)->plugin +static ScopedPointer sPlugin; + static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) { // first internal init const bool doInternalInit = (opcode == -1729 && index == 0xdead && value == 0xf00d); - if (doInternalInit) + if (doInternalInit || opcode == effOpen) { - // set valid but dummy values - d_nextBufferSize = 512; - d_nextSampleRate = 44100.0; - d_nextPluginIsDummy = true; - d_nextCanRequestParameterValueChanges = true; - } + if (sPlugin == nullptr) + { + // set valid but dummy values + d_nextBufferSize = 512; + d_nextSampleRate = 44100.0; + d_nextPluginIsDummy = true; + d_nextCanRequestParameterValueChanges = true; - // Create dummy plugin to get data from - static PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); + // Create dummy plugin to get data from + sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr); - if (doInternalInit) - { - // unset - d_nextBufferSize = 0; - d_nextSampleRate = 0.0; - d_nextPluginIsDummy = false; - d_nextCanRequestParameterValueChanges = false; + // unset + d_nextBufferSize = 0; + d_nextSampleRate = 0.0; + d_nextPluginIsDummy = false; + d_nextCanRequestParameterValueChanges = false; + } - *(PluginExporter**)ptr = &plugin; - return 0; + if (doInternalInit) + { + *(PluginExporter**)ptr = sPlugin.get(); + return 0; + } } // handle base opcodes @@ -1462,33 +1467,34 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t delete obj; #endif + sPlugin = nullptr; return 1; } //delete effect; return 0; case effGetParamLabel: - if (ptr != nullptr && index < static_cast(plugin.getParameterCount())) + if (ptr != nullptr && index < static_cast(sPlugin->getParameterCount())) { - DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterUnit(index), 8); + DISTRHO_NAMESPACE::strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8); return 1; } return 0; case effGetParamName: - if (ptr != nullptr && index < static_cast(plugin.getParameterCount())) + if (ptr != nullptr && index < static_cast(sPlugin->getParameterCount())) { - const String& shortName(plugin.getParameterShortName(index)); + const String& shortName(sPlugin->getParameterShortName(index)); if (shortName.isNotEmpty()) DISTRHO_NAMESPACE::strncpy((char*)ptr, shortName, 16); else - DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterName(index), 16); + DISTRHO_NAMESPACE::strncpy((char*)ptr, sPlugin->getParameterName(index), 16); return 1; } return 0; case effGetParameterProperties: - if (ptr != nullptr && index < static_cast(plugin.getParameterCount())) + if (ptr != nullptr && index < static_cast(sPlugin->getParameterCount())) { if (VstParameterProperties* const properties = (VstParameterProperties*)ptr) { @@ -1496,19 +1502,19 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t // full name DISTRHO_NAMESPACE::strncpy(properties->label, - plugin.getParameterName(index), + sPlugin->getParameterName(index), sizeof(properties->label)); // short name - const String& shortName(plugin.getParameterShortName(index)); + const String& shortName(sPlugin->getParameterShortName(index)); if (shortName.isNotEmpty()) DISTRHO_NAMESPACE::strncpy(properties->shortLabel, - plugin.getParameterShortName(index), + sPlugin->getParameterShortName(index), sizeof(properties->shortLabel)); // parameter hints - const uint32_t hints = plugin.getParameterHints(index); + const uint32_t hints = sPlugin->getParameterHints(index); if (hints & kParameterIsOutput) return 1; @@ -1520,7 +1526,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t if (hints & kParameterIsInteger) { - const ParameterRanges& ranges(plugin.getParameterRanges(index)); + const ParameterRanges& ranges(sPlugin->getParameterRanges(index)); properties->flags |= kVstParameterUsesIntegerMinMax; properties->minInteger = static_cast(ranges.min); properties->maxInteger = static_cast(ranges.max); @@ -1532,14 +1538,14 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t } // parameter group (category in vst) - const uint32_t groupId = plugin.getParameterGroupId(index); + const uint32_t groupId = sPlugin->getParameterGroupId(index); if (groupId != kPortGroupNone) { // we can't use groupId directly, so use the index array where this group is stored in - for (uint32_t i=0, count=plugin.getPortGroupCount(); i < count; ++i) + for (uint32_t i=0, count=sPlugin->getPortGroupCount(); i < count; ++i) { - const PortGroupWithId& portGroup(plugin.getPortGroupByIndex(i)); + const PortGroupWithId& portGroup(sPlugin->getPortGroupByIndex(i)); if (portGroup.groupId == groupId) { @@ -1553,8 +1559,8 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t if (properties->category != 0) { - for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i) - if (plugin.getParameterGroupId(i) == groupId) + for (uint32_t i=0, count=sPlugin->getParameterCount(); i < count; ++i) + if (sPlugin->getParameterGroupId(i) == groupId) ++properties->numParametersInCategory; } } @@ -1574,7 +1580,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetEffectName: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, plugin.getName(), 32); + DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getName(), 32); return 1; } return 0; @@ -1582,7 +1588,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetVendorString: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, plugin.getMaker(), 32); + DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getMaker(), 32); return 1; } return 0; @@ -1590,13 +1596,13 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetProductString: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, plugin.getLabel(), 32); + DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getLabel(), 32); return 1; } return 0; case effGetVendorVersion: - return plugin.getVersion(); + return sPlugin->getVersion(); case effGetVstVersion: return kVstVersion; From a4eed81b7311c32284883f265d7634c5354f17d7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 27 Jun 2022 17:35:46 +0100 Subject: [PATCH 418/504] Fix previous commit, missed an include Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST2.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 9a1cad45..56a3e116 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -17,6 +17,7 @@ #include "DistrhoPluginInternal.hpp" #include "../DistrhoPluginUtils.hpp" #include "../extra/ScopedSafeLocale.hpp" +#include "../extra/ScopedPointer.hpp" #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI # undef DISTRHO_PLUGIN_HAS_UI From db893e72df9932005f1c40a55691d7ae13952a56 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 27 Jun 2022 23:42:20 +0100 Subject: [PATCH 419/504] Decode file URLs provided by DBus portals Signed-off-by: falkTX --- distrho/extra/FileBrowserDialogImpl.cpp | 51 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index bb15b3ac..657bf326 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -59,6 +59,18 @@ START_NAMESPACE_DISTRHO // static pointer used for signal null/none action taken static const char* const kSelectedFileCancelled = "__dpf_cancelled__"; +#ifdef HAVE_DBUS +static constexpr bool isHexChar(const char c) noexcept +{ + return c >= '0' && c <= 'f' && (c < '9' || (c >= 'A' && c <= 'F') || c >= 'a'); +} + +static constexpr int toHexChar(const char c) noexcept +{ + return c >= '0' && c <= '9' ? c - '0' : (c >= 'A' && c <= 'F' ? c - 'A' : c - 'a') + 10; +} +#endif + struct FileBrowserData { const char* selectedFile; @@ -568,7 +580,44 @@ bool fileBrowserIdle(const FileBrowserHandle handle) DISTRHO_SAFE_ASSERT_BREAK(value != nullptr); if (const char* const localvalue = std::strstr(value, "file:///")) - handle->selectedFile = strdup(localvalue + 7); + { + if (char* const decodedvalue = strdup(localvalue + 7)) + { + for (char* s = decodedvalue; (s = std::strchr(s, '%')) != nullptr; ++s) + { + if (! isHexChar(s[1]) || ! isHexChar(s[2])) + continue; + + const int decodedNum = toHexChar(s[1]) * 0x10 + toHexChar(s[2]); + + char replacementChar; + switch (decodedNum) + { + case 0x20: replacementChar = ' '; break; + case 0x22: replacementChar = '\"'; break; + case 0x23: replacementChar = '#'; break; + case 0x25: replacementChar = '%'; break; + case 0x3c: replacementChar = '<'; break; + case 0x3e: replacementChar = '>'; break; + case 0x5b: replacementChar = '['; break; + case 0x5c: replacementChar = '\\'; break; + case 0x5d: replacementChar = ']'; break; + case 0x5e: replacementChar = '^'; break; + case 0x60: replacementChar = '`'; break; + case 0x7b: replacementChar = '{'; break; + case 0x7c: replacementChar = '|'; break; + case 0x7d: replacementChar = '}'; break; + case 0x7e: replacementChar = '~'; break; + default: continue; + } + + s[0] = replacementChar; + std::memmove(s + 1, s + 3, std::strlen(s) - 2); + } + + handle->selectedFile = decodedvalue; + } + } } while(false); From e22b971a123e46f18189aaef9d3584a670c32cd7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 28 Jun 2022 13:26:39 +0100 Subject: [PATCH 420/504] Fix a typo Signed-off-by: falkTX --- distrho/extra/FileBrowserDialogImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index 657bf326..587de38a 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -62,7 +62,7 @@ static const char* const kSelectedFileCancelled = "__dpf_cancelled__"; #ifdef HAVE_DBUS static constexpr bool isHexChar(const char c) noexcept { - return c >= '0' && c <= 'f' && (c < '9' || (c >= 'A' && c <= 'F') || c >= 'a'); + return c >= '0' && c <= 'f' && (c <= '9' || (c >= 'A' && c <= 'F') || c >= 'a'); } static constexpr int toHexChar(const char c) noexcept From 90b8d2d2710d7aeb9f8f097dc3b4baed11c1073f Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 29 Jun 2022 01:05:18 +0100 Subject: [PATCH 421/504] Fix macOS hosts providing windowless views on init Like FLStudio VST3 hosting --- dgl/src/pugl.cpp | 18 ++++++++++++++---- distrho/src/DistrhoUIVST3.cpp | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 119c576b..3f63905a 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -237,10 +237,20 @@ double puglGetScaleFactorFromParent(const PuglView* const view) { const PuglNativeView parent = view->parent ? view->parent : view->transientParent ? view->transientParent : 0; #if defined(DISTRHO_OS_MAC) - NSWindow* const window = parent != 0 ? [(NSView*)parent window] - : view->impl->window ? view->impl->window : [view->impl->wrapperView window]; - NSScreen* const screen = window != nullptr ? [window screen] : [NSScreen mainScreen]; - return [screen backingScaleFactor]; + // some of these can return 0 as backingScaleFactor, pick the most relevant valid one + const NSWindow* possibleWindows[] = { + parent != 0 ? [(NSView*)parent window] : nullptr, + view->impl->window, + [view->impl->wrapperView window] + }; + for (size_t i=0; iimpl->hwnd; return puglWinGetViewScaleFactor(hwnd); diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 44469e71..9e6674b2 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -306,6 +306,7 @@ public: } else if (fNeedsResizeFromPlugin) { + d_stdout("postInit forcely sets size from plugin as %u %u", fUI.getWidth(), fUI.getHeight()); setSize(fUI.getWidth(), fUI.getHeight()); } @@ -1478,6 +1479,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp { return uivst3->getSize(rect); d_stdout("dpf_plugin_view::get_size => %p | V3_NOT_INITIALIZED", self); + std::memset(rect, 0, sizeof(v3_view_rect)); view->sizeRequestedBeforeBeingAttached = true; return V3_NOT_INITIALIZED; } From 38dad9fdfd75e0887a31abb9bfe7acff88cfd85b Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 30 Jun 2022 15:40:46 +0100 Subject: [PATCH 422/504] Tweak requirements for filebrowser dialog on macOS Signed-off-by: falkTX --- distrho/extra/FileBrowserDialogImpl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index 587de38a..108475cd 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -321,8 +321,9 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, ScopedPointer handle(new FileBrowserData(options.saving)); #ifdef DISTRHO_OS_MAC -# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_8 +# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 // unsupported + d_stderr2("fileBrowserCreate is unsupported on macos < 10.8"); return nullptr; # else NSSavePanel* const nsBasePanel = handle->nsBasePanel; From 194f04aee8396c98f320052626603d77a1b1931e Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 30 Jun 2022 20:02:42 +0100 Subject: [PATCH 423/504] Allow to build with custom DISTRHO and DGL namespace Signed-off-by: falkTX --- Makefile.base.mk | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 8f6ec0ce..efec11d3 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -438,6 +438,17 @@ else ifeq ($(HAVE_OPENGL),true) HAVE_DGL = $(HAVE_X11) endif +# --------------------------------------------------------------------------------------------------------------------- +# Namespace flags + +ifneq ($(DISTRHO_NAMESPACE),) +BUILD_CXX_FLAGS += -DDISTRHO_NAMESPACE=$(DISTRHO_NAMESPACE) +endif + +ifneq ($(DGL_NAMESPACE),) +BUILD_CXX_FLAGS += -DDGL_NAMESPACE=$(DGL_NAMESPACE) +endif + # --------------------------------------------------------------------------------------------------------------------- # Optional flags @@ -473,7 +484,6 @@ ifeq ($(USE_RGBA),true) BUILD_CXX_FLAGS += -DDGL_USE_RGBA endif - # --------------------------------------------------------------------------------------------------------------------- # Set app extension From 478cb33f041b167ebaa07ba34c7cf438dc23d96a Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 1 Jul 2022 18:13:07 +0100 Subject: [PATCH 424/504] wasm and egl/gles related things Signed-off-by: falkTX --- Makefile.base.mk | 174 ++++++++++++++++++----------- Makefile.plugins.mk | 11 +- dgl/src/Application.cpp | 17 ++- dgl/src/ApplicationPrivateData.cpp | 2 + dgl/src/NanoVG.cpp | 9 +- dgl/src/OpenGL.cpp | 62 +++++----- dgl/src/WindowPrivateData.cpp | 3 - dgl/src/nanovg/nanovg_gl.h | 37 +++++- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 42 ++++--- dgl/src/pugl.hpp | 3 - distrho/src/DistrhoDefines.h | 2 + pugl-updates-notes.txt | 3 - tests/Makefile | 22 +++- tests/NanoImage.cpp | 8 +- tests/widgets/ResizeHandle.hpp | 2 +- 16 files changed, 268 insertions(+), 131 deletions(-) delete mode 100644 pugl-updates-notes.txt diff --git a/Makefile.base.mk b/Makefile.base.mk index efec11d3..55dd9a9b 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -25,30 +25,36 @@ ifneq ($(HAIKU),true) ifneq ($(HURD),true) ifneq ($(LINUX),true) ifneq ($(MACOS),true) +ifneq ($(WASM),true) ifneq ($(WINDOWS),true) ifneq (,$(findstring bsd,$(TARGET_MACHINE))) -BSD=true +BSD = true else ifneq (,$(findstring haiku,$(TARGET_MACHINE))) -HAIKU=true +HAIKU = true else ifneq (,$(findstring linux,$(TARGET_MACHINE))) -LINUX=true +LINUX = true else ifneq (,$(findstring gnu,$(TARGET_MACHINE))) -HURD=true +HURD = true else ifneq (,$(findstring apple,$(TARGET_MACHINE))) -MACOS=true +MACOS = true else ifneq (,$(findstring mingw,$(TARGET_MACHINE))) -WINDOWS=true +WINDOWS = true +else ifneq (,$(findstring msys,$(TARGET_MACHINE))) +WINDOWS = true +else ifneq (,$(findstring wasm,$(TARGET_MACHINE))) +WASM = true else ifneq (,$(findstring windows,$(TARGET_MACHINE))) -WINDOWS=true +WINDOWS = true endif -endif -endif -endif -endif -endif -endif +endif # WINDOWS +endif # WASM +endif # MACOS +endif # LINUX +endif # HURD +endif # HAIKU +endif # BSD # --------------------------------------------------------------------------------------------------------------------- # Auto-detect the processor @@ -56,24 +62,28 @@ endif TARGET_PROCESSOR := $(firstword $(subst -, ,$(TARGET_MACHINE))) ifneq (,$(filter i%86,$(TARGET_PROCESSOR))) -CPU_I386=true -CPU_I386_OR_X86_64=true +CPU_I386 = true +CPU_I386_OR_X86_64 = true +endif +ifneq (,$(filter wasm32,$(TARGET_PROCESSOR))) +CPU_I386 = true +CPU_I386_OR_X86_64 = true endif ifneq (,$(filter x86_64,$(TARGET_PROCESSOR))) -CPU_X86_64=true -CPU_I386_OR_X86_64=true +CPU_X86_64 = true +CPU_I386_OR_X86_64 = true endif ifneq (,$(filter arm%,$(TARGET_PROCESSOR))) -CPU_ARM=true -CPU_ARM_OR_AARCH64=true +CPU_ARM = true +CPU_ARM_OR_AARCH64 = true endif ifneq (,$(filter arm64%,$(TARGET_PROCESSOR))) -CPU_ARM64=true -CPU_ARM_OR_AARCH64=true +CPU_ARM64 = true +CPU_ARM_OR_AARCH64 = true endif ifneq (,$(filter aarch64%,$(TARGET_PROCESSOR))) -CPU_AARCH64=true -CPU_ARM_OR_AARCH64=true +CPU_AARCH64 = true +CPU_ARM_OR_AARCH64 = true endif # --------------------------------------------------------------------------------------------------------------------- @@ -90,47 +100,57 @@ endif # Set LINUX_OR_MACOS ifeq ($(LINUX),true) -LINUX_OR_MACOS=true +LINUX_OR_MACOS = true endif ifeq ($(MACOS),true) -LINUX_OR_MACOS=true +LINUX_OR_MACOS = true endif # --------------------------------------------------------------------------------------------------------------------- -# Set MACOS_OR_WINDOWS and HAIKU_OR_MACOS_OR_WINDOWS +# Set MACOS_OR_WINDOWS, MACOS_OR_WASM_OR_WINDOWS, HAIKU_OR_MACOS_OR_WINDOWS and HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS ifeq ($(HAIKU),true) -HAIKU_OR_MACOS_OR_WINDOWS=true +HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true +HAIKU_OR_MACOS_OR_WINDOWS = true endif ifeq ($(MACOS),true) -MACOS_OR_WINDOWS=true -HAIKU_OR_MACOS_OR_WINDOWS=true +HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true +HAIKU_OR_MACOS_OR_WINDOWS = true +MACOS_OR_WASM_OR_WINDOWS = true +MACOS_OR_WINDOWS = true +endif + +ifeq ($(WASM),true) +HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true +MACOS_OR_WASM_OR_WINDOWS = true endif ifeq ($(WINDOWS),true) -MACOS_OR_WINDOWS=true -HAIKU_OR_MACOS_OR_WINDOWS=true +HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS = true +HAIKU_OR_MACOS_OR_WINDOWS = true +MACOS_OR_WASM_OR_WINDOWS = true +MACOS_OR_WINDOWS = true endif # --------------------------------------------------------------------------------------------------------------------- # Set UNIX ifeq ($(BSD),true) -UNIX=true +UNIX = true endif ifeq ($(HURD),true) -UNIX=true +UNIX = true endif ifeq ($(LINUX),true) -UNIX=true +UNIX = true endif ifeq ($(MACOS),true) -UNIX=true +UNIX = true endif # --------------------------------------------------------------------------------------------------------------------- @@ -140,7 +160,12 @@ BASE_FLAGS = -Wall -Wextra -pipe -MD -MP BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections ifeq ($(CPU_I386_OR_X86_64),true) -BASE_OPTS += -mtune=generic -msse -msse2 -mfpmath=sse +BASE_OPTS += -mtune=generic -msse -msse2 +ifeq ($(WASM),true) +BASE_OPTS += -msse3 -msimd128 +else +BASE_OPTS += -mfpmath=sse +endif endif ifeq ($(CPU_ARM),true) @@ -151,16 +176,19 @@ endif ifeq ($(MACOS),true) # MacOS linker flags -LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip -Wl,-dead_strip_dylibs +LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip,-dead_strip_dylibs ifneq ($(SKIP_STRIPPING),true) LINK_OPTS += -Wl,-x endif else # Common linker flags -LINK_OPTS = -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed +LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections ifneq ($(SKIP_STRIPPING),true) LINK_OPTS += -Wl,--strip-all endif +ifneq ($(WASM),true) +LINK_OPTS += -Wl,--as-needed +endif endif ifeq ($(SKIP_STRIPPING),true) @@ -172,7 +200,7 @@ ifeq ($(NOOPT),true) BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections endif -ifneq ($(MACOS_OR_WINDOWS),true) +ifneq ($(MACOS_OR_WASM_OR_WINDOWS),true) ifneq ($(BSD),true) BASE_FLAGS += -fno-gnu-unique endif @@ -191,6 +219,9 @@ endif ifeq ($(DEBUG),true) BASE_FLAGS += -DDEBUG -O0 -g LINK_OPTS = +ifeq ($(WASM),true) +LINK_OPTS += -sASSERTIONS=1 +endif else BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden CXXFLAGS += -fvisibility-inlines-hidden @@ -210,9 +241,12 @@ BUILD_C_FLAGS = $(BASE_FLAGS) -std=gnu99 $(CFLAGS) BUILD_CXX_FLAGS = $(BASE_FLAGS) -std=gnu++11 $(CXXFLAGS) LINK_FLAGS = $(LINK_OPTS) $(LDFLAGS) -ifneq ($(MACOS),true) +ifeq ($(WASM),true) +# Special flag for emscripten +LINK_FLAGS += -sLLD_REPORT_UNDEFINED +else ifneq ($(MACOS),true) # Not available on MacOS -LINK_FLAGS += -Wl,--no-undefined +LINK_FLAGS += -Wl,--no-undefined endif ifeq ($(MACOS_OLD),true) @@ -252,7 +286,7 @@ endif HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) -ifeq ($(MACOS_OR_WINDOWS),true) +ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) HAVE_OPENGL = true else HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) @@ -297,18 +331,13 @@ HAVE_JACK = true ifeq ($(HAIKU),true) DGL_SYSTEM_LIBS += -lbe -endif - -ifeq ($(MACOS),true) +else ifeq ($(MACOS),true) DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo -endif - -ifeq ($(WINDOWS),true) +else ifeq ($(WASM),true) +else ifeq ($(WINDOWS),true) DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 # -lole32 -endif - -ifneq ($(MACOS_OR_WINDOWS),true) +else ifeq ($(HAVE_DBUS),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) @@ -355,18 +384,18 @@ DGL_FLAGS += -DHAVE_OPENGL ifeq ($(HAIKU),true) OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl) OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl) -endif - -ifeq ($(MACOS),true) +else ifeq ($(MACOS),true) OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations OPENGL_LIBS = -framework OpenGL +else ifeq ($(WASM),true) +ifneq ($(USE_GLES2),true) +ifneq ($(USE_GLES3),true) +OPENGL_LIBS = -sLEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0 endif - -ifeq ($(WINDOWS),true) -OPENGL_LIBS = -lopengl32 endif - -ifneq ($(MACOS_OR_WINDOWS),true) +else ifeq ($(WINDOWS),true) +OPENGL_LIBS = -lopengl32 +else OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl x11) OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl x11) endif @@ -378,7 +407,7 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Set Stub specific stuff -ifeq ($(MACOS_OR_WINDOWS),true) +ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) HAVE_STUB = true else HAVE_STUB = $(HAVE_X11) @@ -425,7 +454,7 @@ JACK_LIBS = $(shell $(PKG_CONFIG) --libs jack) endif endif -ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) SHARED_MEMORY_LIBS = -lrt endif @@ -468,6 +497,14 @@ ifneq ($(WINDOWS_ICON_ID),) BUILD_CXX_FLAGS += -DDGL_WINDOWS_ICON_ID=$(WINDOWS_ICON_ID) endif +ifeq ($(USE_GLES2),true) +BUILD_CXX_FLAGS += -DDGL_USE_GLES -DDGL_USE_GLES2 +endif + +ifeq ($(USE_GLES3),true) +BUILD_CXX_FLAGS += -DDGL_USE_GLES -DDGL_USE_GLES3 +endif + ifeq ($(USE_OPENGL3),true) BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 endif @@ -487,21 +524,21 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Set app extension -ifeq ($(WINDOWS),true) +ifeq ($(WASM),true) +APP_EXT = .html +else ifeq ($(WINDOWS),true) APP_EXT = .exe endif # --------------------------------------------------------------------------------------------------------------------- # Set shared lib extension -LIB_EXT = .so - ifeq ($(MACOS),true) LIB_EXT = .dylib -endif - -ifeq ($(WINDOWS),true) +else ifeq ($(WINDOWS),true) LIB_EXT = .dll +else +LIB_EXT = .so endif # --------------------------------------------------------------------------------------------------------------------- @@ -550,9 +587,12 @@ features: $(call print_available,HURD) $(call print_available,LINUX) $(call print_available,MACOS) + $(call print_available,WASM) $(call print_available,WINDOWS) + $(call print_available,HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS) $(call print_available,HAIKU_OR_MACOS_OR_WINDOWS) $(call print_available,LINUX_OR_MACOS) + $(call print_available,MACOS_OR_WASM_OR_WINDOWS) $(call print_available,MACOS_OR_WINDOWS) $(call print_available,UNIX) @echo === Detected features diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 0aeea911..147b2068 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -51,7 +51,7 @@ BASE_FLAGS += -DHAVE_PULSEAUDIO endif # always needed -ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true) +ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) ifneq ($(STATIC_BUILD),true) LINK_FLAGS += -ldl endif @@ -162,6 +162,15 @@ 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_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp +else ifeq ($(WASM),true) +SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" +SYMBOLS_DSSI = -sEXPORTED_FUNCTIONS="['ladspa_descriptor','dssi_descriptor']" +SYMBOLS_LV2DSP = -sEXPORTED_FUNCTIONS="['lv2_descriptor']" +SYMBOLS_LV2UI = -sEXPORTED_FUNCTIONS="['lv2ui_descriptor']" +SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2ui_descriptor']" +SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']" +SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']" +SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" else ifeq ($(WINDOWS),true) SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index 5d6529a9..0ec4de82 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -16,10 +16,21 @@ #include "ApplicationPrivateData.hpp" +#ifdef __EMSCRIPTEN__ +# include +#endif + START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- +#ifdef __EMSCRIPTEN__ +static void app_idle(void* const app) +{ + static_cast(app)->idle(); +} +#endif + Application::Application(const bool isStandalone) : pData(new PrivateData(isStandalone)) {} @@ -37,8 +48,12 @@ void Application::exec(const uint idleTimeInMs) { DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop_arg(app_idle, this, 0, true); +#else while (! pData->isQuitting) pData->idle(idleTimeInMs); +#endif } void Application::quit() diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 590ae3b5..7bf48836 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -60,7 +60,9 @@ Application::PrivateData::PrivateData(const bool standalone) DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); puglSetWorldHandle(world, this); +#ifndef __EMSCRIPTEN__ puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); +#endif } Application::PrivateData::~PrivateData() diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index cb78fade..15c1b6a2 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -82,7 +82,9 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) // Include NanoVG OpenGL implementation //#define STB_IMAGE_STATIC -#ifdef DGL_USE_OPENGL3 +#if defined(DGL_USE_GLES2) +# define NANOVG_GLES2_IMPLEMENTATION +#elif defined(DGL_USE_OPENGL3) # define NANOVG_GL3_IMPLEMENTATION #else # define NANOVG_GL2_IMPLEMENTATION @@ -314,7 +316,10 @@ NanoVG::Paint::operator NVGpaint() const noexcept NanoVG::NanoVG(int flags) : fContext(nvgCreateGL(flags)), fInFrame(false), - fIsSubWidget(false) {} + fIsSubWidget(false) +{ + DISTRHO_SAFE_ASSERT(fContext); +} NanoVG::~NanoVG() { diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 0992cc32..b7426370 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -35,11 +35,23 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- -#ifdef DGL_USE_OPENGL3 +#if defined(DGL_USE_GLES2) +static void notImplemented(const char* const name) +{ +// d_stderr2("GLES2 function not implemented: %s", name); +} +#elif defined(DGL_USE_GLES3) +static void notImplemented(const char* const name) +{ + d_stderr2("GLES3 function not implemented: %s", name); +} +#elif defined(DGL_USE_OPENGL3) static void notImplemented(const char* const name) { d_stderr2("OpenGL3 function not implemented: %s", name); } +#else +# define DGL_USE_COMPAT_OPENGL #endif // ----------------------------------------------------------------------- @@ -47,7 +59,7 @@ static void notImplemented(const char* const name) void Color::setFor(const GraphicsContext&, const bool includeAlpha) { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL if (includeAlpha) glColor4f(red, green, blue, alpha); else @@ -62,7 +74,7 @@ void Color::setFor(const GraphicsContext&, const bool includeAlpha) // ----------------------------------------------------------------------- // Line -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL template static void drawLine(const Point& posStart, const Point& posEnd) { @@ -82,7 +94,7 @@ static void drawLine(const Point& posStart, const Point& posEnd) template void Line::draw(const GraphicsContext&, const T width) { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL DISTRHO_SAFE_ASSERT_RETURN(width != 0,); glLineWidth(static_cast(width)); @@ -96,7 +108,7 @@ void Line::draw(const GraphicsContext&, const T width) template void Line::draw() { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawLine(posStart, posEnd); #else notImplemented("Line::draw"); @@ -113,7 +125,7 @@ template class Line; // ----------------------------------------------------------------------- // Circle -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL template static void drawCircle(const Point& pos, const uint numSegments, @@ -146,7 +158,7 @@ static void drawCircle(const Point& pos, template void Circle::draw(const GraphicsContext&) { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); #else notImplemented("Circle::draw"); @@ -159,7 +171,7 @@ void Circle::drawOutline(const GraphicsContext&, const T lineWidth) DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); #else notImplemented("Circle::drawOutline"); @@ -170,7 +182,7 @@ void Circle::drawOutline(const GraphicsContext&, const T lineWidth) template void Circle::draw() { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); #else notImplemented("Circle::draw"); @@ -180,7 +192,7 @@ void Circle::draw() template void Circle::drawOutline() { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); #else notImplemented("Circle::drawOutline"); @@ -197,7 +209,7 @@ template class Circle; // ----------------------------------------------------------------------- // Triangle -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL template static void drawTriangle(const Point& pos1, const Point& pos2, @@ -221,7 +233,7 @@ static void drawTriangle(const Point& pos1, template void Triangle::draw(const GraphicsContext&) { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawTriangle(pos1, pos2, pos3, false); #else notImplemented("Triangle::draw"); @@ -234,7 +246,7 @@ void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawTriangle(pos1, pos2, pos3, true); #else notImplemented("Triangle::drawOutline"); @@ -245,7 +257,7 @@ void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) template void Triangle::draw() { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawTriangle(pos1, pos2, pos3, false); #else notImplemented("Triangle::draw"); @@ -255,7 +267,7 @@ void Triangle::draw() template void Triangle::drawOutline() { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawTriangle(pos1, pos2, pos3, true); #else notImplemented("Triangle::drawOutline"); @@ -272,7 +284,7 @@ template class Triangle; // ----------------------------------------------------------------------- // Rectangle -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL template static void drawRectangle(const Rectangle& rect, const bool outline) { @@ -306,7 +318,7 @@ static void drawRectangle(const Rectangle& rect, const bool outline) template void Rectangle::draw(const GraphicsContext&) { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawRectangle(*this, false); #else notImplemented("Rectangle::draw"); @@ -319,7 +331,7 @@ void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawRectangle(*this, true); #else notImplemented("Rectangle::drawOutline"); @@ -330,7 +342,7 @@ void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) template void Rectangle::draw() { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawRectangle(*this, false); #else notImplemented("Rectangle::draw"); @@ -340,7 +352,7 @@ void Rectangle::draw() template void Rectangle::drawOutline() { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL drawRectangle(*this, true); #else notImplemented("Rectangle::drawOutline"); @@ -395,14 +407,14 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point& pos, con setupCalled = true; } -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL glColor4f(1.0f, 1.0f, 1.0f, 1.0f); #endif glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, textureId); -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL glBegin(GL_QUADS); { @@ -616,21 +628,21 @@ void ImageBaseKnob::onDisplay() if (pData->rotationAngle != 0) { -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL glPushMatrix(); #endif const int w2 = w/2; const int h2 = h/2; -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL glTranslatef(static_cast(w2), static_cast(h2), 0.0f); glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); #endif Rectangle(-w2, -h2, w, h).draw(context); -#ifndef DGL_USE_OPENGL3 +#ifdef DGL_USE_COMPAT_OPENGL glPopMatrix(); #endif } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 963bf83f..92d90fc7 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -253,9 +253,6 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo puglSetMatchingBackendForCurrentBuild(view); puglSetHandle(view, this); - // FIXME? - // puglClearMinSize(view); - puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); #if DGL_USE_RGBA diff --git a/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index dc541ff2..5894ba42 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/dgl/src/nanovg/nanovg_gl.h @@ -151,6 +151,9 @@ struct GLNVGtexture { int width, height; int type; int flags; +#if defined NANOVG_GLES2 + unsigned char* data; +#endif }; typedef struct GLNVGtexture GLNVGtexture; @@ -399,7 +402,10 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id) for (i = 0; i < gl->textureContext->ntextures; i++) { if (gl->textureContext->textures[i].id == id) { if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) + { glDeleteTextures(1, &gl->textureContext->textures[i].tex); + free(gl->textureContext->textures[i].data); + } memset(&gl->textureContext->textures[i], 0, sizeof(gl->textureContext->textures[i])); return 1; } @@ -753,7 +759,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im } // No mips. if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); + printf("Mip-maps is not supported for non power-of-two textures (%d x %d)\n", w, h); imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; } } @@ -783,10 +789,37 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im switch (type) { case NVG_TEXTURE_BGR: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data); +#if NANOVG_GLES2 + // GLES2 cannot handle GL_BGR, do local conversion to GL_RGB + tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 3 * w * h); + for (uint32_t i=0; idata[i*3+0] = data[i*3+2]; + tex->data[i*3+1] = data[i*3+1]; + tex->data[i*3+2] = data[i*3+0]; + } + data = tex->data; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data); +#endif break; case NVG_TEXTURE_BGRA: +#if NANOVG_GLES2 + // GLES2 cannot handle GL_BGRA, do local conversion to GL_RGBA + tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 4 * w * h); + for (uint32_t i=0; idata[i*3+0] = data[i*3+3]; + tex->data[i*3+1] = data[i*3+2]; + tex->data[i*3+2] = data[i*3+1]; + tex->data[i*3+3] = data[i*3+0]; + } + data = tex->data; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); +#endif break; case NVG_TEXTURE_RGB: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 6731c86f..0ea278e7 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 6731c86f3f17712c396316321fffcb415593ff69 +Subproject commit 0ea278e7c911fe83805e58e0d0b002c952e909b1 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 3f63905a..da7f498c 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -51,6 +51,12 @@ # import # include # endif +#elif defined(DISTRHO_OS_WASM) +# include +# include +# ifdef DGL_OPENGL +# include +# endif #elif defined(DISTRHO_OS_WINDOWS) # include # include @@ -66,7 +72,7 @@ # include # include # endif -#else +#elif defined(HAVE_X11) # include # include # include @@ -141,6 +147,12 @@ START_NAMESPACE_DGL # import "pugl-upstream/src/mac_vulkan.m" # endif # pragma clang diagnostic pop +#elif defined(DISTRHO_OS_WASM) +# include "pugl-upstream/src/wasm.c" +# include "pugl-upstream/src/wasm_stub.c" +# ifdef DGL_OPENGL +# include "pugl-upstream/src/wasm_gl.c" +# endif #elif defined(DISTRHO_OS_WINDOWS) # include "pugl-upstream/src/win.c" # include "pugl-upstream/src/win_stub.c" @@ -153,7 +165,7 @@ START_NAMESPACE_DGL # ifdef DGL_VULKAN # include "pugl-upstream/src/win_vulkan.c" # endif -#else +#elif defined(HAVE_X11) # include "pugl-upstream/src/x11.c" # include "pugl-upstream/src/x11_stub.c" # ifdef DGL_CAIRO @@ -204,15 +216,6 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) puglSetBackend(view, puglStubBackend()); } -// -------------------------------------------------------------------------------------------------------------------- -// clear minimum size to 0 - -// void puglClearMinSize(PuglView* const view) -// { -// view->sizeHints[PUGL_MIN_SIZE].width = 0; -// view->sizeHints[PUGL_MIN_SIZE].height = 0; -// } - // -------------------------------------------------------------------------------------------------------------------- // bring view window into the foreground, aka "raise" window @@ -225,7 +228,7 @@ void puglRaiseWindow(PuglView* const view) #elif defined(DISTRHO_OS_WINDOWS) SetForegroundWindow(view->impl->hwnd); SetActiveWindow(view->impl->hwnd); -#else +#elif defined(HAVE_X11) XRaiseWindow(view->world->impl->display, view->impl->win); #endif } @@ -288,7 +291,7 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co } #elif defined(DISTRHO_OS_WINDOWS) // nothing -#else +#elif defined(HAVE_X11) if (const PuglStatus status = updateSizeHints(view)) return status; @@ -320,7 +323,7 @@ void puglSetResizable(PuglView* const view, const bool resizable) : GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX); SetWindowLong(hwnd, GWL_STYLE, winFlags); } -#else +#elif defined(HAVE_X11) updateSizeHints(view); #endif } @@ -355,6 +358,9 @@ PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) const NSSize sizePt = [impl->drawView convertSizeFromBacking:sizePx]; [impl->wrapperView setFrameSize:sizePt]; [impl->drawView setFrameSize:sizePt]; +#elif defined(DISTRHO_OS_WASM) + d_stdout("className is %s", view->world->className); + emscripten_set_canvas_element_size(view->world->className, width, height); #elif defined(DISTRHO_OS_WINDOWS) // matches upstream pugl, except we re-enter context after resize if (const HWND hwnd = view->impl->hwnd) @@ -369,7 +375,7 @@ PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) // make sure to return context back to ourselves puglBackendEnter(view); } -#else +#elif defined(HAVE_X11) // matches upstream pugl, all in one if (const Window window = view->impl->win) { @@ -395,7 +401,9 @@ void puglOnDisplayPrepare(PuglView*) { #ifdef DGL_OPENGL glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +# ifndef DGL_USE_GLES glLoadIdentity(); +# endif #endif } @@ -407,12 +415,16 @@ void puglFallbackOnResize(PuglView* const view) #ifdef DGL_OPENGL glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +# ifndef DGL_USE_GLES glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, static_cast(view->frame.width), static_cast(view->frame.height), 0.0, 0.0, 1.0); glViewport(0, 0, static_cast(view->frame.width), static_cast(view->frame.height)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); +# else + glViewport(0, 0, static_cast(view->frame.width), static_cast(view->frame.height)); +# endif #else return; // unused diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 1241e196..23b62e54 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -52,9 +52,6 @@ bool puglBackendLeave(PuglView* view); // DGL specific, assigns backend that matches current DGL build void puglSetMatchingBackendForCurrentBuild(PuglView* view); -// clear minimum size to 0 -void puglClearMinSize(PuglView* view); - // bring view window into the foreground, aka "raise" window void puglRaiseWindow(PuglView* view); diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h index 94c6a2a4..e42eec2c 100644 --- a/distrho/src/DistrhoDefines.h +++ b/distrho/src/DistrhoDefines.h @@ -45,6 +45,8 @@ # define DISTRHO_OS_BSD 1 # elif defined(__GNU__) # define DISTRHO_OS_GNU_HURD 1 +# elif defined(__EMSCRIPTEN__) +# define DISTRHO_OS_WASM 1 # endif #endif diff --git a/pugl-updates-notes.txt b/pugl-updates-notes.txt deleted file mode 100644 index 254e1589..00000000 --- a/pugl-updates-notes.txt +++ /dev/null @@ -1,3 +0,0 @@ - -puglClearMinSize needed? -puglSetWindowSize was used on first show, still needed? diff --git a/tests/Makefile b/tests/Makefile index 9b652ea5..8ddd754f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -22,17 +22,25 @@ endif # --------------------------------------------------------------------------------------------------------------------- MANUAL_TESTS = -UNIT_TESTS = Application Color Point +UNIT_TESTS = Color Point ifeq ($(HAVE_CAIRO),true) MANUAL_TESTS += Demo.cairo -UNIT_TESTS += Window.cairo endif + ifeq ($(HAVE_OPENGL),true) MANUAL_TESTS += Demo.opengl MANUAL_TESTS += FileBrowserDialog MANUAL_TESTS += NanoImage MANUAL_TESTS += NanoSubWidgets +endif + +ifneq ($(WASM),true) +UNIT_TESTS += Application +ifeq ($(HAVE_CAIRO),true) +UNIT_TESTS += Window.cairo +endif +ifeq ($(HAVE_OPENGL),true) UNIT_TESTS += Window.opengl endif ifeq ($(HAVE_STUB),true) @@ -41,6 +49,7 @@ endif ifeq ($(HAVE_VULKAN),true) UNIT_TESTS += Window.vulkan endif +endif MANUAL_TARGETS = $(MANUAL_TESTS:%=../build/tests/%$(APP_EXT)) UNIT_TARGET = $(UNIT_TESTS:%=../build/tests/%$(APP_EXT)) @@ -54,6 +63,13 @@ all: $(MANUAL_TARGETS) $(UNIT_TARGET) # --------------------------------------------------------------------------------------------------------------------- +Demo.opengl: ../build/tests/Demo.opengl$(APP_EXT) +FileBrowserDialog: ../build/tests/FileBrowserDialog$(APP_EXT) +NanoImage: ../build/tests/NanoImage$(APP_EXT) +NanoSubWidgets: ../build/tests/NanoSubWidgets$(APP_EXT) + +# --------------------------------------------------------------------------------------------------------------------- + define RUN_TEST ${1} @@ -154,6 +170,8 @@ clean: # --------------------------------------------------------------------------------------------------------------------- +.PHONY: Demo.opengl FileBrowserDialog NanoImage NanoSubWidgets + -include $(ALL_OBJS:%.o=%.d) # --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/NanoImage.cpp b/tests/NanoImage.cpp index 9b719d1f..bd6bbb40 100644 --- a/tests/NanoImage.cpp +++ b/tests/NanoImage.cpp @@ -38,8 +38,6 @@ class NanoImageExample : public NanoStandaloneWindow, 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; @@ -57,9 +55,9 @@ public: 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)) + img1(createImageFromRawMemory(CatPics::cat1Width, CatPics::cat1Height, (uchar*)CatPics::cat1Data, 0, kImageFormatBGR)), + img2(createImageFromRawMemory(CatPics::cat2Width, CatPics::cat2Height, (uchar*)CatPics::cat2Data, 0, kImageFormatBGR)), + img3(createImageFromRawMemory(CatPics::cat3Width, CatPics::cat3Height, (uchar*)CatPics::cat3Data, 0, kImageFormatBGR)) { DISTRHO_SAFE_ASSERT(img1.isValid()); DISTRHO_SAFE_ASSERT(img2.isValid()); diff --git a/tests/widgets/ResizeHandle.hpp b/tests/widgets/ResizeHandle.hpp index 99a164a1..a4067d15 100644 --- a/tests/widgets/ResizeHandle.hpp +++ b/tests/widgets/ResizeHandle.hpp @@ -62,7 +62,7 @@ protected: const double lineWidth = 1.0 * getScaleFactor(); #if defined(DGL_OPENGL) && !defined(DGL_USE_OPENGL3) - glMatrixMode(GL_MODELVIEW); +// glMatrixMode(GL_MODELVIEW); #endif // draw white lines, 1px wide From a9951390e653928673ab36f979bcd532c9aa6434 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 3 Jul 2022 19:32:53 +0100 Subject: [PATCH 425/504] Cleanup jackbridge, use SDL for wasm Signed-off-by: falkTX --- distrho/src/jackbridge/JackBridge.cpp | 276 ++++++++++++++--------- distrho/src/jackbridge/JackBridge.hpp | 19 +- distrho/src/jackbridge/RtAudioBridge.hpp | 8 +- distrho/src/jackbridge/SDLBridge.hpp | 216 ++++++++++++++++++ 4 files changed, 393 insertions(+), 126 deletions(-) create mode 100644 distrho/src/jackbridge/SDLBridge.hpp diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 02adf0f4..8268b8fc 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -1,6 +1,6 @@ /* * JackBridge for DPF - * Copyright (C) 2013-2021 Filipe Coelho + * Copyright (C) 2013-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -35,8 +35,10 @@ #include #include "../../extra/LibraryUtils.hpp" -// in case JACK fails, we fallback to RtAudio's native API -#if defined(DISTRHO_PROPER_CPP11_SUPPORT) && !defined(DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK) +// in case JACK fails, we fallback to RtAudio or SDL native API +#if defined(DISTRHO_OS_WASM) +# include "SDLBridge.hpp" +#elif defined(DISTRHO_PROPER_CPP11_SUPPORT) && !defined(DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK) # include "RtAudioBridge.hpp" # ifdef RTAUDIO_API_TYPE # include "rtaudio/RtAudio.cpp" @@ -312,7 +314,10 @@ struct JackBridge { jacksym_set_thread_creator set_thread_creator_ptr; #endif - static bool usingRtAudio; + static bool usingRtAudioOrSDL; +#ifdef DISTRHO_OS_WASM + static SDLBridge sdl; +#endif #ifdef RTAUDIO_API_TYPE static RtAudioBridge rtAudio; #endif @@ -415,6 +420,9 @@ struct JackBridge { , set_thread_creator_ptr(nullptr) #endif { +# ifdef DISTRHO_OS_WASM + return; +# endif # if defined(DISTRHO_OS_MAC) const char* const filename("libjack.dylib"); # elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64) @@ -576,7 +584,10 @@ struct JackBridge { DISTRHO_DECLARE_NON_COPYABLE(JackBridge); }; -bool JackBridge::usingRtAudio = false; +bool JackBridge::usingRtAudioOrSDL = false; +#ifdef DISTRHO_OS_WASM +SDLBridge JackBridge::sdl; +#endif #ifdef RTAUDIO_API_TYPE RtAudioBridge JackBridge::rtAudio; #endif @@ -808,7 +819,7 @@ bool jackbridge_is_ok() noexcept { #if defined(JACKBRIDGE_DUMMY) return false; -#elif defined(JACKBRIDGE_DIRECT) || defined(RTAUDIO_API_TYPE) +#elif defined(JACKBRIDGE_DIRECT) || defined(DISTRHO_OS_WASM) || defined(RTAUDIO_API_TYPE) return true; #else return (getBridgeInstance().lib != nullptr); @@ -817,7 +828,7 @@ bool jackbridge_is_ok() noexcept void jackbridge_init() { -#if defined(__WINE__) && ! defined(JACKBRIDGE_DIRECT) +#if defined(__WINE__) && !defined(JACKBRIDGE_DIRECT) if (getBridgeInstance().set_thread_creator_ptr != nullptr) getBridgeInstance().set_thread_creator_ptr(WineBridge::thread_creator); #endif @@ -831,7 +842,7 @@ void jackbridge_get_version(int* major_ptr, int* minor_ptr, int* micro_ptr, int* #elif defined(JACKBRIDGE_DIRECT) return jack_get_version(major_ptr, minor_ptr, micro_ptr, proto_ptr); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_version_ptr != nullptr) return getBridgeInstance().get_version_ptr(major_ptr, minor_ptr, micro_ptr, proto_ptr); #endif @@ -851,8 +862,12 @@ const char* jackbridge_get_version_string() #elif defined(JACKBRIDGE_DIRECT) return jack_get_version_string(); #else +# ifdef DISTRHO_OS_WASM + if (JackBridge::usingRtAudioOrSDL) + return "2"; // SDL_VERSION; +# endif # ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) + if (JackBridge::usingRtAudioOrSDL) return RTAUDIO_VERSION; # endif if (getBridgeInstance().get_version_string_ptr != nullptr) @@ -869,14 +884,27 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options, #elif defined(JACKBRIDGE_DIRECT) return jack_client_open(client_name, static_cast(options), status); #else +# ifdef DISTRHO_OS_WASM + if (JackBridge::sdl.open(client_name)) + { + d_stdout("SDL audio setup ok"); + JackBridge::usingRtAudioOrSDL = true; + return (jack_client_t*)0x1; // return non-null + // unused + (void)options; + } + d_stderr2("SDL audio setup failed"); +# else if (getBridgeInstance().client_open_ptr != nullptr) if (jack_client_t* const client = getBridgeInstance().client_open_ptr(client_name, static_cast(options), status)) return client; + // TODO +# endif # ifdef RTAUDIO_API_TYPE if (JackBridge::rtAudio.open()) { d_stdout("JACK setup failed, using RtAudio instead"); - JackBridge::usingRtAudio = true; + JackBridge::usingRtAudioOrSDL = true; return (jack_client_t*)0x1; // return non-null } # endif @@ -892,13 +920,16 @@ bool jackbridge_client_close(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_client_close(client) == 0); #else -# ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) + if (JackBridge::usingRtAudioOrSDL) { - JackBridge::usingRtAudio = false; + JackBridge::usingRtAudioOrSDL = false; +# ifdef DISTRHO_OS_WASM + return JackBridge::sdl.close(); +# endif +# ifdef RTAUDIO_API_TYPE return JackBridge::rtAudio.close(); - } # endif + } if (getBridgeInstance().client_close_ptr != nullptr) return (getBridgeInstance().client_close_ptr(client) == 0); #endif @@ -913,22 +944,22 @@ int jackbridge_client_name_size() #elif defined(JACKBRIDGE_DIRECT) return jack_client_name_size(); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().client_name_size_ptr != nullptr) return getBridgeInstance().client_name_size_ptr(); #endif return 33; } -char* jackbridge_get_client_name(jack_client_t* client) +const char* jackbridge_get_client_name(jack_client_t* client) { #if defined(JACKBRIDGE_DUMMY) #elif defined(JACKBRIDGE_DIRECT) return jack_get_client_name(client); #else - if (JackBridge::usingRtAudio) + if (JackBridge::usingRtAudioOrSDL) { - char* const name = (char*)DISTRHO_PLUGIN_NAME; + static const char* const name = DISTRHO_PLUGIN_NAME; return name; } if (getBridgeInstance().get_client_name_ptr != nullptr) @@ -945,7 +976,7 @@ char* jackbridge_client_get_uuid(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_client_get_uuid(client); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (const jacksym_client_get_uuid func = getBridgeInstance().client_get_uuid_ptr) return func(client); #endif @@ -958,7 +989,7 @@ char* jackbridge_get_uuid_for_client_name(jack_client_t* client, const char* nam #elif defined(JACKBRIDGE_DIRECT) return jack_get_uuid_for_client_name(client, name); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_uuid_for_client_name_ptr != nullptr) return getBridgeInstance().get_uuid_for_client_name_ptr(client, name); #endif @@ -971,7 +1002,7 @@ char* jackbridge_get_client_name_by_uuid(jack_client_t* client, const char* uuid #elif defined(JACKBRIDGE_DIRECT) return jack_get_client_name_by_uuid(client, uuid); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_client_name_by_uuid_ptr != nullptr) return getBridgeInstance().get_client_name_by_uuid_ptr(client, uuid); #endif @@ -986,7 +1017,7 @@ bool jackbridge_uuid_parse(const char* buf, jack_uuid_t* uuid) #elif defined(JACKBRIDGE_DIRECT) return (jack_uuid_parse(buf, uuid) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (const jacksym_uuid_parse func = getBridgeInstance().uuid_parse_ptr) return (func(buf, uuid) == 0); #endif @@ -999,7 +1030,7 @@ void jackbridge_uuid_unparse(jack_uuid_t uuid, char buf[JACK_UUID_STRING_SIZE]) #elif defined(JACKBRIDGE_DIRECT) jack_uuid_unparse(uuid, buf); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (const jacksym_uuid_unparse func = getBridgeInstance().uuid_unparse_ptr) return func(uuid, buf); #endif @@ -1013,10 +1044,15 @@ bool jackbridge_activate(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_activate(client) == 0); #else + if (JackBridge::usingRtAudioOrSDL) + { +# ifdef DISTRHO_OS_WASM + return JackBridge::sdl.activate(); +# endif # ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) return JackBridge::rtAudio.activate(); # endif + } if (getBridgeInstance().activate_ptr != nullptr) return (getBridgeInstance().activate_ptr(client) == 0); #endif @@ -1029,10 +1065,15 @@ bool jackbridge_deactivate(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_deactivate(client) == 0); #else + if (JackBridge::usingRtAudioOrSDL) + { +# ifdef DISTRHO_OS_WASM + return JackBridge::sdl.deactivate(); +# endif # ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) return JackBridge::rtAudio.deactivate(); # endif + } if (getBridgeInstance().deactivate_ptr != nullptr) return (getBridgeInstance().deactivate_ptr(client) == 0); #endif @@ -1045,7 +1086,7 @@ bool jackbridge_is_realtime(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_is_realtime(client); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().is_realtime_ptr != nullptr) return getBridgeInstance().is_realtime_ptr(client); #endif @@ -1060,7 +1101,7 @@ bool jackbridge_set_thread_init_callback(jack_client_t* client, JackThreadInitCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_thread_init_callback(client, thread_init_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_thread_init_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_thread_init_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_thread_init(thread_init_callback); @@ -1079,7 +1120,7 @@ void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown #elif defined(JACKBRIDGE_DIRECT) jack_on_shutdown(client, shutdown_callback, arg); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().on_shutdown_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().on_shutdown_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_shutdown(shutdown_callback); @@ -1097,7 +1138,7 @@ void jackbridge_on_info_shutdown(jack_client_t* client, JackInfoShutdownCallback #elif defined(JACKBRIDGE_DIRECT) jack_on_info_shutdown(client, shutdown_callback, arg); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().on_info_shutdown_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().on_info_shutdown_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_info_shutdown(shutdown_callback); @@ -1115,14 +1156,19 @@ bool jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback #elif defined(JACKBRIDGE_DIRECT) return (jack_set_process_callback(client, process_callback, arg) == 0); #else -# ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) + if (JackBridge::usingRtAudioOrSDL) { +# ifdef DISTRHO_OS_WASM + JackBridge::sdl.jackProcessCallback = process_callback; + JackBridge::sdl.jackProcessArg = arg; + return true; +# endif +# ifdef RTAUDIO_API_TYPE JackBridge::rtAudio.jackProcessCallback = process_callback; JackBridge::rtAudio.jackProcessArg = arg; return true; - } # endif + } if (getBridgeInstance().set_process_callback_ptr != nullptr) { # ifdef __WINE__ @@ -1142,7 +1188,7 @@ bool jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallb #elif defined(JACKBRIDGE_DIRECT) return (jack_set_freewheel_callback(client, freewheel_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_freewheel_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_freewheel_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_freewheel(freewheel_callback); @@ -1161,7 +1207,7 @@ bool jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_buffer_size_callback(client, bufsize_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_buffer_size_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_buffer_size_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_bufsize(bufsize_callback); @@ -1180,7 +1226,7 @@ bool jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_sample_rate_callback(client, srate_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_sample_rate_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_sample_rate_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_srate(srate_callback); @@ -1199,7 +1245,7 @@ bool jackbridge_set_client_registration_callback(jack_client_t* client, JackClie #elif defined(JACKBRIDGE_DIRECT) return (jack_set_client_registration_callback(client, registration_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_client_registration_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_client_registration_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_client_reg(registration_callback); @@ -1218,7 +1264,7 @@ bool jackbridge_set_port_registration_callback(jack_client_t* client, JackPortRe #elif defined(JACKBRIDGE_DIRECT) return (jack_set_port_registration_callback(client, registration_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_port_registration_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_port_registration_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_port_reg(registration_callback); @@ -1237,7 +1283,7 @@ bool jackbridge_set_port_rename_callback(jack_client_t* client, JackPortRenameCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_port_rename_callback(client, rename_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_port_rename_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_port_rename_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_port_rename(rename_callback); @@ -1256,7 +1302,7 @@ bool jackbridge_set_port_connect_callback(jack_client_t* client, JackPortConnect #elif defined(JACKBRIDGE_DIRECT) return (jack_set_port_connect_callback(client, connect_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_port_connect_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_port_connect_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_port_conn(connect_callback); @@ -1275,7 +1321,7 @@ bool jackbridge_set_graph_order_callback(jack_client_t* client, JackGraphOrderCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_graph_order_callback(client, graph_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_graph_order_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_graph_order_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_graph_order(graph_callback); @@ -1294,7 +1340,7 @@ bool jackbridge_set_xrun_callback(jack_client_t* client, JackXRunCallback xrun_c #elif defined(JACKBRIDGE_DIRECT) return (jack_set_xrun_callback(client, xrun_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_xrun_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_xrun_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_xrun(xrun_callback); @@ -1313,7 +1359,7 @@ bool jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback #elif defined(JACKBRIDGE_DIRECT) return (jack_set_latency_callback(client, latency_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_latency_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_latency_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_latency(latency_callback); @@ -1334,7 +1380,7 @@ bool jackbridge_set_freewheel(jack_client_t* client, bool onoff) #elif defined(JACKBRIDGE_DIRECT) return jack_set_freewheel(client, onoff); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().set_freewheel_ptr != nullptr) return getBridgeInstance().set_freewheel_ptr(client, onoff); #endif @@ -1347,7 +1393,7 @@ bool jackbridge_set_buffer_size(jack_client_t* client, jack_nframes_t nframes) #elif defined(JACKBRIDGE_DIRECT) return jack_set_buffer_size(client, nframes); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().set_buffer_size_ptr != nullptr) return getBridgeInstance().set_buffer_size_ptr(client, nframes); #endif @@ -1362,10 +1408,15 @@ jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_get_sample_rate(client); #else + if (JackBridge::usingRtAudioOrSDL) + { +# ifdef DISTRHO_OS_WASM + return JackBridge::sdl.sampleRate; +# endif # ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) return JackBridge::rtAudio.sampleRate; # endif + } if (getBridgeInstance().get_sample_rate_ptr != nullptr) return getBridgeInstance().get_sample_rate_ptr(client); #endif @@ -1378,10 +1429,15 @@ jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_get_buffer_size(client); #else + if (JackBridge::usingRtAudioOrSDL) + { +# ifdef DISTRHO_OS_WASM + return JackBridge::sdl.bufferSize; +# endif # ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) return JackBridge::rtAudio.bufferSize; # endif + } if (getBridgeInstance().get_buffer_size_ptr != nullptr) return getBridgeInstance().get_buffer_size_ptr(client); #endif @@ -1394,7 +1450,7 @@ float jackbridge_cpu_load(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_cpu_load(client); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().cpu_load_ptr != nullptr) return getBridgeInstance().cpu_load_ptr(client); #endif @@ -1409,10 +1465,15 @@ jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_na #elif defined(JACKBRIDGE_DIRECT) return jack_port_register(client, port_name, type, flags, buffer_size); #else + if (JackBridge::usingRtAudioOrSDL) + { +# ifdef DISTRHO_OS_WASM + return JackBridge::sdl.registerPort(type, flags); +# endif # ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) return JackBridge::rtAudio.registerPort(type, flags); # endif + } if (getBridgeInstance().port_register_ptr != nullptr) return getBridgeInstance().port_register_ptr(client, port_name, type, static_cast(flags), @@ -1427,7 +1488,7 @@ bool jackbridge_port_unregister(jack_client_t* client, jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_unregister(client, port) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_unregister_ptr != nullptr) return (getBridgeInstance().port_unregister_ptr(client, port) == 0); #endif @@ -1440,10 +1501,15 @@ void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes) #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_buffer(port, nframes); #else + if (JackBridge::usingRtAudioOrSDL) + { +# ifdef DISTRHO_OS_WASM + return JackBridge::sdl.getPortBuffer(port); +# endif # ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudio) return JackBridge::rtAudio.getPortBuffer(port); # endif + } if (getBridgeInstance().port_get_buffer_ptr != nullptr) return getBridgeInstance().port_get_buffer_ptr(port, nframes); #endif @@ -1458,7 +1524,7 @@ const char* jackbridge_port_name(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_name(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_name_ptr != nullptr) return getBridgeInstance().port_name_ptr(port); #endif @@ -1471,7 +1537,7 @@ jack_uuid_t jackbridge_port_uuid(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_uuid(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_uuid_ptr != nullptr) return getBridgeInstance().port_uuid_ptr(port); #endif @@ -1484,7 +1550,7 @@ const char* jackbridge_port_short_name(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_short_name(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_short_name_ptr != nullptr) return getBridgeInstance().port_short_name_ptr(port); #endif @@ -1497,7 +1563,7 @@ int jackbridge_port_flags(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_flags(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_flags_ptr != nullptr) return getBridgeInstance().port_flags_ptr(port); #endif @@ -1510,7 +1576,7 @@ const char* jackbridge_port_type(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_type(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_type_ptr != nullptr) return getBridgeInstance().port_type_ptr(port); #endif @@ -1523,7 +1589,7 @@ bool jackbridge_port_is_mine(const jack_client_t* client, const jack_port_t* por #elif defined(JACKBRIDGE_DIRECT) return jack_port_is_mine(client, port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_is_mine_ptr != nullptr) return getBridgeInstance().port_is_mine_ptr(client, port); #endif @@ -1536,7 +1602,7 @@ int jackbridge_port_connected(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_connected(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_connected_ptr != nullptr) return getBridgeInstance().port_connected_ptr(port); #endif @@ -1549,7 +1615,7 @@ bool jackbridge_port_connected_to(const jack_port_t* port, const char* port_name #elif defined(JACKBRIDGE_DIRECT) return jack_port_connected_to(port, port_name); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_connected_to_ptr != nullptr) return getBridgeInstance().port_connected_to_ptr(port, port_name); #endif @@ -1562,7 +1628,7 @@ const char** jackbridge_port_get_connections(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_connections(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_get_connections_ptr != nullptr) return getBridgeInstance().port_get_connections_ptr(port); #endif @@ -1575,7 +1641,7 @@ const char** jackbridge_port_get_all_connections(const jack_client_t* client, co #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_all_connections(client, port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_get_all_connections_ptr != nullptr) return getBridgeInstance().port_get_all_connections_ptr(client, port); #endif @@ -1590,7 +1656,7 @@ bool jackbridge_port_rename(jack_client_t* client, jack_port_t* port, const char #elif defined(JACKBRIDGE_DIRECT) return (jack_port_rename(client, port, port_name) == 0); #else - if (JackBridge::usingRtAudio) + if (JackBridge::usingRtAudioOrSDL) return false; // Try new API first if (getBridgeInstance().port_rename_ptr != nullptr) @@ -1608,7 +1674,7 @@ bool jackbridge_port_set_alias(jack_port_t* port, const char* alias) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_set_alias(port, alias) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_set_alias_ptr != nullptr) return (getBridgeInstance().port_set_alias_ptr(port, alias) == 0); #endif @@ -1621,7 +1687,7 @@ bool jackbridge_port_unset_alias(jack_port_t* port, const char* alias) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_unset_alias(port, alias) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_unset_alias_ptr != nullptr) return (getBridgeInstance().port_unset_alias_ptr(port, alias) == 0); #endif @@ -1634,7 +1700,7 @@ int jackbridge_port_get_aliases(const jack_port_t* port, char* const aliases[2]) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_get_aliases(port, aliases) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_get_aliases_ptr != nullptr) return getBridgeInstance().port_get_aliases_ptr(port, aliases); #endif @@ -1649,7 +1715,7 @@ bool jackbridge_port_request_monitor(jack_port_t* port, bool onoff) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_request_monitor(port, onoff) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_request_monitor_ptr != nullptr) return (getBridgeInstance().port_request_monitor_ptr(port, onoff) == 0); #endif @@ -1662,7 +1728,7 @@ bool jackbridge_port_request_monitor_by_name(jack_client_t* client, const char* #elif defined(JACKBRIDGE_DIRECT) return (jack_port_request_monitor_by_name(client, port_name, onoff) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_request_monitor_by_name_ptr != nullptr) return (getBridgeInstance().port_request_monitor_by_name_ptr(client, port_name, onoff) == 0); #endif @@ -1675,7 +1741,7 @@ bool jackbridge_port_ensure_monitor(jack_port_t* port, bool onoff) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_ensure_monitor(port, onoff) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_ensure_monitor_ptr != nullptr) return (getBridgeInstance().port_ensure_monitor_ptr(port, onoff) == 0); #endif @@ -1688,7 +1754,7 @@ bool jackbridge_port_monitoring_input(jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_monitoring_input(port); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_monitoring_input_ptr != nullptr) return getBridgeInstance().port_monitoring_input_ptr(port); #endif @@ -1703,7 +1769,7 @@ bool jackbridge_connect(jack_client_t* client, const char* source_port, const ch #elif defined(JACKBRIDGE_DIRECT) return (jack_connect(client, source_port, destination_port) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().connect_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().connect_ptr != nullptr) { const int ret = getBridgeInstance().connect_ptr(client, source_port, destination_port); return ret == 0 || ret == EEXIST; @@ -1718,7 +1784,7 @@ bool jackbridge_disconnect(jack_client_t* client, const char* source_port, const #elif defined(JACKBRIDGE_DIRECT) return (jack_disconnect(client, source_port, destination_port) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().disconnect_ptr != nullptr) return (getBridgeInstance().disconnect_ptr(client, source_port, destination_port) == 0); #endif @@ -1731,7 +1797,7 @@ bool jackbridge_port_disconnect(jack_client_t* client, jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_disconnect(client, port) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_disconnect_ptr != nullptr) return (getBridgeInstance().port_disconnect_ptr(client, port) == 0); #endif @@ -1746,7 +1812,7 @@ int jackbridge_port_name_size() #elif defined(JACKBRIDGE_DIRECT) return jack_port_name_size(); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_name_size_ptr != nullptr) return getBridgeInstance().port_name_size_ptr(); #endif @@ -1759,7 +1825,7 @@ int jackbridge_port_type_size() #elif defined(JACKBRIDGE_DIRECT) return jack_port_type_size(); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_type_size_ptr != nullptr) return getBridgeInstance().port_type_size_ptr(); #endif @@ -1772,7 +1838,7 @@ uint32_t jackbridge_port_type_get_buffer_size(jack_client_t* client, const char* #elif defined(JACKBRIDGE_DIRECT) return static_cast(jack_port_type_get_buffer_size(client, port_type)); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_type_get_buffer_size_ptr != nullptr) return static_cast(getBridgeInstance().port_type_get_buffer_size_ptr(client, port_type)); #endif @@ -1787,7 +1853,7 @@ void jackbridge_port_get_latency_range(jack_port_t* port, uint32_t mode, jack_la #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_latency_range(port, static_cast(mode), range); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_get_latency_range_ptr != nullptr) return getBridgeInstance().port_get_latency_range_ptr(port, static_cast(mode), @@ -1803,7 +1869,7 @@ void jackbridge_port_set_latency_range(jack_port_t* port, uint32_t mode, jack_la #elif defined(JACKBRIDGE_DIRECT) jack_port_set_latency_range(port, static_cast(mode), range); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_set_latency_range_ptr != nullptr) getBridgeInstance().port_set_latency_range_ptr(port, static_cast(mode), @@ -1817,7 +1883,7 @@ bool jackbridge_recompute_total_latencies(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_recompute_total_latencies(client) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().recompute_total_latencies_ptr != nullptr) return (getBridgeInstance().recompute_total_latencies_ptr(client) == 0); #endif @@ -1832,7 +1898,7 @@ const char** jackbridge_get_ports(jack_client_t* client, const char* port_name_p #elif defined(JACKBRIDGE_DIRECT) return jack_get_ports(client, port_name_pattern, type_name_pattern, flags); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_ports_ptr != nullptr) return getBridgeInstance().get_ports_ptr(client, port_name_pattern, type_name_pattern, static_cast(flags)); @@ -1846,7 +1912,7 @@ jack_port_t* jackbridge_port_by_name(jack_client_t* client, const char* port_nam #elif defined(JACKBRIDGE_DIRECT) return jack_port_by_name(client, port_name); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_by_name_ptr != nullptr) return getBridgeInstance().port_by_name_ptr(client, port_name); #endif @@ -1859,7 +1925,7 @@ jack_port_t* jackbridge_port_by_id(jack_client_t* client, jack_port_id_t port_id #elif defined(JACKBRIDGE_DIRECT) return jack_port_by_id(client, port_id); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().port_by_id_ptr != nullptr) return getBridgeInstance().port_by_id_ptr(client, port_id); #endif @@ -1874,7 +1940,7 @@ void jackbridge_free(void* ptr) #elif defined(JACKBRIDGE_DIRECT) return jack_free(ptr); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().free_ptr != nullptr) return getBridgeInstance().free_ptr(ptr); @@ -1891,7 +1957,7 @@ uint32_t jackbridge_midi_get_event_count(void* port_buffer) #elif defined(JACKBRIDGE_DIRECT) return jack_midi_get_event_count(port_buffer); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().midi_get_event_count_ptr != nullptr) return getBridgeInstance().midi_get_event_count_ptr(port_buffer); #endif @@ -1904,7 +1970,7 @@ bool jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint #elif defined(JACKBRIDGE_DIRECT) return (jack_midi_event_get(event, port_buffer, event_index) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().midi_event_get_ptr != nullptr) return (getBridgeInstance().midi_event_get_ptr(event, port_buffer, event_index) == 0); #endif @@ -1917,7 +1983,7 @@ void jackbridge_midi_clear_buffer(void* port_buffer) #elif defined(JACKBRIDGE_DIRECT) jack_midi_clear_buffer(port_buffer); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().midi_clear_buffer_ptr != nullptr) getBridgeInstance().midi_clear_buffer_ptr(port_buffer); #endif @@ -1929,7 +1995,7 @@ bool jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const j #elif defined(JACKBRIDGE_DIRECT) return (jack_midi_event_write(port_buffer, time, data, data_size) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().midi_event_write_ptr != nullptr) return (getBridgeInstance().midi_event_write_ptr(port_buffer, time, data, data_size) == 0); #endif @@ -1942,7 +2008,7 @@ jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_ #elif defined(JACKBRIDGE_DIRECT) return jack_midi_event_reserve(port_buffer, time, data_size); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().midi_event_reserve_ptr != nullptr) return getBridgeInstance().midi_event_reserve_ptr(port_buffer, time, data_size); #endif @@ -1957,7 +2023,7 @@ bool jackbridge_release_timebase(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_release_timebase(client) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().release_timebase_ptr != nullptr) return (getBridgeInstance().release_timebase_ptr(client) == 0); #endif @@ -1970,7 +2036,7 @@ bool jackbridge_set_sync_callback(jack_client_t* client, JackSyncCallback sync_c #elif defined(JACKBRIDGE_DIRECT) return (jack_set_sync_callback(client, sync_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_sync_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_sync_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_sync(sync_callback); @@ -1989,7 +2055,7 @@ bool jackbridge_set_sync_timeout(jack_client_t* client, jack_time_t timeout) #elif defined(JACKBRIDGE_DIRECT) return (jack_set_sync_timeout(client, timeout) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().set_sync_timeout_ptr != nullptr) return (getBridgeInstance().set_sync_timeout_ptr(client, timeout) == 0); #endif @@ -2002,7 +2068,7 @@ bool jackbridge_set_timebase_callback(jack_client_t* client, bool conditional, J #elif defined(JACKBRIDGE_DIRECT) return (jack_set_timebase_callback(client, conditional, timebase_callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_timebase_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_timebase_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_timebase(timebase_callback); @@ -2021,7 +2087,7 @@ bool jackbridge_transport_locate(jack_client_t* client, jack_nframes_t frame) #elif defined(JACKBRIDGE_DIRECT) return (jack_transport_locate(client, frame) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().transport_locate_ptr != nullptr) return (getBridgeInstance().transport_locate_ptr(client, frame) == 0); #endif @@ -2034,7 +2100,7 @@ uint32_t jackbridge_transport_query(const jack_client_t* client, jack_position_t #elif defined(JACKBRIDGE_DIRECT) return jack_transport_query(client, pos); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().transport_query_ptr != nullptr) return getBridgeInstance().transport_query_ptr(client, pos); #endif @@ -2054,7 +2120,7 @@ jack_nframes_t jackbridge_get_current_transport_frame(const jack_client_t* clien #elif defined(JACKBRIDGE_DIRECT) return jack_get_current_transport_frame(client); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_current_transport_frame_ptr != nullptr) return getBridgeInstance().get_current_transport_frame_ptr(client); #endif @@ -2067,7 +2133,7 @@ bool jackbridge_transport_reposition(jack_client_t* client, const jack_position_ #elif defined(JACKBRIDGE_DIRECT) return (jack_transport_reposition(client, pos) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().transport_reposition_ptr != nullptr) return (getBridgeInstance().transport_reposition_ptr(client, pos) == 0); #endif @@ -2080,7 +2146,7 @@ void jackbridge_transport_start(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) jack_transport_start(client); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().transport_start_ptr != nullptr) getBridgeInstance().transport_start_ptr(client); #endif @@ -2092,7 +2158,7 @@ void jackbridge_transport_stop(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) jack_transport_stop(client); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().transport_stop_ptr != nullptr) getBridgeInstance().transport_stop_ptr(client); #endif @@ -2106,7 +2172,7 @@ bool jackbridge_set_property(jack_client_t* client, jack_uuid_t subject, const c #elif defined(JACKBRIDGE_DIRECT) return (jack_set_property(client, subject, key, value, type) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().set_property_ptr != nullptr) return (getBridgeInstance().set_property_ptr(client, subject, key, value, type) == 0); #endif @@ -2119,7 +2185,7 @@ bool jackbridge_get_property(jack_uuid_t subject, const char* key, char** value, #elif defined(JACKBRIDGE_DIRECT) return (jack_get_property(subject, key, value, type) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_property_ptr != nullptr) return (getBridgeInstance().get_property_ptr(subject, key, value, type) == 0); #endif @@ -2132,7 +2198,7 @@ void jackbridge_free_description(jack_description_t* desc, bool free_description #elif defined(JACKBRIDGE_DIRECT) jack_free_description(desc, free_description_itself); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().free_description_ptr != nullptr) getBridgeInstance().free_description_ptr(desc, free_description_itself); #endif @@ -2144,7 +2210,7 @@ bool jackbridge_get_properties(jack_uuid_t subject, jack_description_t* desc) #elif defined(JACKBRIDGE_DIRECT) return (jack_get_properties(subject, desc) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_properties_ptr != nullptr) return (getBridgeInstance().get_properties_ptr(subject, desc) == 0); #endif @@ -2157,7 +2223,7 @@ bool jackbridge_get_all_properties(jack_description_t** descs) #elif defined(JACKBRIDGE_DIRECT) return (jack_get_all_properties(descs) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().get_all_properties_ptr != nullptr) return (getBridgeInstance().get_all_properties_ptr(descs) == 0); #endif @@ -2170,7 +2236,7 @@ bool jackbridge_remove_property(jack_client_t* client, jack_uuid_t subject, cons #elif defined(JACKBRIDGE_DIRECT) return (jack_remove_property(client, subject, key) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().remove_property_ptr != nullptr) return (getBridgeInstance().remove_property_ptr(client, subject, key) == 0); #endif @@ -2183,7 +2249,7 @@ int jackbridge_remove_properties(jack_client_t* client, jack_uuid_t subject) #elif defined(JACKBRIDGE_DIRECT) return jack_remove_properties(client, subject); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().remove_properties_ptr != nullptr) return getBridgeInstance().remove_properties_ptr(client, subject); #endif @@ -2196,7 +2262,7 @@ bool jackbridge_remove_all_properties(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_remove_all_properties(client) == 0); #else - if (! JackBridge::usingRtAudio) + if (! JackBridge::usingRtAudioOrSDL) if (getBridgeInstance().remove_all_properties_ptr != nullptr) return (getBridgeInstance().remove_all_properties_ptr(client) == 0); #endif @@ -2209,7 +2275,7 @@ bool jackbridge_set_property_change_callback(jack_client_t* client, JackProperty #elif defined(JACKBRIDGE_DIRECT) return (jack_set_property_change_callback(client, callback, arg) == 0); #else - if (! JackBridge::usingRtAudio && getBridgeInstance().set_property_change_callback_ptr != nullptr) + if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_property_change_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_prop_change(callback); diff --git a/distrho/src/jackbridge/JackBridge.hpp b/distrho/src/jackbridge/JackBridge.hpp index f96bf16d..0788093f 100644 --- a/distrho/src/jackbridge/JackBridge.hpp +++ b/distrho/src/jackbridge/JackBridge.hpp @@ -300,8 +300,8 @@ JACKBRIDGE_API const char* jackbridge_get_version_string(); JACKBRIDGE_API jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options, jack_status_t* status); JACKBRIDGE_API bool jackbridge_client_close(jack_client_t* client); -JACKBRIDGE_API int jackbridge_client_name_size(); -JACKBRIDGE_API char* jackbridge_get_client_name(jack_client_t* client); +JACKBRIDGE_API int jackbridge_client_name_size(); +JACKBRIDGE_API const char* jackbridge_get_client_name(jack_client_t* client); JACKBRIDGE_API char* jackbridge_client_get_uuid(jack_client_t* client); JACKBRIDGE_API char* jackbridge_get_uuid_for_client_name(jack_client_t* client, const char* name); @@ -408,19 +408,4 @@ JACKBRIDGE_API int jackbridge_remove_properties(jack_client_t* client, jack_uui JACKBRIDGE_API bool jackbridge_remove_all_properties(jack_client_t* client); JACKBRIDGE_API bool jackbridge_set_property_change_callback(jack_client_t* client, JackPropertyChangeCallback callback, void* arg); -JACKBRIDGE_API bool jackbridge_sem_init(void* sem) noexcept; -JACKBRIDGE_API void jackbridge_sem_destroy(void* sem) noexcept; -JACKBRIDGE_API bool jackbridge_sem_connect(void* sem) noexcept; -JACKBRIDGE_API void jackbridge_sem_post(void* sem, bool server) noexcept; -JACKBRIDGE_API bool jackbridge_sem_timedwait(void* sem, uint msecs, bool server) noexcept; - -JACKBRIDGE_API bool jackbridge_shm_is_valid(const void* shm) noexcept; -JACKBRIDGE_API void jackbridge_shm_init(void* shm) noexcept; -JACKBRIDGE_API void jackbridge_shm_attach(void* shm, const char* name) noexcept; -JACKBRIDGE_API void jackbridge_shm_close(void* shm) noexcept; -JACKBRIDGE_API void* jackbridge_shm_map(void* shm, uint64_t size) noexcept; -JACKBRIDGE_API void jackbridge_shm_unmap(void* shm, void* ptr) noexcept; - -JACKBRIDGE_API void jackbridge_parent_deathsig(bool kill) noexcept; - #endif // JACKBRIDGE_HPP_INCLUDED diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index 78f2fdd2..6f2faa8b 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -1,6 +1,6 @@ /* * RtAudioBridge for DPF - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -167,14 +167,14 @@ struct RtAudioBridge { { bool isAudio, isInput; - if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) + /**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) isAudio = true; else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) isAudio = false; else return nullptr; - if (flags & JackPortIsInput) + /**/ if (flags & JackPortIsInput) isInput = true; else if (flags & JackPortIsOutput) isInput = false; @@ -230,7 +230,7 @@ struct RtAudioBridge { uint i = 0; # if DISTRHO_PLUGIN_NUM_INPUTS > 0 - if (float* const insPtr = (float*)inputBuffer) + if (float* const insPtr = (float*)inputBuffer) { for (uint j=0; j + * + * 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 SDLBRIDGE_HPP_INCLUDED +#define SDLBRIDGE_HPP_INCLUDED + +#include "JackBridge.hpp" +#include "../../extra/RingBuffer.hpp" + +#include + +struct SDLBridge { + SDL_AudioDeviceID deviceId = 0; + + // SDL information + uint bufferSize = 0; + uint sampleRate = 0; + + // Port caching information + uint numAudioIns = 0; + uint numAudioOuts = 0; + uint numMidiIns = 0; + uint numMidiOuts = 0; + + // JACK callbacks + JackProcessCallback jackProcessCallback = nullptr; + void* jackProcessArg = nullptr; + + // Runtime buffers + enum PortMask { + kPortMaskAudio = 0x1000, + kPortMaskMIDI = 0x2000, + kPortMaskInput = 0x4000, + kPortMaskOutput = 0x8000, + kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, + kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, + }; +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS]; + float* audioBufferStorage; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + HeapRingBuffer midiInBuffer; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + HeapRingBuffer midiOutBuffer; +#endif + + bool open(const char* const clientName) + { + SDL_AudioSpec requested, received; + std::memset(&requested, 0, sizeof(requested)); + requested.format = AUDIO_F32SYS; + requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS; + requested.freq = 48000; + requested.samples = 512; + requested.callback = SDLCallback; + requested.userdata = this; + + SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName); + // SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, ); + SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "2"); + + /* + deviceId = SDL_OpenAudioDevice("PulseAudio", 0, + &requested, &received, + SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + */ + deviceId = SDL_OpenAudio(&requested, &received) == 0 ? 1 : 0; + DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); + + if (received.channels != DISTRHO_PLUGIN_NUM_OUTPUTS) + { + SDL_CloseAudioDevice(deviceId); + deviceId = 0; + d_stderr2("Invalid or missing audio output channels"); + return false; + } + + bufferSize = received.samples; + sampleRate = received.freq; + +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS); +#endif + + for (uint i=0; i 0 + delete[] audioBufferStorage; + audioBufferStorage = nullptr; +#endif + + return true; + } + + bool activate() + { + DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); + + SDL_PauseAudioDevice(deviceId, 0); + return true; + } + + bool deactivate() + { + DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); + + SDL_PauseAudioDevice(deviceId, 1); + return true; + } + + jack_port_t* registerPort(const char* const type, const ulong flags) + { + bool isAudio, isInput; + + /**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) + isAudio = true; + else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) + isAudio = false; + else + return nullptr; + + /**/ if (flags & JackPortIsInput) + isInput = true; + else if (flags & JackPortIsOutput) + isInput = false; + else + return nullptr; + + const uintptr_t ret = (isAudio ? kPortMaskAudio : kPortMaskMIDI) + | (isInput ? kPortMaskInput : kPortMaskOutput); + + return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++) + : (isInput ? numMidiIns++ : numMidiOuts++))); + } + + void* getPortBuffer(jack_port_t* const port) + { + const uintptr_t portMask = (uintptr_t)port; + DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr); + +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + if (portMask & kPortMaskAudio) + return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)]; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if ((portMask & kPortMaskInputMIDI) == kPortMaskInputMIDI) + return &midiInBuffer; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if ((portMask & kPortMaskOutputMIDI) == kPortMaskOutputMIDI) + return &midiOutBuffer; +#endif + + return nullptr; + } + + static void SDLCallback(void* const userData, uchar* const stream, const int len) + { + SDLBridge* const self = (SDLBridge*)userData; + + // safety checks + DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(len > 0,); + + float* const fstream = (float*)stream; + + if (self->jackProcessCallback == nullptr) + { + std::memset(fstream, 0, len); + return; + } + + const uint numFrames = static_cast(static_cast(len) / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); + + self->jackProcessCallback(numFrames, self->jackProcessArg); + + for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + { + for (uint j=0; j < numFrames; ++j) + fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS+i][j]; + } + } + +}; + +#endif From d8612aa3601f308c3be81e0120bbf16772257b46 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 3 Jul 2022 19:33:49 +0100 Subject: [PATCH 426/504] More wasm related things, generating "standalones" Signed-off-by: falkTX --- Makefile.base.mk | 8 +++++--- Makefile.plugins.mk | 7 ++++++- distrho/src/DistrhoPluginJACK.cpp | 2 +- distrho/src/DistrhoUIPrivateData.hpp | 2 ++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 55dd9a9b..14c55d32 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -160,11 +160,11 @@ BASE_FLAGS = -Wall -Wextra -pipe -MD -MP BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections ifeq ($(CPU_I386_OR_X86_64),true) -BASE_OPTS += -mtune=generic -msse -msse2 +BASE_OPTS += -mtune=generic ifeq ($(WASM),true) -BASE_OPTS += -msse3 -msimd128 +# BASE_OPTS += -msse -msse2 -msse3 -msimd128 else -BASE_OPTS += -mfpmath=sse +BASE_OPTS += -msse -msse2 -mfpmath=sse endif endif @@ -546,6 +546,8 @@ endif ifeq ($(MACOS),true) SHARED = -dynamiclib +else ifeq ($(WASM),true) +SHARED = -sSIDE_MODULE=1 else SHARED = -shared endif diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 147b2068..619b44bc 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -60,7 +60,12 @@ endif # --------------------------------------------------------------------------------------------------------------------- # JACK/Standalone setup -ifneq ($(SKIP_RTAUDIO_FALLBACK),true) +ifeq ($(WASM),true) + +JACK_FLAGS += -sUSE_SDL=2 +JACK_LIBS += -sUSE_SDL=2 + +else ifneq ($(SKIP_RTAUDIO_FALLBACK),true) ifeq ($(MACOS),true) JACK_LIBS += -framework CoreAudio -framework CoreFoundation diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 446fe15d..44b932f0 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -31,7 +31,7 @@ # include "../extra/Thread.hpp" #endif -#ifdef STATIC_BUILD +#if defined(STATIC_BUILD) && !defined(DISTRHO_OS_WASM) # define JACKBRIDGE_DIRECT #endif diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 0ae5a4fb..7aea3ca8 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -107,6 +107,7 @@ public: explicit PluginApplication() : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE) { +#ifndef DISTRHO_OS_WASM const char* const className = ( #ifdef DISTRHO_PLUGIN_BRAND DISTRHO_PLUGIN_BRAND @@ -116,6 +117,7 @@ public: "-" DISTRHO_PLUGIN_NAME ); setClassName(className); +#endif } void triggerIdleCallbacks() From ea18b1a1ad6712a8071bd536da925e8a489dcefb Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Jul 2022 14:18:28 +0100 Subject: [PATCH 427/504] Get all plugins to build for wasm Signed-off-by: falkTX --- Makefile.base.mk | 12 +++++++++--- Makefile.plugins.mk | 4 ++-- distrho/src/DistrhoPluginVST2.cpp | 2 +- distrho/src/jackbridge/SDLBridge.hpp | 2 ++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 14c55d32..3156686e 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -175,20 +175,24 @@ endif endif ifeq ($(MACOS),true) + # MacOS linker flags LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-dead_strip,-dead_strip_dylibs ifneq ($(SKIP_STRIPPING),true) LINK_OPTS += -Wl,-x endif + else + # Common linker flags LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections +ifneq ($(WASM),true) +LINK_OPTS += -Wl,--as-needed ifneq ($(SKIP_STRIPPING),true) LINK_OPTS += -Wl,--strip-all endif -ifneq ($(WASM),true) -LINK_OPTS += -Wl,--as-needed endif + endif ifeq ($(SKIP_STRIPPING),true) @@ -535,6 +539,8 @@ endif ifeq ($(MACOS),true) LIB_EXT = .dylib +else ifeq ($(WASM),true) +LIB_EXT = .wasm else ifeq ($(WINDOWS),true) LIB_EXT = .dll else @@ -547,7 +553,7 @@ endif ifeq ($(MACOS),true) SHARED = -dynamiclib else ifeq ($(WASM),true) -SHARED = -sSIDE_MODULE=1 +SHARED = -sSIDE_MODULE=2 else SHARED = -shared endif diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 619b44bc..59f91dd5 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -170,9 +170,9 @@ SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp else ifeq ($(WASM),true) SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" SYMBOLS_DSSI = -sEXPORTED_FUNCTIONS="['ladspa_descriptor','dssi_descriptor']" -SYMBOLS_LV2DSP = -sEXPORTED_FUNCTIONS="['lv2_descriptor']" +SYMBOLS_LV2DSP = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl']" SYMBOLS_LV2UI = -sEXPORTED_FUNCTIONS="['lv2ui_descriptor']" -SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','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_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 56a3e116..b158629d 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1665,7 +1665,7 @@ static struct Cleanup { END_NAMESPACE_DISTRHO DISTRHO_PLUGIN_EXPORT -#if DISTRHO_OS_WINDOWS || DISTRHO_OS_MAC +#if DISTRHO_OS_MAC || DISTRHO_OS_WASM || DISTRHO_OS_WINDOWS const AEffect* VSTPluginMain(audioMasterCallback audioMaster); #else const AEffect* VSTPluginMain(audioMasterCallback audioMaster) asm ("main"); diff --git a/distrho/src/jackbridge/SDLBridge.hpp b/distrho/src/jackbridge/SDLBridge.hpp index 149963ca..2c5138ac 100644 --- a/distrho/src/jackbridge/SDLBridge.hpp +++ b/distrho/src/jackbridge/SDLBridge.hpp @@ -204,11 +204,13 @@ struct SDLBridge { self->jackProcessCallback(numFrames, self->jackProcessArg); +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) { for (uint j=0; j < numFrames; ++j) fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS+i][j]; } +#endif } }; From f04817d31e0a12831b979f28eb911adcc1b20fd4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Jul 2022 14:20:28 +0100 Subject: [PATCH 428/504] Fix regular non-gles2 build Signed-off-by: falkTX --- dgl/src/nanovg/nanovg_gl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index 5894ba42..4342c25f 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/dgl/src/nanovg/nanovg_gl.h @@ -404,7 +404,9 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id) if (gl->textureContext->textures[i].tex != 0 && (gl->textureContext->textures[i].flags & NVG_IMAGE_NODELETE) == 0) { glDeleteTextures(1, &gl->textureContext->textures[i].tex); +#if defined NANOVG_GLES2 free(gl->textureContext->textures[i].data); +#endif } memset(&gl->textureContext->textures[i], 0, sizeof(gl->textureContext->textures[i])); return 1; From 49a017bd8f7a5a33899b7d28895f8cfdd71c1e16 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Jul 2022 14:23:45 +0100 Subject: [PATCH 429/504] Fix a compiler warning Signed-off-by: falkTX --- distrho/src/jackbridge/SDLBridge.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distrho/src/jackbridge/SDLBridge.hpp b/distrho/src/jackbridge/SDLBridge.hpp index 2c5138ac..2027e386 100644 --- a/distrho/src/jackbridge/SDLBridge.hpp +++ b/distrho/src/jackbridge/SDLBridge.hpp @@ -194,17 +194,19 @@ struct SDLBridge { float* const fstream = (float*)stream; +#if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 if (self->jackProcessCallback == nullptr) +#endif { std::memset(fstream, 0, len); return; } +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 const uint numFrames = static_cast(static_cast(len) / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); self->jackProcessCallback(numFrames, self->jackProcessArg); -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) { for (uint j=0; j < numFrames; ++j) From 3aa2e83debdc096fe247829ecdebf11d57ab57e6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Jul 2022 14:49:21 +0100 Subject: [PATCH 430/504] Set VST3 filename for wasm builds Signed-off-by: falkTX --- Makefile.plugins.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 59f91dd5..ba01181f 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -121,6 +121,8 @@ VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so else ifeq ($(MACOS),true) VST3_CONTENTS = $(NAME).vst3/Contents VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) +else ifeq ($(WASM),true) +VST3_FILENAME = $(NAME).vst3/Contents/wasm/$(NAME).vst3 else ifeq ($(WINDOWS),true) ifeq ($(CPU_I386),true) VST3_FILENAME = $(NAME).vst3/Contents/x86-win/$(NAME).vst3 From 33ee09ae7126a688d380f51fe0ba4eab6ed391c1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Jul 2022 20:42:11 +0100 Subject: [PATCH 431/504] A few more wasm tweaks, do not allow Thread.hpp usage Signed-off-by: falkTX --- Makefile.base.mk | 12 +++++++++++- distrho/extra/Thread.hpp | 4 ++++ distrho/src/jackbridge/SDLBridge.hpp | 8 ++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 3156686e..a54d1f1f 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -89,13 +89,23 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Set PKG_CONFIG (can be overridden by environment variable) -ifeq ($(WINDOWS),true) +ifeq ($(WASM),true) +# Skip on wasm by default +PKG_CONFIG ?= false +else ifeq ($(WINDOWS),true) # Build statically on Windows by default PKG_CONFIG ?= pkg-config --static else PKG_CONFIG ?= pkg-config endif +# --------------------------------------------------------------------------------------------------------------------- +# Set cross compiling flag + +ifeq ($(WASM),true) +CROSS_COMPILING = true +endif + # --------------------------------------------------------------------------------------------------------------------- # Set LINUX_OR_MACOS diff --git a/distrho/extra/Thread.hpp b/distrho/extra/Thread.hpp index ec6f13d9..63a01acf 100644 --- a/distrho/extra/Thread.hpp +++ b/distrho/extra/Thread.hpp @@ -25,6 +25,10 @@ # include #endif +#ifdef DISTRHO_OS_WASM +# error Threads do not work under wasm! +#endif + START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- diff --git a/distrho/src/jackbridge/SDLBridge.hpp b/distrho/src/jackbridge/SDLBridge.hpp index 2027e386..38faf1d6 100644 --- a/distrho/src/jackbridge/SDLBridge.hpp +++ b/distrho/src/jackbridge/SDLBridge.hpp @@ -194,15 +194,15 @@ struct SDLBridge { float* const fstream = (float*)stream; -#if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 +// #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 if (self->jackProcessCallback == nullptr) -#endif +// #endif { std::memset(fstream, 0, len); return; } -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 +// #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 const uint numFrames = static_cast(static_cast(len) / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); self->jackProcessCallback(numFrames, self->jackProcessArg); @@ -212,7 +212,7 @@ struct SDLBridge { for (uint j=0; j < numFrames; ++j) fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS+i][j]; } -#endif +// #endif } }; From 58153fdefd7c2e33421a1cd1aa15d02add36262a Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 4 Jul 2022 20:43:17 +0100 Subject: [PATCH 432/504] Add new Runner class for a different approach to threads Signed-off-by: falkTX --- distrho/extra/Runner.hpp | 235 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 distrho/extra/Runner.hpp diff --git a/distrho/extra/Runner.hpp b/distrho/extra/Runner.hpp new file mode 100644 index 00000000..cb6a494a --- /dev/null +++ b/distrho/extra/Runner.hpp @@ -0,0 +1,235 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_RUNNER_HPP_INCLUDED +#define DISTRHO_RUNNER_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +#ifndef DISTRHO_OS_WASM +# include "Thread.hpp" +#else +# include +#endif + +START_NAMESPACE_DISTRHO + +// ------------------------------------------------------------------------------------------------------------------- +// Runner class + +/** + Runner class for DPF. + + This is a handy class that handles "idle" time in either background or main thread, + whichever is more suitable to the target platform. + Typically background threads on desktop platforms, main thread on web. + + A single function is expected to be implemented by subclasses, + which directly allows it to stop the runner by returning false. + + You can use it for quick operations that do not need to be handled in the main thread if possible. + The target is to spread out execution over many runs, instead of spending a lot of time on a single task. + */ +class Runner +{ +protected: + /* + * Constructor. + */ + Runner(const char* const runnerName = nullptr) noexcept + #ifndef DISTRHO_OS_WASM + : fRunnerThread(this, runnerName), + #else + : fRunnerName(runnerName), + fShouldStop(false), + #endif + fTimeInterval(0) {} + + /* + * Destructor. + */ + virtual ~Runner() /*noexcept*/ + { + DISTRHO_SAFE_ASSERT(! isRunnerActive()); + + stopRunner(); + } + + /* + * Virtual function to be implemented by the subclass. + * Return true to keep running, false to stop execution. + */ + virtual bool run() = 0; + + /* + * Check if the runner should stop. + * To be called from inside the runner to know if a stop request has been made. + */ + bool shouldRunnerStop() const noexcept + { + #ifndef DISTRHO_OS_WASM + return fRunnerThread.shouldThreadExit(); + #else + return fShouldStop; + #endif + } + + // --------------------------------------------------------------------------------------------------------------- + +public: + /* + * Check if the runner is active. + */ + bool isRunnerActive() noexcept + { + #ifndef DISTRHO_OS_WASM + return fRunnerThread.isThreadRunning(); + #else + fShouldStop = false; + return true; + #endif + } + + /* + * Start the thread. + */ + bool startRunner(const uint timeIntervalMilliseconds = 0) noexcept + { + fTimeInterval = timeIntervalMilliseconds; + #ifndef DISTRHO_OS_WASM + return fRunnerThread.startThread(); + #else + fShouldStop = false; + emscripten_async_call(_entryPoint, this, timeIntervalMilliseconds); + return true; + #endif + } + + /* + * Stop the runner. + * This will signal the runner to stop if active, and wait until it finishes. + */ + bool stopRunner() noexcept + { + #ifndef DISTRHO_OS_WASM + return fRunnerThread.stopThread(-1); + #else + fShouldStop = true; + return true; + #endif + } + + /* + * Tell the runner to stop as soon as possible. + */ + void signalRunnerShouldStop() noexcept + { + #ifndef DISTRHO_OS_WASM + fRunnerThread.signalThreadShouldExit(); + #else + fShouldStop = true; + #endif + } + + // --------------------------------------------------------------------------------------------------------------- + + /* + * Returns the name of the runner. + * This is the name that gets set in the constructor. + */ + const String& getRunnerName() const noexcept + { + #ifndef DISTRHO_OS_WASM + return fRunnerThread.getThreadName(); + #else + return fRunnerName; + #endif + } + + // --------------------------------------------------------------------------------------------------------------- + +private: +#ifndef DISTRHO_OS_WASM + class RunnerThread : public Thread + { + Runner* const runner; + + public: + RunnerThread(Runner* const r, const char* const rn) + : Thread(rn), + runner(r) {} + + protected: + void run() override + { + const uint timeInterval = runner->fTimeInterval; + + while (!shouldThreadExit()) + { + bool stillRunning = false; + + try { + stillRunning = runner->run(); + } catch(...) {} + + if (stillRunning && !shouldThreadExit()) + { + if (timeInterval != 0) + d_msleep(timeInterval); + + pthread_yield(); + continue; + } + + break; + } + } + } fRunnerThread; +#else + const String fRunnerName; + volatile bool fShouldStop; + + void _runEntryPoint() noexcept + { + if (fShouldStop) + return; + + bool stillRunning = false; + + try { + stillRunning = run(); + } catch(...) {} + + if (stillRunning && !fShouldStop) + emscripten_async_call(_entryPoint, this, fTimeInterval); + } + + static void _entryPoint(void* const userData) noexcept + { + static_cast(userData)->_runEntryPoint(); + } +#endif + + uint fTimeInterval; + + DISTRHO_DECLARE_NON_COPYABLE(Runner) +}; + +// ------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_RUNNER_HPP_INCLUDED From 717c7596c29530998fc8522d000c9e856618a56b Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Jul 2022 10:39:10 +0100 Subject: [PATCH 433/504] Allow to force nanovg GL version Signed-off-by: falkTX --- dgl/src/nanovg/nanovg_gl.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/dgl/src/nanovg/nanovg_gl.h b/dgl/src/nanovg/nanovg_gl.h index 4342c25f..27e12835 100644 --- a/dgl/src/nanovg/nanovg_gl.h +++ b/dgl/src/nanovg/nanovg_gl.h @@ -18,6 +18,28 @@ #ifndef NANOVG_GL_H #define NANOVG_GL_H +#if defined NANOVG_GL2_FORCED +# undef NANOVG_GL3 +# undef NANOVG_GLES2 +# undef NANOVG_GLES3 +# define NANOVG_GL2 1 +#elif defined NANOVG_GL3_FORCED +# undef NANOVG_GL2 +# undef NANOVG_GLES2 +# undef NANOVG_GLES3 +# define NANOVG_GL3 1 +#elif defined NANOVG_GLES2_FORCED +# undef NANOVG_GL2 +# undef NANOVG_GL3 +# undef NANOVG_GLES3 +# define NANOVG_GLES2 1 +#elif defined NANOVG_GLES3_FORCED +# undef NANOVG_GL2 +# undef NANOVG_GL3 +# undef NANOVG_GLES2 +# define NANOVG_GLES3 1 +#endif + #ifdef __cplusplus extern "C" { #endif From 828ce002a0800f66129ed5c1a833dff8ff55faba Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Jul 2022 14:40:57 +0100 Subject: [PATCH 434/504] Update pugl, fix inverted mouse buttons on wasm Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 0ea278e7..dbad9da2 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 0ea278e7c911fe83805e58e0d0b002c952e909b1 +Subproject commit dbad9da2f2b5ce9b6a0049a70268a0b8a3ec14a2 From 614eeaf0ef0390d4956feb991e9038980cf50371 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Jul 2022 15:08:16 +0100 Subject: [PATCH 435/504] Add a missing include Signed-off-by: falkTX --- distrho/extra/Runner.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/distrho/extra/Runner.hpp b/distrho/extra/Runner.hpp index cb6a494a..0f9dddbe 100644 --- a/distrho/extra/Runner.hpp +++ b/distrho/extra/Runner.hpp @@ -22,6 +22,7 @@ #ifndef DISTRHO_OS_WASM # include "Thread.hpp" #else +# include "String.hpp" # include #endif From c2938c02994988bd5dde4c4cbba9951709789ee8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 5 Jul 2022 23:36:26 +0100 Subject: [PATCH 436/504] Enable simd for wasm, early implementation of file dialogs Signed-off-by: falkTX --- Makefile.base.mk | 2 +- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 1 + distrho/DistrhoUI_macOS.mm | 1 + distrho/extra/FileBrowserDialogImpl.cpp | 91 +++++++++++++++++++++++-- distrho/src/DistrhoUI.cpp | 1 + distrho/src/DistrhoUIPrivateData.hpp | 2 + 7 files changed, 93 insertions(+), 7 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index a54d1f1f..e1144a48 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -172,7 +172,7 @@ BASE_OPTS = -O3 -ffast-math -fdata-sections -ffunction-sections ifeq ($(CPU_I386_OR_X86_64),true) BASE_OPTS += -mtune=generic ifeq ($(WASM),true) -# BASE_OPTS += -msse -msse2 -msse3 -msimd128 +BASE_OPTS += -msse -msse2 -msse3 -msimd128 else BASE_OPTS += -msse -msse2 -mfpmath=sse endif diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index dbad9da2..eb760757 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit dbad9da2f2b5ce9b6a0049a70268a0b8a3ec14a2 +Subproject commit eb7607571d38d8f06eea95eb2a04667402d49eab diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index da7f498c..1254da28 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -108,6 +108,7 @@ #ifndef DGL_FILE_BROWSER_DISABLED # define FILE_BROWSER_DIALOG_DGL_NAMESPACE +# define FILE_BROWSER_DIALOG_NAMESPACE DGL_NAMESPACE # define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED START_NAMESPACE_DGL # include "../../distrho/extra/FileBrowserDialogImpl.hpp" diff --git a/distrho/DistrhoUI_macOS.mm b/distrho/DistrhoUI_macOS.mm index 15dfa6ac..80b06182 100644 --- a/distrho/DistrhoUI_macOS.mm +++ b/distrho/DistrhoUI_macOS.mm @@ -25,6 +25,7 @@ #if DISTRHO_UI_FILE_BROWSER # define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED +# define FILE_BROWSER_DIALOG_NAMESPACE DISTRHO_NAMESPACE # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE START_NAMESPACE_DISTRHO # include "extra/FileBrowserDialogImpl.hpp" diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index 108475cd..76d01978 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -27,6 +27,9 @@ #ifdef DISTRHO_OS_MAC # import #endif +#ifdef DISTRHO_OS_WASM +# include +#endif #ifdef DISTRHO_OS_WINDOWS # include # include @@ -71,6 +74,52 @@ static constexpr int toHexChar(const char c) noexcept } #endif +#ifdef DISTRHO_OS_WASM +# define DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION +# define DISTRHO_WASM_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) +# define DISTRHO_WASM_NAMESPACE_HELPER(NS) #NS +# define DISTRHO_WASM_NAMESPACE(NS) DISTRHO_WASM_NAMESPACE_HELPER(NS) +// FIXME use world class name as prefix +EM_JS(bool, DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, openWebBrowserFileDialog), (const char* funcname, void* handle), { + var canvasFileElem = document.getElementById('canvas_file_open'); + var jsfuncname = UTF8ToString(funcname); + if (canvasFileElem) { + canvasFileElem.onchange = function(e) { + if (!!canvasFileElem.files) { + var file = canvasFileElem.files[0]; + var filename = '/' + file.name; + var reader = new FileReader(); + reader.onloadend = function(e) { + var content = new Uint8Array(reader.result); + Module.FS.writeFile(filename, content); + Module.ccall(jsfuncname, 'null', ['number', 'string'], [handle, filename]); + }; + reader.readAsArrayBuffer(file); + } + }; + canvasFileElem.click(); + return true; + } + return false; +}); +EM_JS(bool, DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, downloadWebBrowserFile), (), { + var canvasFileElem = document.getElementById('canvas_file_save'); + if (canvasFileElem) { + // FIXME filename shouldn't change + var content = Module.FS.readFile('/download.vcv'); + canvasFileElem.download = 'download.vcv'; + canvasFileElem.href = URL.createObjectURL(new Blob([content])); + canvasFileElem.click(); + return true; + } + return false; +}); +# define openWebBrowserFileDialogNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, openWebBrowserFileDialog) +# define downloadWebBrowserFileNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, downloadWebBrowserFile) +# define fileBrowserSetPathNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, fileBrowserSetPath) +# define fileBrowserSetPathFuncName DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE) "_fileBrowserSetPath" +#endif + struct FileBrowserData { const char* selectedFile; @@ -284,6 +333,19 @@ struct FileBrowserData { // -------------------------------------------------------------------------------------------------------------------- +#ifdef DISTRHO_OS_WASM +extern "C" { +EMSCRIPTEN_KEEPALIVE +void fileBrowserSetPathNamespaced(FileBrowserHandle handle, const char* filename) +{ + handle->free(); + + if (filename != nullptr && filename[0] != '\0') + handle->selectedFile = strdup(filename); +} +} +#endif + FileBrowserHandle fileBrowserCreate(const bool isEmbed, const uintptr_t windowId, const double scaleFactor, @@ -295,17 +357,13 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, { #ifdef DISTRHO_OS_WINDOWS if (char* const cwd = _getcwd(nullptr, 0)) - { - startDir = cwd; - std::free(cwd); - } #else if (char* const cwd = getcwd(nullptr, 0)) +#endif { startDir = cwd; std::free(cwd); } -#endif } DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), nullptr); @@ -375,6 +433,18 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, # endif #endif +#ifdef DISTRHO_OS_WASM + if (options.saving) + { + handle->selectedFile = strdup("/download"); + return handle.release(); + } + + const char* const funcname = fileBrowserSetPathFuncName; + if (openWebBrowserFileDialogNamespaced(funcname, handle.get())) + return handle.release(); +#endif + #ifdef DISTRHO_OS_WINDOWS handle->setupAndStart(isEmbed, startDir, windowTitle, windowId, options); #endif @@ -663,6 +733,11 @@ bool fileBrowserIdle(const FileBrowserHandle handle) void fileBrowserClose(const FileBrowserHandle handle) { +#ifdef DISTRHO_OS_WASM + if (fileBrowserGetPath(handle) != nullptr) + downloadWebBrowserFileNamespaced(); +#endif + #ifdef HAVE_X11 if (Display* const x11display = handle->x11display) x_fib_close(x11display); @@ -693,3 +768,9 @@ END_NAMESPACE_DISTRHO #undef FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE #undef FILE_BROWSER_DIALOG_DGL_NAMESPACE +#undef FILE_BROWSER_DIALOG_NAMESPACE + +#undef openWebBrowserFileDialogNamespaced +#undef downloadWebBrowserFileNamespaced +#undef fileBrowserSetPathNamespaced +#undef fileBrowserSetPathFuncName diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index cc3f53c4..3af6926f 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -44,6 +44,7 @@ # define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_show) # define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_status) # define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED +# define FILE_BROWSER_DIALOG_NAMESPACE DISTRHO_NAMESPACE # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE START_NAMESPACE_DISTRHO # include "../extra/FileBrowserDialogImpl.hpp" diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 7aea3ca8..4e99fa2b 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -481,7 +481,9 @@ inline void PluginWindow::onFileSelected(const char* const filename) } #endif + puglBackendEnter(pData->view); ui->uiFileBrowserSelected(filename); + puglBackendLeave(pData->view); } #endif From b63db742200341b91358c14a7290104cb97d5613 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Jul 2022 14:31:44 +0100 Subject: [PATCH 437/504] wasm file browser details, add WASM_EXCEPTIONS build option Signed-off-by: falkTX --- Makefile.base.mk | 4 + distrho/extra/FileBrowserDialogImpl.cpp | 115 +++++++++++++++++------- distrho/extra/FileBrowserDialogImpl.hpp | 4 + 3 files changed, 89 insertions(+), 34 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index e1144a48..14341635 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -267,6 +267,10 @@ ifeq ($(MACOS_OLD),true) BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) -DHAVE_CPP11_SUPPORT=0 endif +ifeq ($(WASM_EXCEPTIONS),true) +BUILD_CXX_FLAGS += -fexceptions +endif + ifeq ($(WINDOWS),true) # Always build statically on windows LINK_FLAGS += -static -static-libgcc -static-libstdc++ diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index 76d01978..87fbd138 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -81,38 +81,63 @@ static constexpr int toHexChar(const char c) noexcept # define DISTRHO_WASM_NAMESPACE(NS) DISTRHO_WASM_NAMESPACE_HELPER(NS) // FIXME use world class name as prefix EM_JS(bool, DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, openWebBrowserFileDialog), (const char* funcname, void* handle), { - var canvasFileElem = document.getElementById('canvas_file_open'); + var canvasFileOpenElem = document.getElementById('canvas_file_open'); var jsfuncname = UTF8ToString(funcname); - if (canvasFileElem) { - canvasFileElem.onchange = function(e) { - if (!!canvasFileElem.files) { - var file = canvasFileElem.files[0]; - var filename = '/' + file.name; - var reader = new FileReader(); - reader.onloadend = function(e) { - var content = new Uint8Array(reader.result); - Module.FS.writeFile(filename, content); - Module.ccall(jsfuncname, 'null', ['number', 'string'], [handle, filename]); - }; - reader.readAsArrayBuffer(file); - } - }; - canvasFileElem.click(); - return true; + var jsfunc = Module.cwrap(jsfuncname, 'null', ['number', 'string']); + + if (!canvasFileOpenElem) { + jsfunc(handle, ""); + return false; } - return false; + + canvasFileOpenElem.onchange = function(e) { + if (!canvasFileOpenElem.files) { + jsfunc(handle, ""); + return; + } + + var file = canvasFileOpenElem.files[0]; + var filename = '/' + file.name; + var reader = new FileReader(); + + reader.onloadend = function(e) { + var content = new Uint8Array(reader.result); + Module.FS.writeFile(filename, content); + jsfunc(handle, filename); + }; + + reader.readAsArrayBuffer(file); + }; + + canvasFileOpenElem.click(); + return true; }); -EM_JS(bool, DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, downloadWebBrowserFile), (), { - var canvasFileElem = document.getElementById('canvas_file_save'); - if (canvasFileElem) { - // FIXME filename shouldn't change - var content = Module.FS.readFile('/download.vcv'); - canvasFileElem.download = 'download.vcv'; - canvasFileElem.href = URL.createObjectURL(new Blob([content])); - canvasFileElem.click(); - return true; +EM_JS(bool, DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, downloadWebBrowserFile), (const char* nameprefix, const char* filename), { + var canvasFileObjName = UTF8ToString(nameprefix) + "_file_save"; + var jsfilename = UTF8ToString(filename); + + var canvasFileSaveElem = document.getElementById(canvasFileObjName); + if (canvasFileSaveElem) { + // only 1 file save allowed at once + console.warn("One file save operation already in progress, refusing to open another"); + return false; } - return false; + + canvasFileSaveElem = document.createElement('a'); + canvasFileSaveElem.download = jsfilename; + canvasFileSaveElem.id = canvasFileObjName; + canvasFileSaveElem.style.display = 'none'; + document.body.appendChild(canvasFileSaveElem); + + var content = Module.FS.readFile('/' + jsfilename); + canvasFileSaveElem.href = URL.createObjectURL(new Blob([content])); + canvasFileSaveElem.click(); + + setTimeout(function() { + URL.revokeObjectURL(canvasFileSaveElem.href); + document.body.removeChild(canvasFileSaveElem); + }, 2000); + return true; }); # define openWebBrowserFileDialogNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, openWebBrowserFileDialog) # define downloadWebBrowserFileNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, downloadWebBrowserFile) @@ -134,6 +159,11 @@ struct FileBrowserData { Display* x11display; #endif +#ifdef DISTRHO_OS_WASM + char* defaultName; + bool saving; +#endif + #ifdef DISTRHO_OS_WINDOWS OPENFILENAMEW ofn; volatile bool threadCancelled; @@ -270,11 +300,11 @@ struct FileBrowserData { return 0; } #else // DISTRHO_OS_WINDOWS - FileBrowserData(const bool saving) + FileBrowserData(const bool save) : selectedFile(nullptr) { #ifdef DISTRHO_OS_MAC - if (saving) + if (save) { nsOpenPanel = nullptr; nsBasePanel = [[NSSavePanel savePanel]retain]; @@ -285,6 +315,10 @@ struct FileBrowserData { nsBasePanel = nsOpenPanel; } #endif +#ifdef DISTRHO_OS_WASM + defaultName = nullptr; + saving = save; +#endif #ifdef HAVE_DBUS if ((dbuscon = dbus_bus_get(DBUS_BUS_SESSION, nullptr)) != nullptr) dbus_connection_set_exit_on_disconnect(dbuscon, false); @@ -294,7 +328,7 @@ struct FileBrowserData { #endif // maybe unused - return; (void)saving; + return; (void)save; } ~FileBrowserData() @@ -302,6 +336,9 @@ struct FileBrowserData { #ifdef DISTRHO_OS_MAC [nsBasePanel release]; #endif +#ifdef DISTRHO_OS_WASM + std::free(defaultName); +#endif #ifdef HAVE_DBUS if (dbuscon != nullptr) dbus_connection_unref(dbuscon); @@ -342,6 +379,8 @@ void fileBrowserSetPathNamespaced(FileBrowserHandle handle, const char* filename if (filename != nullptr && filename[0] != '\0') handle->selectedFile = strdup(filename); + else + handle->selectedFile = kSelectedFileCancelled; } } #endif @@ -436,7 +475,15 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, #ifdef DISTRHO_OS_WASM if (options.saving) { - handle->selectedFile = strdup("/download"); + const size_t len = options.defaultName != nullptr ? strlen(options.defaultName) : 0; + DISTRHO_SAFE_ASSERT_RETURN(len != 0, nullptr); + + char* const filename = static_cast(malloc(len + 2)); + filename[0] = '/'; + std::memcpy(filename + 1, options.defaultName, len + 1); + + handle->defaultName = strdup(options.defaultName); + handle->selectedFile = filename; return handle.release(); } @@ -734,8 +781,8 @@ bool fileBrowserIdle(const FileBrowserHandle handle) void fileBrowserClose(const FileBrowserHandle handle) { #ifdef DISTRHO_OS_WASM - if (fileBrowserGetPath(handle) != nullptr) - downloadWebBrowserFileNamespaced(); + if (handle->saving && fileBrowserGetPath(handle) != nullptr) + downloadWebBrowserFileNamespaced(DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE), handle->defaultName); #endif #ifdef HAVE_X11 diff --git a/distrho/extra/FileBrowserDialogImpl.hpp b/distrho/extra/FileBrowserDialogImpl.hpp index e15cfef0..5b47a52c 100644 --- a/distrho/extra/FileBrowserDialogImpl.hpp +++ b/distrho/extra/FileBrowserDialogImpl.hpp @@ -34,6 +34,9 @@ struct FileBrowserOptions { /** Whether we are saving, opening files otherwise (default) */ bool saving; + /** Default filename when saving, required in some platforms (basename without path separators) */ + const char* defaultName; + /** Start directory, uses current working directory if null */ const char* startDir; @@ -74,6 +77,7 @@ struct FileBrowserOptions { /** Constructor for default values */ FileBrowserOptions() : saving(false), + defaultName(nullptr), startDir(nullptr), title(nullptr), buttons() {} From 5e77ebb317d5fd7209386ce8eb367682896d02b2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Jul 2022 17:12:24 +0100 Subject: [PATCH 438/504] Skip pthread_yield for now Signed-off-by: falkTX --- distrho/extra/Runner.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distrho/extra/Runner.hpp b/distrho/extra/Runner.hpp index 0f9dddbe..f1ad970b 100644 --- a/distrho/extra/Runner.hpp +++ b/distrho/extra/Runner.hpp @@ -191,7 +191,8 @@ private: if (timeInterval != 0) d_msleep(timeInterval); - pthread_yield(); + // FIXME + // pthread_yield(); continue; } From ee94ba0caeabe25e2ecff1039d321e8edb5acedd Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 6 Jul 2022 23:38:55 +0100 Subject: [PATCH 439/504] Automatically set a few flags on wasm Signed-off-by: falkTX --- Makefile.plugins.mk | 4 ++++ dgl/src/pugl-upstream | 2 +- distrho/src/DistrhoUtils.cpp | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index ba01181f..4656ad24 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -65,6 +65,10 @@ ifeq ($(WASM),true) JACK_FLAGS += -sUSE_SDL=2 JACK_LIBS += -sUSE_SDL=2 +ifneq ($(FILE_BROWSER_DISABLED),true) +JACK_LIBS += -sEXPORTED_RUNTIME_METHODS=FS,cwrap +endif + else ifneq ($(SKIP_RTAUDIO_FALLBACK),true) ifeq ($(MACOS),true) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index eb760757..6d4e2510 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit eb7607571d38d8f06eea95eb2a04667402d49eab +Subproject commit 6d4e25100be6719565b512d6aeac19c5b581ba48 diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index 364461ff..fc0ff25a 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -78,7 +78,11 @@ const char* getPluginFormatName() noexcept #if defined(DISTRHO_PLUGIN_TARGET_CARLA) return "Carla"; #elif defined(DISTRHO_PLUGIN_TARGET_JACK) +# ifdef DISTRHO_OS_WASM + return "Wasm/Standalone"; +# else return "JACK/Standalone"; +# endif #elif defined(DISTRHO_PLUGIN_TARGET_LADSPA) return "LADSPA"; #elif defined(DISTRHO_PLUGIN_TARGET_DSSI) From 34bf2a4dfca724fc544f8ab0a15727ddf558d7d8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 7 Jul 2022 01:00:05 +0100 Subject: [PATCH 440/504] Use emscripten_set_timeout_loop instead of manual management Signed-off-by: falkTX --- distrho/extra/Runner.hpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/distrho/extra/Runner.hpp b/distrho/extra/Runner.hpp index f1ad970b..fb3e181c 100644 --- a/distrho/extra/Runner.hpp +++ b/distrho/extra/Runner.hpp @@ -23,7 +23,7 @@ # include "Thread.hpp" #else # include "String.hpp" -# include +# include #endif START_NAMESPACE_DISTRHO @@ -114,7 +114,7 @@ public: return fRunnerThread.startThread(); #else fShouldStop = false; - emscripten_async_call(_entryPoint, this, timeIntervalMilliseconds); + emscripten_set_timeout_loop(_entryPoint, timeIntervalMilliseconds, this); return true; #endif } @@ -204,10 +204,10 @@ private: const String fRunnerName; volatile bool fShouldStop; - void _runEntryPoint() noexcept + EM_BOOL _runEntryPoint() noexcept { if (fShouldStop) - return; + return EM_FALSE; bool stillRunning = false; @@ -216,12 +216,14 @@ private: } catch(...) {} if (stillRunning && !fShouldStop) - emscripten_async_call(_entryPoint, this, fTimeInterval); + return EM_TRUE; + + return EM_FALSE; } - static void _entryPoint(void* const userData) noexcept + static EM_BOOL _entryPoint(double, void* const userData) noexcept { - static_cast(userData)->_runEntryPoint(); + return static_cast(userData)->_runEntryPoint(); } #endif From aa99d51731cf915d83abb578c232b89fbad6c231 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 7 Jul 2022 13:34:42 +0100 Subject: [PATCH 441/504] Use GL2 by default with compat profile, set GL3 and GLES target too Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 10 +++++++++- dgl/src/pugl-upstream | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 92d90fc7..dddcf264 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -261,10 +261,18 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo puglSetViewHint(view, PUGL_DEPTH_BITS, 16); #endif puglSetViewHint(view, PUGL_STENCIL_BITS, 8); -#ifdef DGL_USE_OPENGL3 + +#if defined(DGL_USE_OPENGL3) || defined(DGL_USE_GLES3) puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); +#elif defined(DGL_USE_GLES2) + puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); + puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); +#else + puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_TRUE); + puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); #endif + // PUGL_SAMPLES ?? puglSetEventFunc(view, puglEventCallback); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 6d4e2510..c4fd6af2 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 6d4e25100be6719565b512d6aeac19c5b581ba48 +Subproject commit c4fd6af2ebd694fcbafeb81619a44ad90639a3b0 From b9e654c3d331933dbeae0413246be8dc2bd64a58 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 8 Jul 2022 02:28:41 +0100 Subject: [PATCH 442/504] Fix wasm returning non-null invalid value for open file; Cleanup Signed-off-by: falkTX --- distrho/extra/FileBrowserDialogImpl.cpp | 2 ++ distrho/src/DistrhoUIPrivateData.hpp | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index 87fbd138..d06477cd 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -490,6 +490,8 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, const char* const funcname = fileBrowserSetPathFuncName; if (openWebBrowserFileDialogNamespaced(funcname, handle.get())) return handle.release(); + + return nullptr; #endif #ifdef DISTRHO_OS_WINDOWS diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 4e99fa2b..1cc23ccf 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -25,6 +25,7 @@ #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # include "../extra/Sleep.hpp" +// TODO import and use file browser here #else # include "../../dgl/src/ApplicationPrivateData.hpp" # include "../../dgl/src/WindowPrivateData.hpp" @@ -319,7 +320,7 @@ struct UI::PrivateData { uint fgColor; double scaleFactor; uintptr_t winId; -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) +#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI char* uiStateFileKeyRequest; #endif char* bundlePath; @@ -346,7 +347,7 @@ struct UI::PrivateData { fgColor(0xffffffff), scaleFactor(1.0), winId(0), -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) +#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI uiStateFileKeyRequest(nullptr), #endif bundlePath(nullptr), @@ -382,7 +383,7 @@ struct UI::PrivateData { ~PrivateData() noexcept { -#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) +#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI std::free(uiStateFileKeyRequest); #endif std::free(bundlePath); @@ -437,7 +438,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) if (fileRequestCallbackFunc != nullptr) return fileRequestCallbackFunc(callbacksPtr, key); -#if DISTRHO_PLUGIN_WANT_STATE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) +#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI std::free(uiStateFileKeyRequest); uiStateFileKeyRequest = strdup(key); DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false); @@ -457,7 +458,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) // ----------------------------------------------------------------------- // PluginWindow onFileSelected that require UI::PrivateData definitions -#if DISTRHO_UI_FILE_BROWSER +#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI inline void PluginWindow::onFileSelected(const char* const filename) { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); From 7b1ccac2a05068332cc17ffe13796cf562e34456 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Jul 2022 14:55:41 +0100 Subject: [PATCH 443/504] Always build wasm standalones with -sMAIN_MODULE Signed-off-by: falkTX --- Makefile.plugins.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 4656ad24..8ee9b7ab 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -64,6 +64,7 @@ ifeq ($(WASM),true) JACK_FLAGS += -sUSE_SDL=2 JACK_LIBS += -sUSE_SDL=2 +JACK_LIBS += -sMAIN_MODULE ifneq ($(FILE_BROWSER_DISABLED),true) JACK_LIBS += -sEXPORTED_RUNTIME_METHODS=FS,cwrap From a415fc875a3bc8587524957ec88091153db25795 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Jul 2022 15:05:23 +0100 Subject: [PATCH 444/504] Fix HAVE_DGL check for wasm Signed-off-by: falkTX --- Makefile.base.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 14341635..2102410f 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -479,7 +479,7 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Backwards-compatible HAVE_DGL -ifeq ($(MACOS_OR_WINDOWS),true) +ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) HAVE_DGL = true else ifeq ($(HAVE_OPENGL),true) HAVE_DGL = $(HAVE_X11) From 8a2eb6cdb64913e884332e351afd255c87872a80 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Jul 2022 19:17:03 +0100 Subject: [PATCH 445/504] Simplify wasm runner, now using emscripten_set_interval Signed-off-by: falkTX --- distrho/extra/Runner.hpp | 51 ++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/distrho/extra/Runner.hpp b/distrho/extra/Runner.hpp index fb3e181c..de35f076 100644 --- a/distrho/extra/Runner.hpp +++ b/distrho/extra/Runner.hpp @@ -53,11 +53,14 @@ protected: Runner(const char* const runnerName = nullptr) noexcept #ifndef DISTRHO_OS_WASM : fRunnerThread(this, runnerName), + fTimeInterval(0) #else : fRunnerName(runnerName), - fShouldStop(false), + fIntervalId(0) #endif - fTimeInterval(0) {} + { + } + /* * Destructor. @@ -84,7 +87,7 @@ protected: #ifndef DISTRHO_OS_WASM return fRunnerThread.shouldThreadExit(); #else - return fShouldStop; + return fIntervalId == 0; #endif } @@ -99,8 +102,7 @@ public: #ifndef DISTRHO_OS_WASM return fRunnerThread.isThreadRunning(); #else - fShouldStop = false; - return true; + return fIntervalId != 0; #endif } @@ -109,12 +111,13 @@ public: */ bool startRunner(const uint timeIntervalMilliseconds = 0) noexcept { - fTimeInterval = timeIntervalMilliseconds; #ifndef DISTRHO_OS_WASM + DISTRHO_SAFE_ASSERT_RETURN(!fRunnerThread.isThreadRunning(), false); + fTimeInterval = timeIntervalMilliseconds; return fRunnerThread.startThread(); #else - fShouldStop = false; - emscripten_set_timeout_loop(_entryPoint, timeIntervalMilliseconds, this); + DISTRHO_SAFE_ASSERT_RETURN(fIntervalId == 0, false); + fIntervalId = emscripten_set_interval(_entryPoint, timeIntervalMilliseconds, this); return true; #endif } @@ -128,7 +131,7 @@ public: #ifndef DISTRHO_OS_WASM return fRunnerThread.stopThread(-1); #else - fShouldStop = true; + signalRunnerShouldStop(); return true; #endif } @@ -141,7 +144,11 @@ public: #ifndef DISTRHO_OS_WASM fRunnerThread.signalThreadShouldExit(); #else - fShouldStop = true; + if (fIntervalId != 0) + { + emscripten_clear_interval(fIntervalId); + fIntervalId = 0; + } #endif } @@ -200,35 +207,33 @@ private: } } } fRunnerThread; + + uint fTimeInterval; #else const String fRunnerName; - volatile bool fShouldStop; + long fIntervalId; - EM_BOOL _runEntryPoint() noexcept + void _runEntryPoint() noexcept { - if (fShouldStop) - return EM_FALSE; - bool stillRunning = false; try { stillRunning = run(); } catch(...) {} - if (stillRunning && !fShouldStop) - return EM_TRUE; - - return EM_FALSE; + if (fIntervalId != 0 && !stillRunning) + { + emscripten_clear_interval(fIntervalId); + fIntervalId = 0; + } } - static EM_BOOL _entryPoint(double, void* const userData) noexcept + static void _entryPoint(void* const userData) noexcept { - return static_cast(userData)->_runEntryPoint(); + static_cast(userData)->_runEntryPoint(); } #endif - uint fTimeInterval; - DISTRHO_DECLARE_NON_COPYABLE(Runner) }; From 36efc2473f9023a7a0cc0ea1fddcf249847958d3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Jul 2022 19:24:09 +0100 Subject: [PATCH 446/504] Make wasm file dialogs properly namespaced Signed-off-by: falkTX --- distrho/extra/FileBrowserDialogImpl.cpp | 133 ++++++++++++++---------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/distrho/extra/FileBrowserDialogImpl.cpp b/distrho/extra/FileBrowserDialogImpl.cpp index d06477cd..2e9de42b 100644 --- a/distrho/extra/FileBrowserDialogImpl.cpp +++ b/distrho/extra/FileBrowserDialogImpl.cpp @@ -74,77 +74,98 @@ static constexpr int toHexChar(const char c) noexcept } #endif +// -------------------------------------------------------------------------------------------------------------------- + #ifdef DISTRHO_OS_WASM # define DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION # define DISTRHO_WASM_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_WASM_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) # define DISTRHO_WASM_NAMESPACE_HELPER(NS) #NS # define DISTRHO_WASM_NAMESPACE(NS) DISTRHO_WASM_NAMESPACE_HELPER(NS) +# define fileBrowserSetPathNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, fileBrowserSetPath) +# define fileBrowserSetPathFuncName DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE) "_fileBrowserSetPath" + // FIXME use world class name as prefix -EM_JS(bool, DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, openWebBrowserFileDialog), (const char* funcname, void* handle), { - var canvasFileOpenElem = document.getElementById('canvas_file_open'); - var jsfuncname = UTF8ToString(funcname); - var jsfunc = Module.cwrap(jsfuncname, 'null', ['number', 'string']); +static bool openWebBrowserFileDialog(const char* const funcname, void* const handle) +{ + const char* const nameprefix = DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE); - if (!canvasFileOpenElem) { - jsfunc(handle, ""); - return false; - } + return EM_ASM_INT({ + var canvasFileObjName = UTF8ToString($0) + "_file_open"; + var canvasFileOpenElem = document.getElementById(canvasFileObjName); - canvasFileOpenElem.onchange = function(e) { - if (!canvasFileOpenElem.files) { - jsfunc(handle, ""); - return; + var jsfuncname = UTF8ToString($1); + var jsfunc = Module.cwrap(jsfuncname, 'null', ['number', 'string']); + + if (canvasFileOpenElem) { + document.body.removeChild(canvasFileOpenElem); } - var file = canvasFileOpenElem.files[0]; - var filename = '/' + file.name; - var reader = new FileReader(); + canvasFileOpenElem = document.createElement('input'); + canvasFileOpenElem.type = 'file'; + canvasFileOpenElem.id = canvasFileObjName; + canvasFileOpenElem.style.display = 'none'; + document.body.appendChild(canvasFileOpenElem); + + canvasFileOpenElem.onchange = function(e) { + if (!canvasFileOpenElem.files) { + jsfunc($2, ""); + return; + } + + var file = canvasFileOpenElem.files[0]; + var filename = '/' + file.name; + var reader = new FileReader(); + + reader.onloadend = function(e) { + var content = new Uint8Array(reader.result); + Module.FS.writeFile(filename, content); + jsfunc($2, filename); + }; - reader.onloadend = function(e) { - var content = new Uint8Array(reader.result); - Module.FS.writeFile(filename, content); - jsfunc(handle, filename); + reader.readAsArrayBuffer(file); }; - reader.readAsArrayBuffer(file); - }; + canvasFileOpenElem.click(); + return 1; + }, nameprefix, funcname, handle) != 0; +} - canvasFileOpenElem.click(); - return true; -}); -EM_JS(bool, DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, downloadWebBrowserFile), (const char* nameprefix, const char* filename), { - var canvasFileObjName = UTF8ToString(nameprefix) + "_file_save"; - var jsfilename = UTF8ToString(filename); +static bool downloadWebBrowserFile(const char* const filename) +{ + const char* const nameprefix = DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE); - var canvasFileSaveElem = document.getElementById(canvasFileObjName); - if (canvasFileSaveElem) { - // only 1 file save allowed at once - console.warn("One file save operation already in progress, refusing to open another"); - return false; - } + return EM_ASM_INT({ + var canvasFileObjName = UTF8ToString($0) + "_file_save"; + var jsfilename = UTF8ToString($1); - canvasFileSaveElem = document.createElement('a'); - canvasFileSaveElem.download = jsfilename; - canvasFileSaveElem.id = canvasFileObjName; - canvasFileSaveElem.style.display = 'none'; - document.body.appendChild(canvasFileSaveElem); - - var content = Module.FS.readFile('/' + jsfilename); - canvasFileSaveElem.href = URL.createObjectURL(new Blob([content])); - canvasFileSaveElem.click(); - - setTimeout(function() { - URL.revokeObjectURL(canvasFileSaveElem.href); - document.body.removeChild(canvasFileSaveElem); - }, 2000); - return true; -}); -# define openWebBrowserFileDialogNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, openWebBrowserFileDialog) -# define downloadWebBrowserFileNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, downloadWebBrowserFile) -# define fileBrowserSetPathNamespaced DISTRHO_WASM_NAMESPACE_MACRO(FILE_BROWSER_DIALOG_NAMESPACE, fileBrowserSetPath) -# define fileBrowserSetPathFuncName DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE) "_fileBrowserSetPath" + var canvasFileSaveElem = document.getElementById(canvasFileObjName); + if (canvasFileSaveElem) { + // only 1 file save allowed at once + console.warn("One file save operation already in progress, refusing to open another"); + return 0; + } + + canvasFileSaveElem = document.createElement('a'); + canvasFileSaveElem.download = jsfilename; + canvasFileSaveElem.id = canvasFileObjName; + canvasFileSaveElem.style.display = 'none'; + document.body.appendChild(canvasFileSaveElem); + + var content = Module.FS.readFile('/' + jsfilename); + canvasFileSaveElem.href = URL.createObjectURL(new Blob([content])); + canvasFileSaveElem.click(); + + setTimeout(function() { + URL.revokeObjectURL(canvasFileSaveElem.href); + document.body.removeChild(canvasFileSaveElem); + }, 2000); + return 1; + }, nameprefix, filename) != 0; +} #endif +// -------------------------------------------------------------------------------------------------------------------- + struct FileBrowserData { const char* selectedFile; @@ -488,7 +509,7 @@ FileBrowserHandle fileBrowserCreate(const bool isEmbed, } const char* const funcname = fileBrowserSetPathFuncName; - if (openWebBrowserFileDialogNamespaced(funcname, handle.get())) + if (openWebBrowserFileDialog(funcname, handle.get())) return handle.release(); return nullptr; @@ -784,7 +805,7 @@ void fileBrowserClose(const FileBrowserHandle handle) { #ifdef DISTRHO_OS_WASM if (handle->saving && fileBrowserGetPath(handle) != nullptr) - downloadWebBrowserFileNamespaced(DISTRHO_WASM_NAMESPACE(FILE_BROWSER_DIALOG_NAMESPACE), handle->defaultName); + downloadWebBrowserFile(handle->defaultName); #endif #ifdef HAVE_X11 @@ -819,7 +840,5 @@ END_NAMESPACE_DISTRHO #undef FILE_BROWSER_DIALOG_DGL_NAMESPACE #undef FILE_BROWSER_DIALOG_NAMESPACE -#undef openWebBrowserFileDialogNamespaced -#undef downloadWebBrowserFileNamespaced #undef fileBrowserSetPathNamespaced #undef fileBrowserSetPathFuncName From 02216aba747685fe3d8c1e7b95d7fc008249fee2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 9 Jul 2022 22:35:58 +0100 Subject: [PATCH 447/504] Allow to redirect Runner emscript calls, needed for exceptions Signed-off-by: falkTX --- Makefile.base.mk | 1 + Makefile.plugins.mk | 2 +- dgl/src/pugl-upstream | 2 +- distrho/extra/Runner.hpp | 15 +++++++++++---- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 2102410f..2dab4bac 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -269,6 +269,7 @@ endif ifeq ($(WASM_EXCEPTIONS),true) BUILD_CXX_FLAGS += -fexceptions +LINK_FLAGS += -fexceptions endif ifeq ($(WINDOWS),true) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 8ee9b7ab..99fb613a 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -64,7 +64,7 @@ ifeq ($(WASM),true) JACK_FLAGS += -sUSE_SDL=2 JACK_LIBS += -sUSE_SDL=2 -JACK_LIBS += -sMAIN_MODULE +JACK_LIBS += -sMAIN_MODULE -ldl ifneq ($(FILE_BROWSER_DISABLED),true) JACK_LIBS += -sEXPORTED_RUNTIME_METHODS=FS,cwrap diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index c4fd6af2..12b10e4b 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit c4fd6af2ebd694fcbafeb81619a44ad90639a3b0 +Subproject commit 12b10e4bb38da3a88278becdf3c4f31ea1a86f32 diff --git a/distrho/extra/Runner.hpp b/distrho/extra/Runner.hpp index de35f076..c1fd658b 100644 --- a/distrho/extra/Runner.hpp +++ b/distrho/extra/Runner.hpp @@ -28,6 +28,14 @@ START_NAMESPACE_DISTRHO +#ifdef DISTRHO_RUNNER_INDIRECT_WASM_CALLS +long d_emscripten_set_interval(void (*)(void*), double, void*); +void d_emscripten_clear_interval(long); +#else +# define d_emscripten_set_interval emscripten_set_interval +# define d_emscripten_clear_interval emscripten_clear_interval +#endif + // ------------------------------------------------------------------------------------------------------------------- // Runner class @@ -61,7 +69,6 @@ protected: { } - /* * Destructor. */ @@ -117,7 +124,7 @@ public: return fRunnerThread.startThread(); #else DISTRHO_SAFE_ASSERT_RETURN(fIntervalId == 0, false); - fIntervalId = emscripten_set_interval(_entryPoint, timeIntervalMilliseconds, this); + fIntervalId = d_emscripten_set_interval(_entryPoint, timeIntervalMilliseconds, this); return true; #endif } @@ -146,7 +153,7 @@ public: #else if (fIntervalId != 0) { - emscripten_clear_interval(fIntervalId); + d_emscripten_clear_interval(fIntervalId); fIntervalId = 0; } #endif @@ -223,7 +230,7 @@ private: if (fIntervalId != 0 && !stillRunning) { - emscripten_clear_interval(fIntervalId); + d_emscripten_clear_interval(fIntervalId); fIntervalId = 0; } } From f022c766bd315b5bb22800b9ab78a4e1ec991edc Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 10 Jul 2022 03:54:08 +0100 Subject: [PATCH 448/504] Add Application::getClassName(), more wasm pugl things Signed-off-by: falkTX --- dgl/Application.hpp | 8 +++++++- dgl/src/ApplicationPrivateData.cpp | 7 +++++++ dgl/src/pugl-upstream | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dgl/Application.hpp b/dgl/Application.hpp index 37a91b76..ed25a64b 100644 --- a/dgl/Application.hpp +++ b/dgl/Application.hpp @@ -109,7 +109,7 @@ public: void removeIdleCallback(IdleCallback* callback); /** - Set the class name of the application. + Get the class name of the application. This is a stable identifier for the application, used as the window class/instance name on X11 and Windows. It is not displayed to the user, but can be used in scripts and by window managers, @@ -117,6 +117,12 @@ public: Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name. */ + const char* getClassName() const noexcept; + + /** + Set the class name of the application. + @see getClassName + */ void setClassName(const char* name); private: diff --git a/dgl/src/ApplicationPrivateData.cpp b/dgl/src/ApplicationPrivateData.cpp index 7bf48836..ca06a1bb 100644 --- a/dgl/src/ApplicationPrivateData.cpp +++ b/dgl/src/ApplicationPrivateData.cpp @@ -45,6 +45,13 @@ static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept // -------------------------------------------------------------------------------------------------------------------- +const char* Application::getClassName() const noexcept +{ + return puglGetClassName(pData->world); +} + +// -------------------------------------------------------------------------------------------------------------------- + Application::PrivateData::PrivateData(const bool standalone) : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, standalone ? PUGL_WORLD_THREADS : 0x0)), diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 12b10e4b..12640c9e 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 12b10e4bb38da3a88278becdf3c4f31ea1a86f32 +Subproject commit 12640c9e41cd334f05c2a3d075c41db2c5bb7135 From ceaea6080f6a704a0c992c766e28d7cdb4901f11 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 10 Jul 2022 18:00:15 +0100 Subject: [PATCH 449/504] Rework SDL bridge to support audio input Signed-off-by: falkTX --- distrho/src/jackbridge/JackBridge.cpp | 2 +- distrho/src/jackbridge/SDLBridge.hpp | 182 ++++++++++++++++++++------ 2 files changed, 145 insertions(+), 39 deletions(-) diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 8268b8fc..97987052 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -36,7 +36,7 @@ #include "../../extra/LibraryUtils.hpp" // in case JACK fails, we fallback to RtAudio or SDL native API -#if defined(DISTRHO_OS_WASM) +#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_WASM) # include "SDLBridge.hpp" #elif defined(DISTRHO_PROPER_CPP11_SUPPORT) && !defined(DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK) # include "RtAudioBridge.hpp" diff --git a/distrho/src/jackbridge/SDLBridge.hpp b/distrho/src/jackbridge/SDLBridge.hpp index 38faf1d6..332206a7 100644 --- a/distrho/src/jackbridge/SDLBridge.hpp +++ b/distrho/src/jackbridge/SDLBridge.hpp @@ -23,7 +23,12 @@ #include struct SDLBridge { - SDL_AudioDeviceID deviceId = 0; +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + SDL_AudioDeviceID captureDeviceId = 0; +#endif +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + SDL_AudioDeviceID playbackDeviceId = 0; +#endif // SDL information uint bufferSize = 0; @@ -61,37 +66,97 @@ struct SDLBridge { bool open(const char* const clientName) { - SDL_AudioSpec requested, received; + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + SDL_InitSubSystem(SDL_INIT_AUDIO); + + SDL_AudioSpec requested; std::memset(&requested, 0, sizeof(requested)); requested.format = AUDIO_F32SYS; - requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS; requested.freq = 48000; requested.samples = 512; - requested.callback = SDLCallback; requested.userdata = this; SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName); - // SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, ); SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "2"); + #endif + + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure"); + requested.channels = DISTRHO_PLUGIN_NUM_INPUTS; + requested.callback = AudioInputCallback; - /* - deviceId = SDL_OpenAudioDevice("PulseAudio", 0, - &requested, &received, - SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); - */ - deviceId = SDL_OpenAudio(&requested, &received) == 0 ? 1 : 0; - DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); + SDL_AudioSpec receivedCapture; + captureDeviceId = SDL_OpenAudioDevice(nullptr, 1, &requested, &receivedCapture, + SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); + if (captureDeviceId == 0) + { + d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError()); + return false; + } + + if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS) + { + SDL_CloseAudioDevice(captureDeviceId); + captureDeviceId = 0; + d_stderr2("Invalid or missing audio input channels"); + return false; + } + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + SDL_AudioSpec receivedPlayback; + SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback"); + requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS; + requested.callback = AudioOutputCallback; - if (received.channels != DISTRHO_PLUGIN_NUM_OUTPUTS) + playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback, + SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); + if (playbackDeviceId == 0) { - SDL_CloseAudioDevice(deviceId); - deviceId = 0; + d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError()); + return false; + } + + if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS) + { + SDL_CloseAudioDevice(playbackDeviceId); + playbackDeviceId = 0; d_stderr2("Invalid or missing audio output channels"); return false; } + #endif + + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + // if using both input and output, make sure they match + if (receivedCapture.samples != receivedPlayback.samples) + { + SDL_CloseAudioDevice(captureDeviceId); + SDL_CloseAudioDevice(playbackDeviceId); + captureDeviceId = playbackDeviceId = 0; + d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedCapture.samples); + return false; + } + if (receivedCapture.freq != receivedPlayback.freq) + { + SDL_CloseAudioDevice(captureDeviceId); + SDL_CloseAudioDevice(playbackDeviceId); + captureDeviceId = playbackDeviceId = 0; + d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedCapture.freq); + return false; + } + bufferSize = receivedCapture.samples; + sampleRate = receivedCapture.freq; + #elif DISTRHO_PLUGIN_NUM_INPUTS > 0 + bufferSize = receivedCapture.samples; + sampleRate = receivedCapture.freq; + #elif DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + bufferSize = receivedPlayback.samples; + sampleRate = receivedPlayback.freq; + #else + d_stderr2("SDL without audio, unsupported for now"); + return false; + #endif - bufferSize = received.samples; - sampleRate = received.freq; #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; @@ -108,11 +173,16 @@ struct SDLBridge { bool close() { - DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); - - // SDL_CloseAudioDevice(deviceId); - SDL_CloseAudio(); - deviceId = 0; +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); + SDL_CloseAudioDevice(captureDeviceId); + captureDeviceId = 0; +#endif +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); + SDL_CloseAudioDevice(playbackDeviceId); + playbackDeviceId = 0; +#endif #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 delete[] audioBufferStorage; @@ -124,17 +194,27 @@ struct SDLBridge { bool activate() { - DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); - - SDL_PauseAudioDevice(deviceId, 0); +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); + SDL_PauseAudioDevice(captureDeviceId, 0); +#endif +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); + SDL_PauseAudioDevice(playbackDeviceId, 0); +#endif return true; } bool deactivate() { - DISTRHO_SAFE_ASSERT_RETURN(deviceId != 0, false); - - SDL_PauseAudioDevice(deviceId, 1); +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); + SDL_PauseAudioDevice(captureDeviceId, 1); +#endif +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); + SDL_PauseAudioDevice(playbackDeviceId, 1); +#endif return true; } @@ -184,7 +264,8 @@ struct SDLBridge { return nullptr; } - static void SDLCallback(void* const userData, uchar* const stream, const int len) +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + static void AudioInputCallback(void* const userData, uchar* const stream, const int len) { SDLBridge* const self = (SDLBridge*)userData; @@ -192,29 +273,54 @@ struct SDLBridge { DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(len > 0,); - float* const fstream = (float*)stream; + const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); + + const float* const fstream = (const float*)stream; + + for (uint i=0; iaudioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i]; + } + + #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 + // if there are no outputs, run process callback now + if (self->jackProcessCallback != nullptr) + self->jackProcessCallback(numFrames, self->jackProcessArg); + #endif + } +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + static void AudioOutputCallback(void* const userData, uchar* const stream, const int len) + { + SDLBridge* const self = (SDLBridge*)userData; + + // safety checks + DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(len > 0,); -// #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 if (self->jackProcessCallback == nullptr) -// #endif { - std::memset(fstream, 0, len); + std::memset(stream, 0, len); return; } -// #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - const uint numFrames = static_cast(static_cast(len) / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); + const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); + + float* const fstream = (float*)stream; self->jackProcessCallback(numFrames, self->jackProcessArg); for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) { for (uint j=0; j < numFrames; ++j) - fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS+i][j]; + fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j]; } -// #endif } - +#endif }; #endif From f1e157a03c40637c9055ddf82e7b0543b386c48a Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 10 Jul 2022 18:45:49 +0100 Subject: [PATCH 450/504] SDL bridge: Use input instead of output for process sync Signed-off-by: falkTX --- distrho/src/jackbridge/SDLBridge.hpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/distrho/src/jackbridge/SDLBridge.hpp b/distrho/src/jackbridge/SDLBridge.hpp index 332206a7..76cf4960 100644 --- a/distrho/src/jackbridge/SDLBridge.hpp +++ b/distrho/src/jackbridge/SDLBridge.hpp @@ -77,7 +77,7 @@ struct SDLBridge { requested.userdata = this; SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName); - SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "2"); + // SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "1"); #endif #if DISTRHO_PLUGIN_NUM_INPUTS > 0 @@ -273,6 +273,9 @@ struct SDLBridge { DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(len > 0,); + if (self->jackProcessCallback == nullptr) + return; + const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS); DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); @@ -284,11 +287,7 @@ struct SDLBridge { self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i]; } - #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 - // if there are no outputs, run process callback now - if (self->jackProcessCallback != nullptr) - self->jackProcessCallback(numFrames, self->jackProcessArg); - #endif + self->jackProcessCallback(numFrames, self->jackProcessArg); } #endif @@ -310,9 +309,12 @@ struct SDLBridge { const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); - float* const fstream = (float*)stream; - + #if DISTRHO_PLUGIN_NUM_INPUTS == 0 + // if there are no inputs, run process callback now self->jackProcessCallback(numFrames, self->jackProcessArg); + #endif + + float* const fstream = (float*)stream; for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) { From ec02b889233b1145db74fda7fe1565d645f42d02 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 04:22:40 +0100 Subject: [PATCH 451/504] Rework native audio standalone fallback code, add full wasm stuff Signed-off-by: falkTX --- Makefile.base.mk | 15 +- Makefile.plugins.mk | 13 + distrho/extra/RingBuffer.hpp | 25 +- distrho/src/jackbridge/JackBridge.cpp | 411 ++++++++---------- distrho/src/jackbridge/NativeBridge.hpp | 213 +++++++++ distrho/src/jackbridge/RtAudioBridge.hpp | 174 +++----- .../{SDLBridge.hpp => SDL2Bridge.hpp} | 178 ++------ distrho/src/jackbridge/WebBridge.hpp | 354 +++++++++++++++ 8 files changed, 887 insertions(+), 496 deletions(-) create mode 100644 distrho/src/jackbridge/NativeBridge.hpp rename distrho/src/jackbridge/{SDLBridge.hpp => SDL2Bridge.hpp} (62%) create mode 100644 distrho/src/jackbridge/WebBridge.hpp diff --git a/Makefile.base.mk b/Makefile.base.mk index 2dab4bac..696cdb69 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -324,9 +324,9 @@ endif HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true) -ifeq ($(SKIP_RTAUDIO_FALLBACK),true) -CXXFLAGS += -DDPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK -else +ifneq ($(SKIP_NATIVE_AUDIO_FALLBACK),true) +ifneq ($(SKIP_RTAUDIO_FALLBACK),true) + ifeq ($(MACOS),true) HAVE_RTAUDIO = true else ifeq ($(WINDOWS),true) @@ -334,11 +334,14 @@ HAVE_RTAUDIO = true else HAVE_ALSA = $(shell $(PKG_CONFIG) --exists alsa && echo true) HAVE_PULSEAUDIO = $(shell $(PKG_CONFIG) --exists libpulse-simple && echo true) +HAVE_SDL2 = $(shell $(PKG_CONFIG) --exists sdl2 && echo true) ifeq ($(HAVE_ALSA),true) HAVE_RTAUDIO = true else ifeq ($(HAVE_PULSEAUDIO),true) HAVE_RTAUDIO = true endif +endif + endif endif @@ -466,6 +469,11 @@ PULSEAUDIO_FLAGS = $(shell $(PKG_CONFIG) --cflags libpulse-simple) PULSEAUDIO_LIBS = $(shell $(PKG_CONFIG) --libs libpulse-simple) endif +ifeq ($(HAVE_SDL2),true) +SDL2_FLAGS = $(shell $(PKG_CONFIG) --cflags sdl2) +SDL2_LIBS = $(shell $(PKG_CONFIG) --libs sdl2) +endif + ifeq ($(HAVE_JACK),true) ifeq ($(STATIC_BUILD),true) JACK_FLAGS = $(shell $(PKG_CONFIG) --cflags jack) @@ -627,6 +635,7 @@ features: $(call print_available,HAVE_OPENGL) $(call print_available,HAVE_PULSEAUDIO) $(call print_available,HAVE_RTAUDIO) + $(call print_available,HAVE_SDL2) $(call print_available,HAVE_STUB) $(call print_available,HAVE_VULKAN) $(call print_available,HAVE_X11) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 99fb613a..c848d9d7 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -50,6 +50,14 @@ ifeq ($(HAVE_PULSEAUDIO),true) BASE_FLAGS += -DHAVE_PULSEAUDIO endif +ifeq ($(HAVE_RTAUDIO),true) +BASE_FLAGS += -DHAVE_RTAUDIO +endif + +ifeq ($(HAVE_SDL2),true) +BASE_FLAGS += -DHAVE_SDL2 +endif + # always needed ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) ifneq ($(STATIC_BUILD),true) @@ -94,6 +102,11 @@ JACK_LIBS += -lpthread endif endif +ifeq ($(HAVE_SDL2),true) +JACK_FLAGS += $(SDL2_FLAGS) +JACK_LIBS += $(SDL2_LIBS) +endif + endif # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/extra/RingBuffer.hpp b/distrho/extra/RingBuffer.hpp index 9e80a6be..fc3f0136 100644 --- a/distrho/extra/RingBuffer.hpp +++ b/distrho/extra/RingBuffer.hpp @@ -203,11 +203,23 @@ public: /* * Get the size of the data available to read. */ - uint32_t getAvailableDataSize() const noexcept + uint32_t getReadableDataSize() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0); - const uint32_t wrap((buffer->tail > buffer->wrtn) ? 0 : buffer->size); + const uint32_t wrap = buffer->head > buffer->tail ? 0 : buffer->size; + + return wrap + buffer->head - buffer->tail; + } + + /* + * Get the size of the data available to write. + */ + uint32_t getWritableDataSize() const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0); + + const uint32_t wrap = (buffer->tail > buffer->wrtn) ? 0 : buffer->size; return wrap + buffer->tail - buffer->wrtn; } @@ -724,6 +736,15 @@ public: heapBuffer.size = 0; } + void copyFromAndClearOther(HeapRingBuffer& other) + { + DISTRHO_SAFE_ASSERT_RETURN(other.heapBuffer.size == heapBuffer.size,); + + std::memcpy(&heapBuffer, &other.heapBuffer, sizeof(HeapBuffer) - sizeof(uint8_t*)); + std::memcpy(heapBuffer.buf, other.heapBuffer.buf, sizeof(uint8_t) * heapBuffer.size); + other.clearData(); + } + private: /** The heap buffer used for this class. */ HeapBuffer heapBuffer; diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 97987052..f5fb3229 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -35,16 +35,24 @@ #include #include "../../extra/LibraryUtils.hpp" -// in case JACK fails, we fallback to RtAudio or SDL native API -#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_WASM) -# include "SDLBridge.hpp" -#elif defined(DISTRHO_PROPER_CPP11_SUPPORT) && !defined(DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK) +// in case JACK fails, we fallback to native bridges simulating JACK API +#include "NativeBridge.hpp" + +#if defined(DISTRHO_OS_WASM) +# include "WebBridge.hpp" +#endif + +#if defined(HAVE_RTAUDIO) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 # include "RtAudioBridge.hpp" # ifdef RTAUDIO_API_TYPE # include "rtaudio/RtAudio.cpp" # endif #endif +#if defined(HAVE_SDL2) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 +# include "SDL2Bridge.hpp" +#endif + // ----------------------------------------------------------------------------- extern "C" { @@ -314,14 +322,6 @@ struct JackBridge { jacksym_set_thread_creator set_thread_creator_ptr; #endif - static bool usingRtAudioOrSDL; -#ifdef DISTRHO_OS_WASM - static SDLBridge sdl; -#endif -#ifdef RTAUDIO_API_TYPE - static RtAudioBridge rtAudio; -#endif - JackBridge() : lib(nullptr), get_version_ptr(nullptr), @@ -420,18 +420,21 @@ struct JackBridge { , set_thread_creator_ptr(nullptr) #endif { -# ifdef DISTRHO_OS_WASM + #ifdef DISTRHO_OS_WASM + // never use jack in wasm return; -# endif -# if defined(DISTRHO_OS_MAC) - const char* const filename("libjack.dylib"); -# elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64) - const char* const filename("libjack64.dll"); -# elif defined(DISTRHO_OS_WINDOWS) - const char* const filename("libjack.dll"); -# else - const char* const filename("libjack.so.0"); -# endif + #endif + + #if defined(DISTRHO_OS_MAC) + const char* const filename = "libjack.dylib"; + #elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64) + const char* const filename = "libjack64.dll"; + #elif defined(DISTRHO_OS_WINDOWS) + const char* const filename = "libjack.dll"; + #else + const char* const filename = "libjack.so.0"; + #endif + USE_NAMESPACE_DISTRHO lib = lib_open(filename); @@ -584,13 +587,9 @@ struct JackBridge { DISTRHO_DECLARE_NON_COPYABLE(JackBridge); }; -bool JackBridge::usingRtAudioOrSDL = false; -#ifdef DISTRHO_OS_WASM -SDLBridge JackBridge::sdl; -#endif -#ifdef RTAUDIO_API_TYPE -RtAudioBridge JackBridge::rtAudio; -#endif +static bool usingNativeBridge = false; +static bool usingRealJACK = true; +static NativeBridge* nativeBridge = nullptr; // ----------------------------------------------------------------------------- @@ -842,9 +841,8 @@ void jackbridge_get_version(int* major_ptr, int* minor_ptr, int* micro_ptr, int* #elif defined(JACKBRIDGE_DIRECT) return jack_get_version(major_ptr, minor_ptr, micro_ptr, proto_ptr); #else - if (! JackBridge::usingRtAudioOrSDL) - if (getBridgeInstance().get_version_ptr != nullptr) - return getBridgeInstance().get_version_ptr(major_ptr, minor_ptr, micro_ptr, proto_ptr); + if (usingRealJACK && getBridgeInstance().get_version_ptr != nullptr) + return getBridgeInstance().get_version_ptr(major_ptr, minor_ptr, micro_ptr, proto_ptr); #endif if (major_ptr != nullptr) *major_ptr = 0; @@ -862,15 +860,7 @@ const char* jackbridge_get_version_string() #elif defined(JACKBRIDGE_DIRECT) return jack_get_version_string(); #else -# ifdef DISTRHO_OS_WASM - if (JackBridge::usingRtAudioOrSDL) - return "2"; // SDL_VERSION; -# endif -# ifdef RTAUDIO_API_TYPE - if (JackBridge::usingRtAudioOrSDL) - return RTAUDIO_VERSION; -# endif - if (getBridgeInstance().get_version_string_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().get_version_string_ptr != nullptr) return getBridgeInstance().get_version_string_ptr(); #endif return nullptr; @@ -884,30 +874,40 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options, #elif defined(JACKBRIDGE_DIRECT) return jack_client_open(client_name, static_cast(options), status); #else -# ifdef DISTRHO_OS_WASM - if (JackBridge::sdl.open(client_name)) - { - d_stdout("SDL audio setup ok"); - JackBridge::usingRtAudioOrSDL = true; - return (jack_client_t*)0x1; // return non-null - // unused - (void)options; - } - d_stderr2("SDL audio setup failed"); -# else + #ifndef DISTRHO_OS_WASM if (getBridgeInstance().client_open_ptr != nullptr) if (jack_client_t* const client = getBridgeInstance().client_open_ptr(client_name, static_cast(options), status)) return client; - // TODO -# endif -# ifdef RTAUDIO_API_TYPE - if (JackBridge::rtAudio.open()) - { - d_stdout("JACK setup failed, using RtAudio instead"); - JackBridge::usingRtAudioOrSDL = true; - return (jack_client_t*)0x1; // return non-null - } -# endif + #endif + + static jack_client_t* const kValidClient = (jack_client_t*)0x1; + + // maybe unused + (void)kValidClient; + + usingNativeBridge = true; + usingRealJACK = false; + + #ifdef DISTRHO_OS_WASM + nativeBridge = new WebBridge; + if (nativeBridge->open(client_name)) + return kValidClient; + delete nativeBridge; + #endif + + #if defined(HAVE_RTAUDIO) && defined(RTAUDIO_API_TYPE) + nativeBridge = new RtAudioBridge; + if (nativeBridge->open(client_name)) + return kValidClient; + delete nativeBridge; + #endif + + #if defined(HAVE_SDL2) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + nativeBridge = new SDL2Bridge; + if (nativeBridge->open(client_name)) + return kValidClient; + delete nativeBridge; + #endif #endif if (status != nullptr) *status = JackServerError; @@ -920,15 +920,17 @@ bool jackbridge_client_close(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_client_close(client) == 0); #else - if (JackBridge::usingRtAudioOrSDL) + if (usingNativeBridge) { - JackBridge::usingRtAudioOrSDL = false; -# ifdef DISTRHO_OS_WASM - return JackBridge::sdl.close(); -# endif -# ifdef RTAUDIO_API_TYPE - return JackBridge::rtAudio.close(); -# endif + if (nativeBridge != nullptr) + { + nativeBridge->close(); + delete nativeBridge; + nativeBridge = nullptr; + } + usingNativeBridge = false; + usingRealJACK = true; + return true; } if (getBridgeInstance().client_close_ptr != nullptr) return (getBridgeInstance().client_close_ptr(client) == 0); @@ -944,9 +946,8 @@ int jackbridge_client_name_size() #elif defined(JACKBRIDGE_DIRECT) return jack_client_name_size(); #else - if (! JackBridge::usingRtAudioOrSDL) - if (getBridgeInstance().client_name_size_ptr != nullptr) - return getBridgeInstance().client_name_size_ptr(); + if (usingRealJACK && getBridgeInstance().client_name_size_ptr != nullptr) + return getBridgeInstance().client_name_size_ptr(); #endif return 33; } @@ -957,11 +958,8 @@ const char* jackbridge_get_client_name(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_get_client_name(client); #else - if (JackBridge::usingRtAudioOrSDL) - { - static const char* const name = DISTRHO_PLUGIN_NAME; - return name; - } + if (usingNativeBridge) + return DISTRHO_PLUGIN_NAME; if (getBridgeInstance().get_client_name_ptr != nullptr) return getBridgeInstance().get_client_name_ptr(client); #endif @@ -976,7 +974,7 @@ char* jackbridge_client_get_uuid(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_client_get_uuid(client); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (const jacksym_client_get_uuid func = getBridgeInstance().client_get_uuid_ptr) return func(client); #endif @@ -989,7 +987,7 @@ char* jackbridge_get_uuid_for_client_name(jack_client_t* client, const char* nam #elif defined(JACKBRIDGE_DIRECT) return jack_get_uuid_for_client_name(client, name); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().get_uuid_for_client_name_ptr != nullptr) return getBridgeInstance().get_uuid_for_client_name_ptr(client, name); #endif @@ -1002,7 +1000,7 @@ char* jackbridge_get_client_name_by_uuid(jack_client_t* client, const char* uuid #elif defined(JACKBRIDGE_DIRECT) return jack_get_client_name_by_uuid(client, uuid); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().get_client_name_by_uuid_ptr != nullptr) return getBridgeInstance().get_client_name_by_uuid_ptr(client, uuid); #endif @@ -1017,7 +1015,7 @@ bool jackbridge_uuid_parse(const char* buf, jack_uuid_t* uuid) #elif defined(JACKBRIDGE_DIRECT) return (jack_uuid_parse(buf, uuid) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (const jacksym_uuid_parse func = getBridgeInstance().uuid_parse_ptr) return (func(buf, uuid) == 0); #endif @@ -1030,7 +1028,7 @@ void jackbridge_uuid_unparse(jack_uuid_t uuid, char buf[JACK_UUID_STRING_SIZE]) #elif defined(JACKBRIDGE_DIRECT) jack_uuid_unparse(uuid, buf); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (const jacksym_uuid_unparse func = getBridgeInstance().uuid_unparse_ptr) return func(uuid, buf); #endif @@ -1044,15 +1042,8 @@ bool jackbridge_activate(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_activate(client) == 0); #else - if (JackBridge::usingRtAudioOrSDL) - { -# ifdef DISTRHO_OS_WASM - return JackBridge::sdl.activate(); -# endif -# ifdef RTAUDIO_API_TYPE - return JackBridge::rtAudio.activate(); -# endif - } + if (usingNativeBridge) + return nativeBridge->activate(); if (getBridgeInstance().activate_ptr != nullptr) return (getBridgeInstance().activate_ptr(client) == 0); #endif @@ -1065,15 +1056,8 @@ bool jackbridge_deactivate(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_deactivate(client) == 0); #else - if (JackBridge::usingRtAudioOrSDL) - { -# ifdef DISTRHO_OS_WASM - return JackBridge::sdl.deactivate(); -# endif -# ifdef RTAUDIO_API_TYPE - return JackBridge::rtAudio.deactivate(); -# endif - } + if (usingNativeBridge) + return nativeBridge->deactivate(); if (getBridgeInstance().deactivate_ptr != nullptr) return (getBridgeInstance().deactivate_ptr(client) == 0); #endif @@ -1086,7 +1070,7 @@ bool jackbridge_is_realtime(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_is_realtime(client); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().is_realtime_ptr != nullptr) return getBridgeInstance().is_realtime_ptr(client); #endif @@ -1101,7 +1085,7 @@ bool jackbridge_set_thread_init_callback(jack_client_t* client, JackThreadInitCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_thread_init_callback(client, thread_init_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_thread_init_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_thread_init_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_thread_init(thread_init_callback); @@ -1120,7 +1104,7 @@ void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown #elif defined(JACKBRIDGE_DIRECT) jack_on_shutdown(client, shutdown_callback, arg); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().on_shutdown_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().on_shutdown_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_shutdown(shutdown_callback); @@ -1138,7 +1122,7 @@ void jackbridge_on_info_shutdown(jack_client_t* client, JackInfoShutdownCallback #elif defined(JACKBRIDGE_DIRECT) jack_on_info_shutdown(client, shutdown_callback, arg); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().on_info_shutdown_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().on_info_shutdown_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_info_shutdown(shutdown_callback); @@ -1156,18 +1140,11 @@ bool jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback #elif defined(JACKBRIDGE_DIRECT) return (jack_set_process_callback(client, process_callback, arg) == 0); #else - if (JackBridge::usingRtAudioOrSDL) + if (usingNativeBridge) { -# ifdef DISTRHO_OS_WASM - JackBridge::sdl.jackProcessCallback = process_callback; - JackBridge::sdl.jackProcessArg = arg; + nativeBridge->jackProcessCallback = process_callback; + nativeBridge->jackProcessArg = arg; return true; -# endif -# ifdef RTAUDIO_API_TYPE - JackBridge::rtAudio.jackProcessCallback = process_callback; - JackBridge::rtAudio.jackProcessArg = arg; - return true; -# endif } if (getBridgeInstance().set_process_callback_ptr != nullptr) { @@ -1188,7 +1165,7 @@ bool jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallb #elif defined(JACKBRIDGE_DIRECT) return (jack_set_freewheel_callback(client, freewheel_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_freewheel_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_freewheel_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_freewheel(freewheel_callback); @@ -1207,7 +1184,7 @@ bool jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_buffer_size_callback(client, bufsize_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_buffer_size_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_buffer_size_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_bufsize(bufsize_callback); @@ -1226,7 +1203,7 @@ bool jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_sample_rate_callback(client, srate_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_sample_rate_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_sample_rate_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_srate(srate_callback); @@ -1245,7 +1222,7 @@ bool jackbridge_set_client_registration_callback(jack_client_t* client, JackClie #elif defined(JACKBRIDGE_DIRECT) return (jack_set_client_registration_callback(client, registration_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_client_registration_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_client_registration_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_client_reg(registration_callback); @@ -1264,7 +1241,7 @@ bool jackbridge_set_port_registration_callback(jack_client_t* client, JackPortRe #elif defined(JACKBRIDGE_DIRECT) return (jack_set_port_registration_callback(client, registration_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_port_registration_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_port_registration_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_port_reg(registration_callback); @@ -1283,7 +1260,7 @@ bool jackbridge_set_port_rename_callback(jack_client_t* client, JackPortRenameCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_port_rename_callback(client, rename_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_port_rename_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_port_rename_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_port_rename(rename_callback); @@ -1302,7 +1279,7 @@ bool jackbridge_set_port_connect_callback(jack_client_t* client, JackPortConnect #elif defined(JACKBRIDGE_DIRECT) return (jack_set_port_connect_callback(client, connect_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_port_connect_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_port_connect_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_port_conn(connect_callback); @@ -1321,7 +1298,7 @@ bool jackbridge_set_graph_order_callback(jack_client_t* client, JackGraphOrderCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_graph_order_callback(client, graph_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_graph_order_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_graph_order_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_graph_order(graph_callback); @@ -1340,7 +1317,7 @@ bool jackbridge_set_xrun_callback(jack_client_t* client, JackXRunCallback xrun_c #elif defined(JACKBRIDGE_DIRECT) return (jack_set_xrun_callback(client, xrun_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_xrun_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_xrun_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_xrun(xrun_callback); @@ -1359,7 +1336,7 @@ bool jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback #elif defined(JACKBRIDGE_DIRECT) return (jack_set_latency_callback(client, latency_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_latency_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_latency_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_latency(latency_callback); @@ -1380,7 +1357,7 @@ bool jackbridge_set_freewheel(jack_client_t* client, bool onoff) #elif defined(JACKBRIDGE_DIRECT) return jack_set_freewheel(client, onoff); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().set_freewheel_ptr != nullptr) return getBridgeInstance().set_freewheel_ptr(client, onoff); #endif @@ -1393,7 +1370,7 @@ bool jackbridge_set_buffer_size(jack_client_t* client, jack_nframes_t nframes) #elif defined(JACKBRIDGE_DIRECT) return jack_set_buffer_size(client, nframes); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().set_buffer_size_ptr != nullptr) return getBridgeInstance().set_buffer_size_ptr(client, nframes); #endif @@ -1408,15 +1385,8 @@ jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_get_sample_rate(client); #else - if (JackBridge::usingRtAudioOrSDL) - { -# ifdef DISTRHO_OS_WASM - return JackBridge::sdl.sampleRate; -# endif -# ifdef RTAUDIO_API_TYPE - return JackBridge::rtAudio.sampleRate; -# endif - } + if (usingNativeBridge) + return nativeBridge->sampleRate; if (getBridgeInstance().get_sample_rate_ptr != nullptr) return getBridgeInstance().get_sample_rate_ptr(client); #endif @@ -1429,15 +1399,8 @@ jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_get_buffer_size(client); #else - if (JackBridge::usingRtAudioOrSDL) - { -# ifdef DISTRHO_OS_WASM - return JackBridge::sdl.bufferSize; -# endif -# ifdef RTAUDIO_API_TYPE - return JackBridge::rtAudio.bufferSize; -# endif - } + if (usingNativeBridge) + return nativeBridge->bufferSize; if (getBridgeInstance().get_buffer_size_ptr != nullptr) return getBridgeInstance().get_buffer_size_ptr(client); #endif @@ -1450,7 +1413,7 @@ float jackbridge_cpu_load(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return jack_cpu_load(client); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().cpu_load_ptr != nullptr) return getBridgeInstance().cpu_load_ptr(client); #endif @@ -1465,15 +1428,8 @@ jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_na #elif defined(JACKBRIDGE_DIRECT) return jack_port_register(client, port_name, type, flags, buffer_size); #else - if (JackBridge::usingRtAudioOrSDL) - { -# ifdef DISTRHO_OS_WASM - return JackBridge::sdl.registerPort(type, flags); -# endif -# ifdef RTAUDIO_API_TYPE - return JackBridge::rtAudio.registerPort(type, flags); -# endif - } + if (usingNativeBridge) + return nativeBridge->registerPort(type, flags); if (getBridgeInstance().port_register_ptr != nullptr) return getBridgeInstance().port_register_ptr(client, port_name, type, static_cast(flags), @@ -1488,7 +1444,7 @@ bool jackbridge_port_unregister(jack_client_t* client, jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_unregister(client, port) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_unregister_ptr != nullptr) return (getBridgeInstance().port_unregister_ptr(client, port) == 0); #endif @@ -1501,15 +1457,8 @@ void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes) #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_buffer(port, nframes); #else - if (JackBridge::usingRtAudioOrSDL) - { -# ifdef DISTRHO_OS_WASM - return JackBridge::sdl.getPortBuffer(port); -# endif -# ifdef RTAUDIO_API_TYPE - return JackBridge::rtAudio.getPortBuffer(port); -# endif - } + if (usingNativeBridge) + return nativeBridge->getPortBuffer(port); if (getBridgeInstance().port_get_buffer_ptr != nullptr) return getBridgeInstance().port_get_buffer_ptr(port, nframes); #endif @@ -1524,7 +1473,7 @@ const char* jackbridge_port_name(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_name(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_name_ptr != nullptr) return getBridgeInstance().port_name_ptr(port); #endif @@ -1537,7 +1486,7 @@ jack_uuid_t jackbridge_port_uuid(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_uuid(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_uuid_ptr != nullptr) return getBridgeInstance().port_uuid_ptr(port); #endif @@ -1550,7 +1499,7 @@ const char* jackbridge_port_short_name(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_short_name(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_short_name_ptr != nullptr) return getBridgeInstance().port_short_name_ptr(port); #endif @@ -1563,7 +1512,7 @@ int jackbridge_port_flags(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_flags(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_flags_ptr != nullptr) return getBridgeInstance().port_flags_ptr(port); #endif @@ -1576,7 +1525,7 @@ const char* jackbridge_port_type(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_type(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_type_ptr != nullptr) return getBridgeInstance().port_type_ptr(port); #endif @@ -1589,7 +1538,7 @@ bool jackbridge_port_is_mine(const jack_client_t* client, const jack_port_t* por #elif defined(JACKBRIDGE_DIRECT) return jack_port_is_mine(client, port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_is_mine_ptr != nullptr) return getBridgeInstance().port_is_mine_ptr(client, port); #endif @@ -1602,7 +1551,7 @@ int jackbridge_port_connected(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_connected(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_connected_ptr != nullptr) return getBridgeInstance().port_connected_ptr(port); #endif @@ -1615,7 +1564,7 @@ bool jackbridge_port_connected_to(const jack_port_t* port, const char* port_name #elif defined(JACKBRIDGE_DIRECT) return jack_port_connected_to(port, port_name); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_connected_to_ptr != nullptr) return getBridgeInstance().port_connected_to_ptr(port, port_name); #endif @@ -1628,7 +1577,7 @@ const char** jackbridge_port_get_connections(const jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_connections(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_get_connections_ptr != nullptr) return getBridgeInstance().port_get_connections_ptr(port); #endif @@ -1641,7 +1590,7 @@ const char** jackbridge_port_get_all_connections(const jack_client_t* client, co #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_all_connections(client, port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_get_all_connections_ptr != nullptr) return getBridgeInstance().port_get_all_connections_ptr(client, port); #endif @@ -1656,7 +1605,7 @@ bool jackbridge_port_rename(jack_client_t* client, jack_port_t* port, const char #elif defined(JACKBRIDGE_DIRECT) return (jack_port_rename(client, port, port_name) == 0); #else - if (JackBridge::usingRtAudioOrSDL) + if (usingNativeBridge) return false; // Try new API first if (getBridgeInstance().port_rename_ptr != nullptr) @@ -1674,7 +1623,7 @@ bool jackbridge_port_set_alias(jack_port_t* port, const char* alias) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_set_alias(port, alias) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_set_alias_ptr != nullptr) return (getBridgeInstance().port_set_alias_ptr(port, alias) == 0); #endif @@ -1687,7 +1636,7 @@ bool jackbridge_port_unset_alias(jack_port_t* port, const char* alias) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_unset_alias(port, alias) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_unset_alias_ptr != nullptr) return (getBridgeInstance().port_unset_alias_ptr(port, alias) == 0); #endif @@ -1700,7 +1649,7 @@ int jackbridge_port_get_aliases(const jack_port_t* port, char* const aliases[2]) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_get_aliases(port, aliases) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_get_aliases_ptr != nullptr) return getBridgeInstance().port_get_aliases_ptr(port, aliases); #endif @@ -1715,7 +1664,7 @@ bool jackbridge_port_request_monitor(jack_port_t* port, bool onoff) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_request_monitor(port, onoff) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_request_monitor_ptr != nullptr) return (getBridgeInstance().port_request_monitor_ptr(port, onoff) == 0); #endif @@ -1728,7 +1677,7 @@ bool jackbridge_port_request_monitor_by_name(jack_client_t* client, const char* #elif defined(JACKBRIDGE_DIRECT) return (jack_port_request_monitor_by_name(client, port_name, onoff) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_request_monitor_by_name_ptr != nullptr) return (getBridgeInstance().port_request_monitor_by_name_ptr(client, port_name, onoff) == 0); #endif @@ -1741,7 +1690,7 @@ bool jackbridge_port_ensure_monitor(jack_port_t* port, bool onoff) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_ensure_monitor(port, onoff) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_ensure_monitor_ptr != nullptr) return (getBridgeInstance().port_ensure_monitor_ptr(port, onoff) == 0); #endif @@ -1754,7 +1703,7 @@ bool jackbridge_port_monitoring_input(jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return jack_port_monitoring_input(port); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_monitoring_input_ptr != nullptr) return getBridgeInstance().port_monitoring_input_ptr(port); #endif @@ -1769,7 +1718,7 @@ bool jackbridge_connect(jack_client_t* client, const char* source_port, const ch #elif defined(JACKBRIDGE_DIRECT) return (jack_connect(client, source_port, destination_port) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().connect_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().connect_ptr != nullptr) { const int ret = getBridgeInstance().connect_ptr(client, source_port, destination_port); return ret == 0 || ret == EEXIST; @@ -1784,7 +1733,7 @@ bool jackbridge_disconnect(jack_client_t* client, const char* source_port, const #elif defined(JACKBRIDGE_DIRECT) return (jack_disconnect(client, source_port, destination_port) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().disconnect_ptr != nullptr) return (getBridgeInstance().disconnect_ptr(client, source_port, destination_port) == 0); #endif @@ -1797,7 +1746,7 @@ bool jackbridge_port_disconnect(jack_client_t* client, jack_port_t* port) #elif defined(JACKBRIDGE_DIRECT) return (jack_port_disconnect(client, port) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_disconnect_ptr != nullptr) return (getBridgeInstance().port_disconnect_ptr(client, port) == 0); #endif @@ -1812,7 +1761,7 @@ int jackbridge_port_name_size() #elif defined(JACKBRIDGE_DIRECT) return jack_port_name_size(); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_name_size_ptr != nullptr) return getBridgeInstance().port_name_size_ptr(); #endif @@ -1825,7 +1774,7 @@ int jackbridge_port_type_size() #elif defined(JACKBRIDGE_DIRECT) return jack_port_type_size(); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_type_size_ptr != nullptr) return getBridgeInstance().port_type_size_ptr(); #endif @@ -1838,7 +1787,7 @@ uint32_t jackbridge_port_type_get_buffer_size(jack_client_t* client, const char* #elif defined(JACKBRIDGE_DIRECT) return static_cast(jack_port_type_get_buffer_size(client, port_type)); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_type_get_buffer_size_ptr != nullptr) return static_cast(getBridgeInstance().port_type_get_buffer_size_ptr(client, port_type)); #endif @@ -1853,7 +1802,7 @@ void jackbridge_port_get_latency_range(jack_port_t* port, uint32_t mode, jack_la #elif defined(JACKBRIDGE_DIRECT) return jack_port_get_latency_range(port, static_cast(mode), range); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_get_latency_range_ptr != nullptr) return getBridgeInstance().port_get_latency_range_ptr(port, static_cast(mode), @@ -1869,7 +1818,7 @@ void jackbridge_port_set_latency_range(jack_port_t* port, uint32_t mode, jack_la #elif defined(JACKBRIDGE_DIRECT) jack_port_set_latency_range(port, static_cast(mode), range); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_set_latency_range_ptr != nullptr) getBridgeInstance().port_set_latency_range_ptr(port, static_cast(mode), @@ -1883,7 +1832,7 @@ bool jackbridge_recompute_total_latencies(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_recompute_total_latencies(client) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().recompute_total_latencies_ptr != nullptr) return (getBridgeInstance().recompute_total_latencies_ptr(client) == 0); #endif @@ -1898,7 +1847,7 @@ const char** jackbridge_get_ports(jack_client_t* client, const char* port_name_p #elif defined(JACKBRIDGE_DIRECT) return jack_get_ports(client, port_name_pattern, type_name_pattern, flags); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().get_ports_ptr != nullptr) return getBridgeInstance().get_ports_ptr(client, port_name_pattern, type_name_pattern, static_cast(flags)); @@ -1912,7 +1861,7 @@ jack_port_t* jackbridge_port_by_name(jack_client_t* client, const char* port_nam #elif defined(JACKBRIDGE_DIRECT) return jack_port_by_name(client, port_name); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_by_name_ptr != nullptr) return getBridgeInstance().port_by_name_ptr(client, port_name); #endif @@ -1925,7 +1874,7 @@ jack_port_t* jackbridge_port_by_id(jack_client_t* client, jack_port_id_t port_id #elif defined(JACKBRIDGE_DIRECT) return jack_port_by_id(client, port_id); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().port_by_id_ptr != nullptr) return getBridgeInstance().port_by_id_ptr(client, port_id); #endif @@ -1940,7 +1889,7 @@ void jackbridge_free(void* ptr) #elif defined(JACKBRIDGE_DIRECT) return jack_free(ptr); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().free_ptr != nullptr) return getBridgeInstance().free_ptr(ptr); @@ -1957,9 +1906,10 @@ uint32_t jackbridge_midi_get_event_count(void* port_buffer) #elif defined(JACKBRIDGE_DIRECT) return jack_midi_get_event_count(port_buffer); #else - if (! JackBridge::usingRtAudioOrSDL) - if (getBridgeInstance().midi_get_event_count_ptr != nullptr) - return getBridgeInstance().midi_get_event_count_ptr(port_buffer); + if (usingNativeBridge) + return nativeBridge->getEventCount(); + if (getBridgeInstance().midi_get_event_count_ptr != nullptr) + return getBridgeInstance().midi_get_event_count_ptr(port_buffer); #endif return 0; } @@ -1970,9 +1920,10 @@ bool jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint #elif defined(JACKBRIDGE_DIRECT) return (jack_midi_event_get(event, port_buffer, event_index) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) - if (getBridgeInstance().midi_event_get_ptr != nullptr) - return (getBridgeInstance().midi_event_get_ptr(event, port_buffer, event_index) == 0); + if (usingNativeBridge) + return nativeBridge->getEvent(event); + if (getBridgeInstance().midi_event_get_ptr != nullptr) + return (getBridgeInstance().midi_event_get_ptr(event, port_buffer, event_index) == 0); #endif return false; } @@ -1983,9 +1934,10 @@ void jackbridge_midi_clear_buffer(void* port_buffer) #elif defined(JACKBRIDGE_DIRECT) jack_midi_clear_buffer(port_buffer); #else - if (! JackBridge::usingRtAudioOrSDL) - if (getBridgeInstance().midi_clear_buffer_ptr != nullptr) - getBridgeInstance().midi_clear_buffer_ptr(port_buffer); + if (usingNativeBridge) + return nativeBridge->clearEventBuffer(); + if (getBridgeInstance().midi_clear_buffer_ptr != nullptr) + getBridgeInstance().midi_clear_buffer_ptr(port_buffer); #endif } @@ -1995,9 +1947,10 @@ bool jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const j #elif defined(JACKBRIDGE_DIRECT) return (jack_midi_event_write(port_buffer, time, data, data_size) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) - if (getBridgeInstance().midi_event_write_ptr != nullptr) - return (getBridgeInstance().midi_event_write_ptr(port_buffer, time, data, data_size) == 0); + if (usingNativeBridge) + return nativeBridge->writeEvent(time, data, data_size); + if (getBridgeInstance().midi_event_write_ptr != nullptr) + return (getBridgeInstance().midi_event_write_ptr(port_buffer, time, data, data_size) == 0); #endif return false; } @@ -2008,7 +1961,7 @@ jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_ #elif defined(JACKBRIDGE_DIRECT) return jack_midi_event_reserve(port_buffer, time, data_size); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().midi_event_reserve_ptr != nullptr) return getBridgeInstance().midi_event_reserve_ptr(port_buffer, time, data_size); #endif @@ -2023,7 +1976,7 @@ bool jackbridge_release_timebase(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_release_timebase(client) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().release_timebase_ptr != nullptr) return (getBridgeInstance().release_timebase_ptr(client) == 0); #endif @@ -2036,7 +1989,7 @@ bool jackbridge_set_sync_callback(jack_client_t* client, JackSyncCallback sync_c #elif defined(JACKBRIDGE_DIRECT) return (jack_set_sync_callback(client, sync_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_sync_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_sync_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_sync(sync_callback); @@ -2055,7 +2008,7 @@ bool jackbridge_set_sync_timeout(jack_client_t* client, jack_time_t timeout) #elif defined(JACKBRIDGE_DIRECT) return (jack_set_sync_timeout(client, timeout) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().set_sync_timeout_ptr != nullptr) return (getBridgeInstance().set_sync_timeout_ptr(client, timeout) == 0); #endif @@ -2068,7 +2021,7 @@ bool jackbridge_set_timebase_callback(jack_client_t* client, bool conditional, J #elif defined(JACKBRIDGE_DIRECT) return (jack_set_timebase_callback(client, conditional, timebase_callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_timebase_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_timebase_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_timebase(timebase_callback); @@ -2087,7 +2040,7 @@ bool jackbridge_transport_locate(jack_client_t* client, jack_nframes_t frame) #elif defined(JACKBRIDGE_DIRECT) return (jack_transport_locate(client, frame) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().transport_locate_ptr != nullptr) return (getBridgeInstance().transport_locate_ptr(client, frame) == 0); #endif @@ -2100,7 +2053,7 @@ uint32_t jackbridge_transport_query(const jack_client_t* client, jack_position_t #elif defined(JACKBRIDGE_DIRECT) return jack_transport_query(client, pos); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().transport_query_ptr != nullptr) return getBridgeInstance().transport_query_ptr(client, pos); #endif @@ -2120,7 +2073,7 @@ jack_nframes_t jackbridge_get_current_transport_frame(const jack_client_t* clien #elif defined(JACKBRIDGE_DIRECT) return jack_get_current_transport_frame(client); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().get_current_transport_frame_ptr != nullptr) return getBridgeInstance().get_current_transport_frame_ptr(client); #endif @@ -2133,7 +2086,7 @@ bool jackbridge_transport_reposition(jack_client_t* client, const jack_position_ #elif defined(JACKBRIDGE_DIRECT) return (jack_transport_reposition(client, pos) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().transport_reposition_ptr != nullptr) return (getBridgeInstance().transport_reposition_ptr(client, pos) == 0); #endif @@ -2146,7 +2099,7 @@ void jackbridge_transport_start(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) jack_transport_start(client); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().transport_start_ptr != nullptr) getBridgeInstance().transport_start_ptr(client); #endif @@ -2158,7 +2111,7 @@ void jackbridge_transport_stop(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) jack_transport_stop(client); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().transport_stop_ptr != nullptr) getBridgeInstance().transport_stop_ptr(client); #endif @@ -2172,7 +2125,7 @@ bool jackbridge_set_property(jack_client_t* client, jack_uuid_t subject, const c #elif defined(JACKBRIDGE_DIRECT) return (jack_set_property(client, subject, key, value, type) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().set_property_ptr != nullptr) return (getBridgeInstance().set_property_ptr(client, subject, key, value, type) == 0); #endif @@ -2185,7 +2138,7 @@ bool jackbridge_get_property(jack_uuid_t subject, const char* key, char** value, #elif defined(JACKBRIDGE_DIRECT) return (jack_get_property(subject, key, value, type) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().get_property_ptr != nullptr) return (getBridgeInstance().get_property_ptr(subject, key, value, type) == 0); #endif @@ -2198,7 +2151,7 @@ void jackbridge_free_description(jack_description_t* desc, bool free_description #elif defined(JACKBRIDGE_DIRECT) jack_free_description(desc, free_description_itself); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().free_description_ptr != nullptr) getBridgeInstance().free_description_ptr(desc, free_description_itself); #endif @@ -2210,7 +2163,7 @@ bool jackbridge_get_properties(jack_uuid_t subject, jack_description_t* desc) #elif defined(JACKBRIDGE_DIRECT) return (jack_get_properties(subject, desc) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().get_properties_ptr != nullptr) return (getBridgeInstance().get_properties_ptr(subject, desc) == 0); #endif @@ -2223,7 +2176,7 @@ bool jackbridge_get_all_properties(jack_description_t** descs) #elif defined(JACKBRIDGE_DIRECT) return (jack_get_all_properties(descs) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().get_all_properties_ptr != nullptr) return (getBridgeInstance().get_all_properties_ptr(descs) == 0); #endif @@ -2236,7 +2189,7 @@ bool jackbridge_remove_property(jack_client_t* client, jack_uuid_t subject, cons #elif defined(JACKBRIDGE_DIRECT) return (jack_remove_property(client, subject, key) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().remove_property_ptr != nullptr) return (getBridgeInstance().remove_property_ptr(client, subject, key) == 0); #endif @@ -2249,7 +2202,7 @@ int jackbridge_remove_properties(jack_client_t* client, jack_uuid_t subject) #elif defined(JACKBRIDGE_DIRECT) return jack_remove_properties(client, subject); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().remove_properties_ptr != nullptr) return getBridgeInstance().remove_properties_ptr(client, subject); #endif @@ -2262,7 +2215,7 @@ bool jackbridge_remove_all_properties(jack_client_t* client) #elif defined(JACKBRIDGE_DIRECT) return (jack_remove_all_properties(client) == 0); #else - if (! JackBridge::usingRtAudioOrSDL) + if (usingRealJACK) if (getBridgeInstance().remove_all_properties_ptr != nullptr) return (getBridgeInstance().remove_all_properties_ptr(client) == 0); #endif @@ -2275,7 +2228,7 @@ bool jackbridge_set_property_change_callback(jack_client_t* client, JackProperty #elif defined(JACKBRIDGE_DIRECT) return (jack_set_property_change_callback(client, callback, arg) == 0); #else - if (! JackBridge::usingRtAudioOrSDL && getBridgeInstance().set_property_change_callback_ptr != nullptr) + if (usingRealJACK && getBridgeInstance().set_property_change_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_prop_change(callback); diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp new file mode 100644 index 00000000..3b1d4a79 --- /dev/null +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -0,0 +1,213 @@ +/* + * Native Bridge for DPF + * Copyright (C) 2021-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef NATIVE_BRIDGE_HPP_INCLUDED +#define NATIVE_BRIDGE_HPP_INCLUDED + +#include "JackBridge.hpp" + +#include "../../extra/RingBuffer.hpp" + +using DISTRHO_NAMESPACE::HeapRingBuffer; + +struct NativeBridge { + // Current status information + uint bufferSize = 0; + uint sampleRate = 0; + + // Port caching information + uint numAudioIns = 0; + uint numAudioOuts = 0; + uint numMidiIns = 0; + uint numMidiOuts = 0; + + // JACK callbacks + JackProcessCallback jackProcessCallback = nullptr; + void* jackProcessArg = nullptr; + + // Runtime buffers + enum PortMask { + kPortMaskAudio = 0x1000, + kPortMaskMIDI = 0x2000, + kPortMaskInput = 0x4000, + kPortMaskOutput = 0x8000, + kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, + kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, + }; +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS] = {}; + float* audioBufferStorage = nullptr; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static constexpr const uint32_t kMaxMIDIInputMessageSize = 3; + uint8_t midiDataStorage[kMaxMIDIInputMessageSize]; + HeapRingBuffer midiInBufferCurrent; + HeapRingBuffer midiInBufferPending; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + HeapRingBuffer midiOutBuffer; +#endif + + virtual ~NativeBridge() {} + virtual bool open(const char* const clientName) = 0; + virtual bool close() = 0; + virtual bool activate() = 0; + virtual bool deactivate() = 0; + + uint32_t getEventCount() + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + // NOTE: this function is only called once per run + midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending); + return midiInBufferCurrent.getReadableDataSize() / (kMaxMIDIInputMessageSize + 1u); + #else + return 0; + #endif + } + + bool getEvent(jack_midi_event_t* const event) + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + // NOTE: this function is called for all events in index succession + if (midiInBufferCurrent.getReadableDataSize() >= (kMaxMIDIInputMessageSize + 1u)) + { + event->time = 0; // TODO + event->size = midiInBufferCurrent.readByte(); + event->buffer = midiDataStorage; + return midiInBufferCurrent.readCustomData(midiDataStorage, kMaxMIDIInputMessageSize); + } + #endif + return false; + } + + void clearEventBuffer() + { + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + midiOutBuffer.clearData(); + #endif + } + + bool writeEvent(const jack_nframes_t time, const jack_midi_data_t* const data, const uint32_t size) + { + if (size > 3) + return false; + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (midiOutBuffer.writeByte(size) && midiOutBuffer.writeCustomData(data, size)) + { + bool fail = false; + // align + switch (size) + { + case 1: fail |= !midiOutBuffer.writeByte(0); + // fall-through + case 2: fail |= !midiOutBuffer.writeByte(0); + } + fail |= !midiOutBuffer.writeUInt(time); + midiOutBuffer.commitWrite(); + return !fail; + } + midiOutBuffer.commitWrite(); + #endif + return false; + } + + void allocBuffers() + { + DISTRHO_SAFE_ASSERT_RETURN(bufferSize != 0,); + + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; + + for (uint i=0; i 0 + std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS); + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512); + midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512); + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + midiOutBuffer.createBuffer(2048); + #endif + } + + void freeBuffers() + { + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + delete[] audioBufferStorage; + audioBufferStorage = nullptr; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + midiInBufferCurrent.deleteBuffer(); + midiInBufferPending.deleteBuffer(); + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + midiOutBuffer.deleteBuffer(); + #endif + } + + jack_port_t* registerPort(const char* const type, const ulong flags) + { + bool isAudio, isInput; + + /**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) + isAudio = true; + else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) + isAudio = false; + else + return nullptr; + + /**/ if (flags & JackPortIsInput) + isInput = true; + else if (flags & JackPortIsOutput) + isInput = false; + else + return nullptr; + + const uintptr_t ret = (isAudio ? kPortMaskAudio : kPortMaskMIDI) + | (isInput ? kPortMaskInput : kPortMaskOutput); + + return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++) + : (isInput ? numMidiIns++ : numMidiOuts++))); + } + + void* getPortBuffer(jack_port_t* const port) + { + const uintptr_t portMask = (uintptr_t)port; + DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr); + + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + if (portMask & kPortMaskAudio) + return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)]; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if ((portMask & kPortMaskInputMIDI) == kPortMaskInputMIDI) + return (void*)0x1; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if ((portMask & kPortMaskOutputMIDI) == kPortMaskOutputMIDI) + return (void*)0x2; + #endif + + return nullptr; + } +}; + +#endif // NATIVE_BRIDGE_HPP_INCLUDED diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index 6f2faa8b..146ed149 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -1,5 +1,5 @@ /* - * RtAudioBridge for DPF + * RtAudio Bridge for DPF * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with @@ -14,10 +14,14 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef RTAUDIOBRIDGE_HPP_INCLUDED -#define RTAUDIOBRIDGE_HPP_INCLUDED +#ifndef RTAUDIO_BRIDGE_HPP_INCLUDED +#define RTAUDIO_BRIDGE_HPP_INCLUDED -#include "JackBridge.hpp" +#include "NativeBridge.hpp" + +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0 +# error RtAudio without audio does not make sense +#endif #if defined(DISTRHO_OS_MAC) # define __MACOSX_CORE__ @@ -37,50 +41,20 @@ # define Point CorePoint /* fix conflict between DGL and macOS Point name */ # include "rtaudio/RtAudio.h" # undef Point -# include "../../extra/RingBuffer.hpp" # include "../../extra/ScopedPointer.hpp" -using DISTRHO_NAMESPACE::HeapRingBuffer; using DISTRHO_NAMESPACE::ScopedPointer; -struct RtAudioBridge { +struct RtAudioBridge : NativeBridge { // pointer to RtAudio instance ScopedPointer handle; - // RtAudio information - uint bufferSize = 0; - uint sampleRate = 0; - - // Port caching information - uint numAudioIns = 0; - uint numAudioOuts = 0; - uint numMidiIns = 0; - uint numMidiOuts = 0; - - // JACK callbacks - JackProcessCallback jackProcessCallback = nullptr; - void* jackProcessArg = nullptr; - - // Runtime buffers - enum PortMask { - kPortMaskAudio = 0x1000, - kPortMaskMIDI = 0x2000, - kPortMaskInput = 0x4000, - kPortMaskOutput = 0x8000, - kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, - kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, - }; -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS]; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - HeapRingBuffer midiInBuffer; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - HeapRingBuffer midiOutBuffer; -#endif + const char* getVersion() const noexcept + { + return RTAUDIO_VERSION; + } - bool open() + bool open(const char* const clientName) override { ScopedPointer rtAudio; @@ -90,24 +64,31 @@ struct RtAudioBridge { uint rtAudioBufferFrames = 512; -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 RtAudio::StreamParameters inParams; - RtAudio::StreamParameters* const inParamsPtr = &inParams; inParams.deviceId = rtAudio->getDefaultInputDevice(); inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS; -#else + RtAudio::StreamParameters* const inParamsPtr = &inParams; + #else RtAudio::StreamParameters* const inParamsPtr = nullptr; -#endif + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 RtAudio::StreamParameters outParams; outParams.deviceId = rtAudio->getDefaultOutputDevice(); outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS; + RtAudio::StreamParameters* const outParamsPtr = &outParams; + #else + RtAudio::StreamParameters* const outParamsPtr = nullptr; + #endif RtAudio::StreamOptions opts; opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_ALSA_USE_DEFAULT; + opts.streamName = clientName; try { - rtAudio->openStream(&outParams, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, RtAudioCallback, this, &opts, nullptr); + rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, + RtAudioCallback, this, &opts, nullptr); } catch (const RtAudioError& err) { d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); return false; @@ -116,17 +97,10 @@ struct RtAudioBridge { handle = rtAudio; bufferSize = rtAudioBufferFrames; sampleRate = handle->getStreamSampleRate(); - -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - midiInBuffer.createBuffer(128); -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - midiOutBuffer.createBuffer(128); -#endif return true; } - bool close() + bool close() override { DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); @@ -141,82 +115,40 @@ struct RtAudioBridge { return true; } - bool activate() + bool activate() override { DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); try { handle->startStream(); - } DISTRHO_SAFE_EXCEPTION("handle->startStream()"); + } DISTRHO_SAFE_EXCEPTION_RETURN("handle->startStream()", false); return true; } - bool deactivate() + bool deactivate() override { DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false); try { handle->stopStream(); - } DISTRHO_SAFE_EXCEPTION("handle->stopStream()"); + } DISTRHO_SAFE_EXCEPTION_RETURN("handle->stopStream()", false); return true; } - jack_port_t* registerPort(const char* const type, const ulong flags) - { - bool isAudio, isInput; - - /**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) - isAudio = true; - else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) - isAudio = false; - else - return nullptr; - - /**/ if (flags & JackPortIsInput) - isInput = true; - else if (flags & JackPortIsOutput) - isInput = false; - else - return nullptr; - - const uintptr_t ret = (isAudio ? kPortMaskAudio : kPortMaskMIDI) - | (isInput ? kPortMaskInput : kPortMaskOutput); - - return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++) - : (isInput ? numMidiIns++ : numMidiOuts++))); - } - - void* getPortBuffer(jack_port_t* const port) - { - const uintptr_t portMask = (uintptr_t)port; - DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr); - -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - if (portMask & kPortMaskAudio) - return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)]; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if ((portMask & kPortMaskInputMIDI) == kPortMaskInputMIDI) - return &midiInBuffer; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - if ((portMask & kPortMaskOutputMIDI) == kPortMaskOutputMIDI) - return &midiOutBuffer; -#endif - - return nullptr; - } - static int RtAudioCallback(void* const outputBuffer, + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 void* const inputBuffer, + #else + void*, + #endif const uint numFrames, const double /* streamTime */, const RtAudioStreamStatus /* status */, void* const userData) { - RtAudioBridge* const self = (RtAudioBridge*)userData; + RtAudioBridge* const self = static_cast(userData); if (self->jackProcessCallback == nullptr) { @@ -225,35 +157,27 @@ struct RtAudioBridge { return 0; } -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - float** const selfAudioBuffers = self->audioBuffers; - - uint i = 0; -# if DISTRHO_PLUGIN_NUM_INPUTS > 0 - if (float* const insPtr = (float*)inputBuffer) + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + if (float* const insPtr = static_cast(inputBuffer)) { - for (uint j=0; jaudioBuffers[i] = insPtr + (i * numFrames); } -# endif -# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - if (float* const outsPtr = (float*)outputBuffer) + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + if (float* const outsPtr = static_cast(outputBuffer)) { - for (uint j=0; jaudioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames); } -# endif -#endif + #endif self->jackProcessCallback(numFrames, self->jackProcessArg); - return 0; -#if DISTRHO_PLUGIN_NUM_INPUTS == 0 - // unused - (void)inputBuffer; -#endif + return 0; } }; #endif // RTAUDIO_API_TYPE -#endif // RTAUDIOBRIDGE_HPP_INCLUDED +#endif // RTAUDIO_BRIDGE_HPP_INCLUDED diff --git a/distrho/src/jackbridge/SDLBridge.hpp b/distrho/src/jackbridge/SDL2Bridge.hpp similarity index 62% rename from distrho/src/jackbridge/SDLBridge.hpp rename to distrho/src/jackbridge/SDL2Bridge.hpp index 76cf4960..84bcf5d5 100644 --- a/distrho/src/jackbridge/SDLBridge.hpp +++ b/distrho/src/jackbridge/SDL2Bridge.hpp @@ -1,5 +1,5 @@ /* - * SDLBridge for DPF + * SDL Bridge for DPF * Copyright (C) 2021-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with @@ -14,15 +14,18 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef SDLBRIDGE_HPP_INCLUDED -#define SDLBRIDGE_HPP_INCLUDED +#ifndef SDL_BRIDGE_HPP_INCLUDED +#define SDL_BRIDGE_HPP_INCLUDED -#include "JackBridge.hpp" -#include "../../extra/RingBuffer.hpp" +#include "NativeBridge.hpp" #include -struct SDLBridge { +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0 +# error SDL without audio does not make sense +#endif + +struct SDL2Bridge : NativeBridge { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 SDL_AudioDeviceID captureDeviceId = 0; #endif @@ -30,43 +33,8 @@ struct SDLBridge { SDL_AudioDeviceID playbackDeviceId = 0; #endif - // SDL information - uint bufferSize = 0; - uint sampleRate = 0; - - // Port caching information - uint numAudioIns = 0; - uint numAudioOuts = 0; - uint numMidiIns = 0; - uint numMidiOuts = 0; - - // JACK callbacks - JackProcessCallback jackProcessCallback = nullptr; - void* jackProcessArg = nullptr; - - // Runtime buffers - enum PortMask { - kPortMaskAudio = 0x1000, - kPortMaskMIDI = 0x2000, - kPortMaskInput = 0x4000, - kPortMaskOutput = 0x8000, - kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, - kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, - }; -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS]; - float* audioBufferStorage; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - HeapRingBuffer midiInBuffer; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - HeapRingBuffer midiOutBuffer; -#endif - - bool open(const char* const clientName) + bool open(const char* const clientName) override { - #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 SDL_InitSubSystem(SDL_INIT_AUDIO); SDL_AudioSpec requested; @@ -78,7 +46,6 @@ struct SDLBridge { SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName); // SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "1"); - #endif #if DISTRHO_PLUGIN_NUM_INPUTS > 0 SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure"); @@ -144,130 +111,67 @@ struct SDLBridge { d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedCapture.freq); return false; } + #endif + + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 bufferSize = receivedCapture.samples; sampleRate = receivedCapture.freq; - #elif DISTRHO_PLUGIN_NUM_INPUTS > 0 - bufferSize = receivedCapture.samples; - sampleRate = receivedCapture.freq; - #elif DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #else bufferSize = receivedPlayback.samples; sampleRate = receivedPlayback.freq; - #else - d_stderr2("SDL without audio, unsupported for now"); - return false; #endif - -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 - std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS); -#endif - - for (uint i=0; i 0 + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); SDL_CloseAudioDevice(captureDeviceId); captureDeviceId = 0; -#endif -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); SDL_CloseAudioDevice(playbackDeviceId); playbackDeviceId = 0; -#endif - -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - delete[] audioBufferStorage; - audioBufferStorage = nullptr; -#endif + #endif + freeBuffers(); return true; } - bool activate() + bool activate() override { -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); SDL_PauseAudioDevice(captureDeviceId, 0); -#endif -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); SDL_PauseAudioDevice(playbackDeviceId, 0); -#endif + #endif return true; } - bool deactivate() + bool deactivate() override { -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); SDL_PauseAudioDevice(captureDeviceId, 1); -#endif -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); SDL_PauseAudioDevice(playbackDeviceId, 1); -#endif + #endif return true; } - jack_port_t* registerPort(const char* const type, const ulong flags) - { - bool isAudio, isInput; - - /**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) - isAudio = true; - else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) - isAudio = false; - else - return nullptr; - - /**/ if (flags & JackPortIsInput) - isInput = true; - else if (flags & JackPortIsOutput) - isInput = false; - else - return nullptr; - - const uintptr_t ret = (isAudio ? kPortMaskAudio : kPortMaskMIDI) - | (isInput ? kPortMaskInput : kPortMaskOutput); - - return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++) - : (isInput ? numMidiIns++ : numMidiOuts++))); - } - - void* getPortBuffer(jack_port_t* const port) - { - const uintptr_t portMask = (uintptr_t)port; - DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr); - -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - if (portMask & kPortMaskAudio) - return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)]; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - if ((portMask & kPortMaskInputMIDI) == kPortMaskInputMIDI) - return &midiInBuffer; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - if ((portMask & kPortMaskOutputMIDI) == kPortMaskOutputMIDI) - return &midiOutBuffer; -#endif - - return nullptr; - } - -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 static void AudioInputCallback(void* const userData, uchar* const stream, const int len) { - SDLBridge* const self = (SDLBridge*)userData; + NativeBridge* const self = static_cast(userData); // safety checks DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); @@ -287,14 +191,17 @@ struct SDLBridge { self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i]; } + #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 + // if there are no outputs, run process callback now self->jackProcessCallback(numFrames, self->jackProcessArg); + #endif } -#endif + #endif -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 static void AudioOutputCallback(void* const userData, uchar* const stream, const int len) { - SDLBridge* const self = (SDLBridge*)userData; + NativeBridge* const self = static_cast(userData); // safety checks DISTRHO_SAFE_ASSERT_RETURN(stream != nullptr,); @@ -309,10 +216,7 @@ struct SDLBridge { const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); - #if DISTRHO_PLUGIN_NUM_INPUTS == 0 - // if there are no inputs, run process callback now self->jackProcessCallback(numFrames, self->jackProcessArg); - #endif float* const fstream = (float*)stream; @@ -322,7 +226,7 @@ struct SDLBridge { fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j]; } } -#endif + #endif }; -#endif +#endif // SDL_BRIDGE_HPP_INCLUDED diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp new file mode 100644 index 00000000..1b97760d --- /dev/null +++ b/distrho/src/jackbridge/WebBridge.hpp @@ -0,0 +1,354 @@ +/* + * Web Audio + MIDI Bridge for DPF + * Copyright (C) 2021-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WEB_BRIDGE_HPP_INCLUDED +#define WEB_BRIDGE_HPP_INCLUDED + +#include "NativeBridge.hpp" + +#include + +struct WebBridge : NativeBridge { +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + bool captureAvailable = false; +#endif +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + bool playbackAvailable = false; +#endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool midiAvailable = false; +#endif + bool active = false; + double timestamp = 0; + + WebBridge() + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + captureAvailable = EM_ASM_INT({ + if (typeof(navigator.mediaDevices) !== 'undefined' && typeof(navigator.mediaDevices.getUserMedia) !== 'undefined') + return 1; + if (typeof(navigator.webkitGetUserMedia) !== 'undefined') + return 1; + return false; + }) != 0; + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + playbackAvailable = EM_ASM_INT({ + if (typeof(AudioContext) !== 'undefined') + return 1; + if (typeof(webkitAudioContext) !== 'undefined') + return 1; + return 0; + }) != 0; + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + midiAvailable = EM_ASM_INT({ + return typeof(navigator.requestMIDIAccess) === 'function' ? 1 : 0; + }) != 0; + #endif + } + + bool open(const char*) override + { + // early bail out if required features are not supported + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + if (!captureAvailable) + { + #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 + d_stderr2("Audio capture is not supported"); + return false; + #else + if (!playbackAvailable) + { + d_stderr2("Audio capture and playback are not supported"); + return false; + } + d_stderr2("Audio capture is not supported, but can still use playback"); + #endif + } + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + if (!playbackAvailable) + { + d_stderr2("Audio playback is not supported"); + return false; + } + #endif + + const bool initialized = EM_ASM_INT({ + if (typeof(Module['WebAudioBridge']) === 'undefined') { + Module['WebAudioBridge'] = {}; + } + + var WAB = Module['WebAudioBridge']; + if (!WAB.audioContext) { + if (typeof(AudioContext) !== 'undefined') { + WAB.audioContext = new AudioContext(); + } else if (typeof(webkitAudioContext) !== 'undefined') { + WAB.audioContext = new webkitAudioContext(); + } + } + + return WAB.audioContext === undefined ? 0 : 1; + }) != 0; + + if (!initialized) + { + d_stderr2("Failed to initialize web audio"); + return false; + } + + bufferSize = 512; + sampleRate = EM_ASM_INT_V({ + var WAB = Module['WebAudioBridge']; + return WAB.audioContext.sampleRate; + }); + + allocBuffers(); + + EM_ASM({ + var numInputs = $0; + var numOutputs = $1; + var bufferSize = $2; + var WAB = Module['WebAudioBridge']; + + // main processor + WAB.processor = WAB.audioContext['createScriptProcessor'](bufferSize, numInputs, numOutputs); + WAB.processor['onaudioprocess'] = function (e) { + var timestamp = performance.now(); + for (var i = 0; i < numInputs; ++i) { + var buffer = e['inputBuffer']['getChannelData'](i); + for (var j = 0; j < bufferSize; ++j) { + // setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float'); + HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j]; + } + } + dynCall('vif', $4, [$5, timestamp]); + for (var i = 0; i < numOutputs; ++i) { + var buffer = e['outputBuffer']['getChannelData'](i); + var offset = bufferSize * (numInputs + i); + for (var j = 0; j < bufferSize; ++j) { + buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; + } + } + }; + + // connect to output + WAB.processor['connect'](WAB.audioContext['destination']); + + // resume/start playback on first click + document.addEventListener('click', function(e) { + var WAB = Module['WebAudioBridge']; + console.log(WAB.audioContext.state); + if (WAB.audioContext.state === 'suspended') + WAB.audioContext.resume(); + }); + }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this); + +// enableInput(); + enableMIDI(); + + return true; + } + + bool close() override + { + freeBuffers(); + return true; + } + + bool activate() override + { + active = true; + return true; + } + + bool deactivate() override + { + active = false; + return true; + } + + bool enableInput() + { + DISTRHO_SAFE_ASSERT_RETURN(DISTRHO_PLUGIN_NUM_INPUTS > 0, false); + + EM_ASM({ + var numInputs = $0; + var WAB = Module['WebAudioBridge']; + + var constraints = {}; + // we need to use this weird awkward way for objects, otherwise build fails + constraints['audio'] = true; + constraints['video'] = false; + constraints['latency'] = 0; + constraints['sampleSize'] = 24; + constraints['mandatory'] = {}; + constraints['mandatory']['autoGainControl'] = false; + constraints['mandatory']['echoCancellation'] = false; + constraints['mandatory']['noiseSuppression'] = false; + constraints['mandatory']['channelCount'] = numInputs; + // old property for chrome + constraints['mandatory']['googAutoGainControl'] = false; + + var success = function(stream) { + WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream); + WAB.captureStreamNode.connect(WAB.processor); + }; + var fail = function() { + }; + + if (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined) { + navigator.mediaDevices.getUserMedia(constraints).then(success).catch(fail); + } else if (navigator.webkitGetUserMedia !== undefined) { + navigator.webkitGetUserMedia(constraints, success, fail); + } + }, DISTRHO_PLUGIN_NUM_INPUTS); + + return true; + } + + bool enableMIDI() + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (midiAvailable) + { + EM_ASM({ + var useInput = !!$0; + var useOutput = !!$1; + var maxSize = $2; + var WAB = Module['WebAudioBridge']; + + var offset = Module._malloc(maxSize); + + var inputCallback = function(event) { + if (event.data.length > maxSize) + return; + var buffer = new Uint8Array(Module.HEAPU8.buffer, offset, maxSize); + buffer.set(event.data); + dynCall('viiif', $3, [$4, buffer.byteOffset, event.data.length, event.timeStamp]); + }; + var stateCallback = function(event) { + if (event.port.state === 'connected' && event.port.connection === 'open') { + if (useInput && event.port.type === 'input') { + if (event.port.name.indexOf('Midi Through') < 0) + event.port.onmidimessage = inputCallback; + } else if (useOutput && event.port.type === 'output') { + event.port.open(); + } + } + }; + + var success = function(midi) { + WAB.midi = midi; + midi.onstatechange = stateCallback; + if (useInput) { + midi.inputs.forEach(function(port) { + if (port.name.indexOf('Midi Through') < 0) + port.onmidimessage = inputCallback; + }); + } + if (useOutput) { + midi.outputs.forEach(function(port) { + port.open(); + }); + } + }; + var fail = function(why) { + console.log("midi access failed:", why); + }; + + navigator.requestMIDIAccess().then(success, fail); + }, DISTRHO_PLUGIN_WANT_MIDI_INPUT, DISTRHO_PLUGIN_WANT_MIDI_OUTPUT, kMaxMIDIInputMessageSize, WebMIDICallback, this); + + return true; + } + else + #endif + { + d_stderr2("MIDI is not supported"); + return false; + } + } + + static void WebAudioCallback(void* const userData, const double timestamp) + { + WebBridge* const self = static_cast(userData); + self->timestamp = timestamp; + + const uint numFrames = self->bufferSize; + + if (self->jackProcessCallback != nullptr && self->active) + { + self->jackProcessCallback(numFrames, self->jackProcessArg); + + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (self->midiAvailable) + { + static_assert(kMaxMIDIInputMessageSize + 1u == 4, "change code if bumping this value"); + uint32_t offset = 0; + uint8_t bytes[4] = {}; + + while (self->midiOutBuffer.isDataAvailableForReading() && + self->midiOutBuffer.readCustomData(bytes, ARRAY_SIZE(bytes))) + { + offset = self->midiOutBuffer.readUInt(); + + EM_ASM({ + var WAB = Module['WebAudioBridge']; + if (WAB.midi) { + var timestamp = performance.now() + $0; + var size = $1; + WAB.midi.outputs.forEach(function(port) { + if (port.state !== 'disconnected') { + port.send(size == 3 ? [ $2, $3, $4 ] : + size == 2 ? [ $2, $3 ] : + [ $2 ], timestamp); + } + }); + } + }, offset, bytes[0], bytes[1], bytes[2], bytes[3]); + } + self->midiOutBuffer.clearData(); + } + #endif + } + else + { + for (uint i=0; iaudioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i], 0, sizeof(float)*numFrames); + } + } + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void WebMIDICallback(void* const userData, uint8_t* const data, const int len, const double timestamp) + { + DISTRHO_SAFE_ASSERT_RETURN(len > 0 && len <= (int)kMaxMIDIInputMessageSize,); + + WebBridge* const self = static_cast(userData); + + // TODO timestamp handling + self->midiInBufferPending.writeByte(static_cast(len)); + self->midiInBufferPending.writeCustomData(data, kMaxMIDIInputMessageSize); + self->midiInBufferPending.commitWrite(); + } + #endif +}; + +#endif // WEB_BRIDGE_HPP_INCLUDED From 7403ec875198b47894cedb5d8545588907c86280 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 04:34:41 +0100 Subject: [PATCH 452/504] Fix some compiler warnings Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- distrho/src/jackbridge/NativeBridge.hpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 12640c9e..9552878a 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 12640c9e41cd334f05c2a3d075c41db2c5bb7135 +Subproject commit 9552878aa764e577cd9cb0ad9933ce18e5ca9882 diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp index 3b1d4a79..533ed898 100644 --- a/distrho/src/jackbridge/NativeBridge.hpp +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -91,6 +91,8 @@ struct NativeBridge { } #endif return false; + // maybe unused + (void)event; } void clearEventBuffer() @@ -122,6 +124,9 @@ struct NativeBridge { midiOutBuffer.commitWrite(); #endif return false; + // maybe unused + (void)data; + (void)time; } void allocBuffers() From e2e2b5507f683a6b62adbd984b62cdde0ed05c3d Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 14:16:29 +0100 Subject: [PATCH 453/504] Introduce ways for standalones to request audio input and midi Signed-off-by: falkTX --- dgl/src/pugl-upstream | 2 +- distrho/DistrhoStandaloneUtils.hpp | 71 +++++++++++++++++++++++++ distrho/src/jackbridge/JackBridge.cpp | 71 ++++++++++++++++++++++++- distrho/src/jackbridge/NativeBridge.hpp | 23 ++++++++ distrho/src/jackbridge/WebBridge.hpp | 59 +++++++++++++++----- 5 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 distrho/DistrhoStandaloneUtils.hpp diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 9552878a..e90fdaa1 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 9552878aa764e577cd9cb0ad9933ce18e5ca9882 +Subproject commit e90fdaa11e0763fa31b6e0aedb32edb1ed2807cc diff --git a/distrho/DistrhoStandaloneUtils.hpp b/distrho/DistrhoStandaloneUtils.hpp new file mode 100644 index 00000000..cff4342f --- /dev/null +++ b/distrho/DistrhoStandaloneUtils.hpp @@ -0,0 +1,71 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_STANDALONE_UTILS_HPP_INCLUDED +#define DISTRHO_STANDALONE_UTILS_HPP_INCLUDED + +#include "src/DistrhoDefines.h" + +START_NAMESPACE_DISTRHO + +/* ------------------------------------------------------------------------------------------------------------ + * Standalone plugin related utilities */ + +/** + @defgroup PluginRelatedUtilities Plugin related utilities + + @{ + */ + +/** + Check if the current standalone supports audio input. +*/ +bool supportsAudioInput(); + +/** + Check if the current standalone supports MIDI. +*/ +bool supportsMIDI(); + +/** + Check if the current standalone has audio input enabled. +*/ +bool isAudioInputEnabled(); + +/** + Check if the current standalone has MIDI enabled. +*/ +bool isMIDIEnabled(); + +/** + Request permissions to use audio input. + Only valid to call if audio input is supported but not currently enabled. +*/ +bool requestAudioInput(); + +/** + Request permissions to use MIDI. + Only valid to call if MIDI is supported but not currently enabled. +*/ +bool requestMIDI(); + +/** @} */ + +// ----------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_STANDALONE_UTILS_HPP_INCLUDED diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index f5fb3229..6fa2551a 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -15,6 +15,7 @@ */ #include "JackBridge.hpp" +#include "../../DistrhoStandaloneUtils.hpp" #if ! (defined(JACKBRIDGE_DIRECT) || defined(JACKBRIDGE_DUMMY)) @@ -42,7 +43,7 @@ # include "WebBridge.hpp" #endif -#if defined(HAVE_RTAUDIO) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 +#if defined(HAVE_RTAUDIO) && defined(DISTRHO_PROPER_CPP11_SUPPORT) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 # include "RtAudioBridge.hpp" # ifdef RTAUDIO_API_TYPE # include "rtaudio/RtAudio.cpp" @@ -2242,3 +2243,71 @@ bool jackbridge_set_property_change_callback(jack_client_t* client, JackProperty } // ----------------------------------------------------------------------------- + +START_NAMESPACE_DISTRHO + +bool supportsAudioInput() +{ +#if defined(JACKBRIDGE_DUMMY) + return false; +#elif !defined(JACKBRIDGE_DIRECT) + if (usingNativeBridge) + return nativeBridge->supportsAudioInput(); +#endif + return true; +} + +bool supportsMIDI() +{ +#if defined(JACKBRIDGE_DUMMY) + return false; +#elif !defined(JACKBRIDGE_DIRECT) + if (usingNativeBridge) + return nativeBridge->supportsMIDI(); +#endif + return true; +} + +bool isAudioInputEnabled() +{ +#if defined(JACKBRIDGE_DUMMY) + return false; +#elif !defined(JACKBRIDGE_DIRECT) + if (usingNativeBridge) + return nativeBridge->isAudioInputEnabled(); +#endif + return true; +} + +bool isMIDIEnabled() +{ +#if defined(JACKBRIDGE_DUMMY) + return false; +#elif !defined(JACKBRIDGE_DIRECT) + if (usingNativeBridge) + return nativeBridge->isMIDIEnabled(); +#endif + return true; +} + +bool requestAudioInput() +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->requestAudioInput(); +#endif + return false; +} + +bool requestMIDI() +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->requestMIDI(); +#endif + return false; +} + +END_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------- diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp index 533ed898..5376135a 100644 --- a/distrho/src/jackbridge/NativeBridge.hpp +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -67,6 +67,29 @@ struct NativeBridge { virtual bool activate() = 0; virtual bool deactivate() = 0; + virtual bool supportsAudioInput() const + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + return true; + #else + return false; + #endif + } + + virtual bool isAudioInputEnabled() const + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + return true; + #else + return false; + #endif + } + + virtual bool supportsMIDI() const { return false; } + virtual bool isMIDIEnabled() const { return false; } + virtual bool requestAudioInput() { return false; } + virtual bool requestMIDI() { return false; } + uint32_t getEventCount() { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp index 1b97760d..d6045ead 100644 --- a/distrho/src/jackbridge/WebBridge.hpp +++ b/distrho/src/jackbridge/WebBridge.hpp @@ -131,7 +131,7 @@ struct WebBridge : NativeBridge { // main processor WAB.processor = WAB.audioContext['createScriptProcessor'](bufferSize, numInputs, numOutputs); WAB.processor['onaudioprocess'] = function (e) { - var timestamp = performance.now(); + // var timestamp = performance.now(); for (var i = 0; i < numInputs; ++i) { var buffer = e['inputBuffer']['getChannelData'](i); for (var j = 0; j < bufferSize; ++j) { @@ -139,7 +139,7 @@ struct WebBridge : NativeBridge { HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j]; } } - dynCall('vif', $4, [$5, timestamp]); + dynCall('vi', $4, [$5]); for (var i = 0; i < numOutputs; ++i) { var buffer = e['outputBuffer']['getChannelData'](i); var offset = bufferSize * (numInputs + i); @@ -161,9 +161,6 @@ struct WebBridge : NativeBridge { }); }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this); -// enableInput(); - enableMIDI(); - return true; } @@ -185,7 +182,25 @@ struct WebBridge : NativeBridge { return true; } - bool enableInput() + bool supportsAudioInput() const override + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + return captureAvailable; + #else + return false; + #endif + } + + bool isAudioInputEnabled() const override + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + return EM_ASM_INT({ return Module['WebAudioBridge'].captureStreamNode ? 1 : 0 }) != 0; + #else + return false; + #endif + } + + bool requestAudioInput() override { DISTRHO_SAFE_ASSERT_RETURN(DISTRHO_PLUGIN_NUM_INPUTS > 0, false); @@ -224,7 +239,25 @@ struct WebBridge : NativeBridge { return true; } - bool enableMIDI() + bool supportsMIDI() const override + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + return midiAvailable; + #else + return false; + #endif + } + + bool isMIDIEnabled() const override + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + return EM_ASM_INT({ return Module['WebAudioBridge'].midi ? 1 : 0 }) != 0; + #else + return false; + #endif + } + + bool requestMIDI() override { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT if (midiAvailable) @@ -287,10 +320,10 @@ struct WebBridge : NativeBridge { } } - static void WebAudioCallback(void* const userData, const double timestamp) + static void WebAudioCallback(void* const userData /* , const double timestamp */) { WebBridge* const self = static_cast(userData); - self->timestamp = timestamp; + // self->timestamp = timestamp; const uint numFrames = self->bufferSize; @@ -299,11 +332,12 @@ struct WebBridge : NativeBridge { self->jackProcessCallback(numFrames, self->jackProcessArg); #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - if (self->midiAvailable) + if (self->midiAvailable && self->midiOutBuffer.isDataAvailableForReading()) { static_assert(kMaxMIDIInputMessageSize + 1u == 4, "change code if bumping this value"); uint32_t offset = 0; uint8_t bytes[4] = {}; + double timestamp = EM_ASM_DOUBLE({ return performance.now(); }); while (self->midiOutBuffer.isDataAvailableForReading() && self->midiOutBuffer.readCustomData(bytes, ARRAY_SIZE(bytes))) @@ -313,7 +347,7 @@ struct WebBridge : NativeBridge { EM_ASM({ var WAB = Module['WebAudioBridge']; if (WAB.midi) { - var timestamp = performance.now() + $0; + var timestamp = $5 + $0; var size = $1; WAB.midi.outputs.forEach(function(port) { if (port.state !== 'disconnected') { @@ -323,8 +357,9 @@ struct WebBridge : NativeBridge { } }); } - }, offset, bytes[0], bytes[1], bytes[2], bytes[3]); + }, offset, bytes[0], bytes[1], bytes[2], bytes[3], timestamp); } + self->midiOutBuffer.clearData(); } #endif From 29e9fe9a9f449a92a5426066d8ac00a698050ee7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 17:55:40 +0100 Subject: [PATCH 454/504] Allow runtime buffer size changes in wasm/bridge modules Signed-off-by: falkTX --- distrho/DistrhoStandaloneUtils.hpp | 15 ++++ distrho/src/jackbridge/JackBridge.cpp | 42 ++++++++- distrho/src/jackbridge/NativeBridge.hpp | 9 ++ distrho/src/jackbridge/WebBridge.hpp | 109 ++++++++++++++++++++++-- 4 files changed, 162 insertions(+), 13 deletions(-) diff --git a/distrho/DistrhoStandaloneUtils.hpp b/distrho/DistrhoStandaloneUtils.hpp index cff4342f..00d3ada9 100644 --- a/distrho/DistrhoStandaloneUtils.hpp +++ b/distrho/DistrhoStandaloneUtils.hpp @@ -35,6 +35,11 @@ START_NAMESPACE_DISTRHO */ bool supportsAudioInput(); +/** + Check if the current standalone supports dynamic buffer size changes. +*/ +bool supportsBufferSizeChanges(); + /** Check if the current standalone supports MIDI. */ @@ -50,12 +55,22 @@ bool isAudioInputEnabled(); */ bool isMIDIEnabled(); +/** + Get the current buffer size. +*/ +uint32_t getBufferSize(); + /** Request permissions to use audio input. Only valid to call if audio input is supported but not currently enabled. */ bool requestAudioInput(); +/** + Request change to a new buffer size. +*/ +bool requestBufferSizeChange(uint32_t newBufferSize); + /** Request permissions to use MIDI. Only valid to call if MIDI is supported but not currently enabled. diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 6fa2551a..d417b5e2 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -1185,7 +1185,13 @@ bool jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_buffer_size_callback(client, bufsize_callback, arg) == 0); #else - if (usingRealJACK && getBridgeInstance().set_buffer_size_callback_ptr != nullptr) + if (usingNativeBridge) + { + nativeBridge->bufferSizeCallback = bufsize_callback; + nativeBridge->jackBufferSizeArg = arg; + return true; + } + if (getBridgeInstance().set_buffer_size_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_bufsize(bufsize_callback); @@ -1371,9 +1377,10 @@ bool jackbridge_set_buffer_size(jack_client_t* client, jack_nframes_t nframes) #elif defined(JACKBRIDGE_DIRECT) return jack_set_buffer_size(client, nframes); #else - if (usingRealJACK) - if (getBridgeInstance().set_buffer_size_ptr != nullptr) - return getBridgeInstance().set_buffer_size_ptr(client, nframes); + if (usingNativeBridge) + return nativeBridge->requestBufferSizeChange(nframes); + if (getBridgeInstance().set_buffer_size_ptr != nullptr) + return getBridgeInstance().set_buffer_size_ptr(client, nframes); #endif return false; } @@ -2257,6 +2264,15 @@ bool supportsAudioInput() return true; } +bool supportsBufferSizeChanges() +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->supportsBufferSizeChanges(); +#endif + return false; +} + bool supportsMIDI() { #if defined(JACKBRIDGE_DUMMY) @@ -2290,6 +2306,15 @@ bool isMIDIEnabled() return true; } +uint32_t getBufferSize() +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->getBufferSize(); +#endif + return 0; +} + bool requestAudioInput() { #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) @@ -2299,6 +2324,15 @@ bool requestAudioInput() return false; } +bool requestBufferSizeChange(const uint32_t newBufferSize) +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->requestBufferSizeChange(newBufferSize); +#endif + return false; +} + bool requestMIDI() { #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp index 5376135a..00599a0b 100644 --- a/distrho/src/jackbridge/NativeBridge.hpp +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -36,7 +36,9 @@ struct NativeBridge { // JACK callbacks JackProcessCallback jackProcessCallback = nullptr; + JackBufferSizeCallback bufferSizeCallback = nullptr; void* jackProcessArg = nullptr; + void* jackBufferSizeArg = nullptr; // Runtime buffers enum PortMask { @@ -85,11 +87,18 @@ struct NativeBridge { #endif } + virtual bool supportsBufferSizeChanges() const { return false; } virtual bool supportsMIDI() const { return false; } virtual bool isMIDIEnabled() const { return false; } virtual bool requestAudioInput() { return false; } + virtual bool requestBufferSizeChange(uint32_t) { return false; } virtual bool requestMIDI() { return false; } + uint32_t getBufferSize() const noexcept + { + return bufferSize; + } + uint32_t getEventCount() { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp index d6045ead..1a970a1c 100644 --- a/distrho/src/jackbridge/WebBridge.hpp +++ b/distrho/src/jackbridge/WebBridge.hpp @@ -114,7 +114,7 @@ struct WebBridge : NativeBridge { return false; } - bufferSize = 512; + bufferSize = 2048; sampleRate = EM_ASM_INT_V({ var WAB = Module['WebAudioBridge']; return WAB.audioContext.sampleRate; @@ -212,15 +212,24 @@ struct WebBridge : NativeBridge { // we need to use this weird awkward way for objects, otherwise build fails constraints['audio'] = true; constraints['video'] = false; - constraints['latency'] = 0; - constraints['sampleSize'] = 24; - constraints['mandatory'] = {}; - constraints['mandatory']['autoGainControl'] = false; - constraints['mandatory']['echoCancellation'] = false; - constraints['mandatory']['noiseSuppression'] = false; - constraints['mandatory']['channelCount'] = numInputs; + constraints['autoGainControl'] = {}; + constraints['autoGainControl']['exact'] = false; + constraints['echoCancellation'] = {}; + constraints['echoCancellation']['exact'] = false; + constraints['noiseSuppression'] = {}; + constraints['noiseSuppression']['exact'] = false; + constraints['channelCount'] = {}; + constraints['channelCount']['min'] = 0; + constraints['channelCount']['ideal'] = numInputs; + constraints['latency'] = {}; + constraints['latency']['min'] = 0; + constraints['latency']['ideal'] = 0; + constraints['sampleSize'] = {}; + constraints['sampleSize']['min'] = 8; + constraints['sampleSize']['max'] = 32; + constraints['sampleSize']['ideal'] = 16; // old property for chrome - constraints['mandatory']['googAutoGainControl'] = false; + constraints['googAutoGainControl'] = false; var success = function(stream) { WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream); @@ -239,6 +248,88 @@ struct WebBridge : NativeBridge { return true; } + bool supportsBufferSizeChanges() const override + { + return true; + } + + bool requestBufferSizeChange(const uint32_t newBufferSize) override + { + // try to create new processor first + bool success = EM_ASM_INT({ + var numInputs = $0; + var numOutputs = $1; + var newBufferSize = $2; + var WAB = Module['WebAudioBridge']; + + try { + WAB.newProcessor = WAB.audioContext['createScriptProcessor'](newBufferSize, numInputs, numOutputs); + } catch (e) { + return 0; + } + + // got new processor, disconnect old one + WAB.processor['disconnect'](WAB.audioContext['destination']); + + if (WAB.captureStreamNode) + WAB.captureStreamNode.disconnect(WAB.processor); + + return 1; + }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, newBufferSize) != 0; + + if (!success) + return false; + + bufferSize = newBufferSize; + freeBuffers(); + allocBuffers(); + + if (bufferSizeCallback != nullptr) + bufferSizeCallback(newBufferSize, jackBufferSizeArg); + + EM_ASM({ + var numInputs = $0; + var numOutputs = $1; + var bufferSize = $2; + var WAB = Module['WebAudioBridge']; + + // store the new processor + delete WAB.processor; + WAB.processor = WAB.newProcessor; + delete WAB.newProcessor; + + // setup new processor the same way as old one + WAB.processor['onaudioprocess'] = function (e) { + // var timestamp = performance.now(); + for (var i = 0; i < numInputs; ++i) { + var buffer = e['inputBuffer']['getChannelData'](i); + for (var j = 0; j < bufferSize; ++j) { + // setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float'); + HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j]; + } + } + dynCall('vi', $4, [$5]); + for (var i = 0; i < numOutputs; ++i) { + var buffer = e['outputBuffer']['getChannelData'](i); + var offset = bufferSize * (numInputs + i); + for (var j = 0; j < bufferSize; ++j) { + buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; + } + } + }; + + // connect to output + WAB.processor['connect'](WAB.audioContext['destination']); + + // and input, if available + if (WAB.captureStreamNode) + WAB.captureStreamNode.connect(WAB.processor); + + }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this); + + return true; + } + bool supportsMIDI() const override { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT From 9ab4ff814516cd8e818ae81acd3d3ff5d04a1ba5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 23:18:15 +0100 Subject: [PATCH 455/504] Try to make X11 copy&paste more robust Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 62 ++++++++++++++++++++++++----------- dgl/src/WindowPrivateData.hpp | 3 +- dgl/src/pugl-upstream | 2 +- dgl/src/pugl.cpp | 22 +++++++++++++ dgl/src/pugl.hpp | 9 +++++ 5 files changed, 77 insertions(+), 21 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index dddcf264..6c341bfd 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -109,7 +109,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - waitingForClipboard(false), + waitingForClipboardData(false), + waitingForClipboardEvents(false), clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED @@ -137,7 +138,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - waitingForClipboard(false), + waitingForClipboardData(false), + waitingForClipboardEvents(false), clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED @@ -167,7 +169,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - waitingForClipboard(false), + waitingForClipboardData(false), + waitingForClipboardEvents(false), clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED @@ -198,7 +201,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), - waitingForClipboard(false), + waitingForClipboardData(false), + waitingForClipboardEvents(false), clipboardTypeId(0), filenameToRenderInto(nullptr), #ifndef DGL_FILE_BROWSER_DISABLED @@ -610,7 +614,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh void Window::PrivateData::onPuglExpose() { - DGL_DBGp("PUGL: onPuglExpose\n"); + DGL_DBG("PUGL: onPuglExpose\n"); puglOnDisplayPrepare(view); @@ -769,36 +773,48 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) const void* Window::PrivateData::getClipboard(size_t& dataSize) { clipboardTypeId = 0; - waitingForClipboard = true; + waitingForClipboardData = true, + waitingForClipboardEvents = true; + // begin clipboard dance here if (puglPaste(view) != PUGL_SUCCESS) { dataSize = 0; - waitingForClipboard = false; + waitingForClipboardEvents = false; return nullptr; } - // wait for type request - while (waitingForClipboard && clipboardTypeId == 0) - puglUpdate(appData->world, 0.03); + // wait for type request, clipboardTypeId must be != 0 to be valid + while (clipboardTypeId == 0 && waitingForClipboardData) + { + #ifdef DGL_USING_X11 + puglX11UpdateWithoutExposures(appData->world); + #endif + } if (clipboardTypeId == 0) { dataSize = 0; - waitingForClipboard = false; + waitingForClipboardEvents = false; return nullptr; } - // wait for actual data - while (waitingForClipboard) - puglUpdate(appData->world, 0.03); + // wait for actual data (assumes offer was accepted) + while (waitingForClipboardData) + { + #ifdef DGL_USING_X11 + puglX11UpdateWithoutExposures(appData->world); + #endif + } if (clipboardTypeId == 0) { dataSize = 0; + waitingForClipboardEvents = false; return nullptr; } + waitingForClipboardEvents = false; return puglGetClipboard(view, clipboardTypeId - 1, &dataSize); } @@ -809,7 +825,8 @@ uint32_t Window::PrivateData::onClipboardDataOffer() if ((clipboardTypeId = self->onClipboardDataOffer()) != 0) return clipboardTypeId; - waitingForClipboard = false; + // stop waiting for data, it was rejected + waitingForClipboardData = false; return 0; } @@ -818,7 +835,7 @@ void Window::PrivateData::onClipboardData(const uint32_t typeId) if (clipboardTypeId != typeId) clipboardTypeId = 0; - waitingForClipboard = false; + waitingForClipboardData = false; } #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) @@ -834,7 +851,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu } #endif - if (pData->waitingForClipboard) + if (pData->waitingForClipboardEvents) { switch (event->type) { @@ -851,8 +868,15 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu case PUGL_BUTTON_RELEASE: case PUGL_MOTION: case PUGL_SCROLL: + case PUGL_TIMER: + case PUGL_LOOP_ENTER: + case PUGL_LOOP_LEAVE: return PUGL_SUCCESS; + case PUGL_DATA_OFFER: + case PUGL_DATA: + break; default: + d_stdout("Got event %d while waitingForClipboardEvents", event->type); break; } } @@ -865,10 +889,10 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu ///< View created, a #PuglEventCreate case PUGL_CREATE: -#if defined(HAVE_X11) && !defined(DISTRHO_OS_MAC) && !defined(DISTRHO_OS_WINDOWS) + #ifdef DGL_USING_X11 if (! pData->isEmbed) puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); -#endif + #endif break; ///< View destroyed, a #PuglEventDestroy diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index c204f7a5..d5c3cbd8 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -78,7 +78,8 @@ struct Window::PrivateData : IdleCallback { bool ignoreIdleCallbacks; /** Whether we are waiting to receive clipboard data, ignoring some events in the process. */ - bool waitingForClipboard; + bool waitingForClipboardData; + bool waitingForClipboardEvents; /** The type id returned by the last onClipboardDataOffer call. */ uint32_t clipboardTypeId; diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index e90fdaa1..1c669b15 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit e90fdaa11e0763fa31b6e0aedb32edb1ed2807cc +Subproject commit 1c669b15877b04c1fc8cff9d37bc336ef0ff7808 diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 1254da28..cc47a9a6 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -226,6 +226,8 @@ void puglRaiseWindow(PuglView* const view) if (NSWindow* const window = view->impl->window ? view->impl->window : [view->impl->wrapperView window]) [window orderFrontRegardless]; +#elif defined(DISTRHO_OS_WASM) + // nothing #elif defined(DISTRHO_OS_WINDOWS) SetForegroundWindow(view->impl->hwnd); SetActiveWindow(view->impl->hwnd); @@ -290,6 +292,8 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co if (aspect && (status = updateSizeHint(view, PUGL_FIXED_ASPECT)) != PUGL_SUCCESS) return status; } +#elif defined(DISTRHO_OS_WASM) + // nothing #elif defined(DISTRHO_OS_WINDOWS) // nothing #elif defined(HAVE_X11) @@ -317,6 +321,8 @@ void puglSetResizable(PuglView* const view, const bool resizable) [window setStyleMask:style]; } // FIXME use [view setAutoresizingMask:NSViewNotSizable] ? +#elif defined(DISTRHO_OS_WASM) + // nothing #elif defined(DISTRHO_OS_WINDOWS) if (const HWND hwnd = view->impl->hwnd) { @@ -433,6 +439,8 @@ void puglFallbackOnResize(PuglView* const view) #endif } +// -------------------------------------------------------------------------------------------------------------------- + #if defined(DISTRHO_OS_MAC) // -------------------------------------------------------------------------------------------------------------------- @@ -570,8 +578,22 @@ void puglWin32ShowCentered(PuglView* const view) // -------------------------------------------------------------------------------------------------------------------- +#elif defined(DISTRHO_OS_WASM) + +// nothing here yet + +// -------------------------------------------------------------------------------------------------------------------- + #elif defined(HAVE_X11) +PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) +{ + world->impl->dispatchingEvents = true; + const PuglStatus st = dispatchX11Events(world); + world->impl->dispatchingEvents = false; + return st; +} + // -------------------------------------------------------------------------------------------------------------------- // X11 specific, set dialog window type and pid hints diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index 23b62e54..d5d540b1 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -84,6 +84,10 @@ PuglStatus puglMacOSRemoveChildWindow(PuglView* view, PuglView* child); // macOS specific, center view based on parent coordinates (if there is one) void puglMacOSShowCentered(PuglView* view); +#elif defined(DISTRHO_OS_WASM) + +// nothing here yet + #elif defined(DISTRHO_OS_WINDOWS) // win32 specific, call ShowWindow with SW_RESTORE @@ -94,6 +98,11 @@ void puglWin32ShowCentered(PuglView* view); #elif defined(HAVE_X11) +#define DGL_USING_X11 + +// X11 specific, update world without triggering exposure evente +PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world); + // X11 specific, set dialog window type and pid hints void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); From 29055e6ff1c93477def5558c7b5d636a228c1a0a Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 23:21:32 +0100 Subject: [PATCH 456/504] Make sure to only wait under X11 Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 6c341bfd..f383916b 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -784,13 +784,11 @@ const void* Window::PrivateData::getClipboard(size_t& dataSize) return nullptr; } + #ifdef DGL_USING_X11 // wait for type request, clipboardTypeId must be != 0 to be valid while (clipboardTypeId == 0 && waitingForClipboardData) - { - #ifdef DGL_USING_X11 puglX11UpdateWithoutExposures(appData->world); - #endif - } + #endif if (clipboardTypeId == 0) { @@ -799,13 +797,11 @@ const void* Window::PrivateData::getClipboard(size_t& dataSize) return nullptr; } + #ifdef DGL_USING_X11 // wait for actual data (assumes offer was accepted) while (waitingForClipboardData) - { - #ifdef DGL_USING_X11 puglX11UpdateWithoutExposures(appData->world); - #endif - } + #endif if (clipboardTypeId == 0) { From 3576108dfd1178122fefd640ac3a55401b759b7f Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 23:32:28 +0100 Subject: [PATCH 457/504] Fix C++98 build Signed-off-by: falkTX --- distrho/src/jackbridge/NativeBridge.hpp | 37 +++++++++++++++++++------ distrho/src/jackbridge/SDL2Bridge.hpp | 22 +++++++++++---- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp index 00599a0b..c3bdee15 100644 --- a/distrho/src/jackbridge/NativeBridge.hpp +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -25,14 +25,14 @@ using DISTRHO_NAMESPACE::HeapRingBuffer; struct NativeBridge { // Current status information - uint bufferSize = 0; - uint sampleRate = 0; + uint bufferSize; + uint sampleRate; // Port caching information - uint numAudioIns = 0; - uint numAudioOuts = 0; - uint numMidiIns = 0; - uint numMidiOuts = 0; + uint numAudioIns; + uint numAudioOuts; + uint numMidiIns; + uint numMidiOuts; // JACK callbacks JackProcessCallback jackProcessCallback = nullptr; @@ -50,8 +50,8 @@ struct NativeBridge { kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, }; #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS] = {}; - float* audioBufferStorage = nullptr; + float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS]; + float* audioBufferStorage; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT static constexpr const uint32_t kMaxMIDIInputMessageSize = 3; @@ -63,6 +63,27 @@ struct NativeBridge { HeapRingBuffer midiOutBuffer; #endif + NativeBridge() + : bufferSize(0), + sampleRate(0), + numAudioIns(0), + numAudioOuts(0), + numMidiIns(0), + numMidiOuts(0), + jackProcessCallback(nullptr), + bufferSizeCallback(nullptr), + jackProcessArg(nullptr), + jackBufferSizeArg(nullptr), + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + audioBuffers(), + audioBufferStorage(nullptr) + #endif + { + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + std::memset(audioBuffers, 0, sizeof(audioBuffers)); + #endif + } + virtual ~NativeBridge() {} virtual bool open(const char* const clientName) = 0; virtual bool close() = 0; diff --git a/distrho/src/jackbridge/SDL2Bridge.hpp b/distrho/src/jackbridge/SDL2Bridge.hpp index 84bcf5d5..88cfd64e 100644 --- a/distrho/src/jackbridge/SDL2Bridge.hpp +++ b/distrho/src/jackbridge/SDL2Bridge.hpp @@ -26,12 +26,22 @@ #endif struct SDL2Bridge : NativeBridge { -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 - SDL_AudioDeviceID captureDeviceId = 0; -#endif -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - SDL_AudioDeviceID playbackDeviceId = 0; -#endif + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + SDL_AudioDeviceID captureDeviceId; + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + SDL_AudioDeviceID playbackDeviceId; + #endif + + SDL2Bridge() + : NativeBridge() + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + , captureDeviceId(0) + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + , playbackDeviceId(0) + #endif + {} bool open(const char* const clientName) override { From 96896dcd4d7164628305ee17d3414e88faba0b39 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 11 Jul 2022 23:42:50 +0100 Subject: [PATCH 458/504] Fix previous commit Signed-off-by: falkTX --- distrho/src/jackbridge/NativeBridge.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp index c3bdee15..c83262b8 100644 --- a/distrho/src/jackbridge/NativeBridge.hpp +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -73,11 +73,11 @@ struct NativeBridge { jackProcessCallback(nullptr), bufferSizeCallback(nullptr), jackProcessArg(nullptr), - jackBufferSizeArg(nullptr), - #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - audioBuffers(), - audioBufferStorage(nullptr) - #endif + jackBufferSizeArg(nullptr) + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + , audioBuffers() + , audioBufferStorage(nullptr) + #endif { #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 std::memset(audioBuffers, 0, sizeof(audioBuffers)); From 9de73b40a9a417f19aa150175081eb4d233a34f5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 12 Jul 2022 10:36:51 +0100 Subject: [PATCH 459/504] Dont lockup in puglX11UpdateWithoutExposures Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 10 ++++++++-- dgl/src/pugl.cpp | 20 ++++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index f383916b..a5d0db49 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -787,7 +787,10 @@ const void* Window::PrivateData::getClipboard(size_t& dataSize) #ifdef DGL_USING_X11 // wait for type request, clipboardTypeId must be != 0 to be valid while (clipboardTypeId == 0 && waitingForClipboardData) - puglX11UpdateWithoutExposures(appData->world); + { + if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) + break; + } #endif if (clipboardTypeId == 0) @@ -800,7 +803,10 @@ const void* Window::PrivateData::getClipboard(size_t& dataSize) #ifdef DGL_USING_X11 // wait for actual data (assumes offer was accepted) while (waitingForClipboardData) - puglX11UpdateWithoutExposures(appData->world); + { + if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) + break; + } #endif if (clipboardTypeId == 0) diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index cc47a9a6..e54c224f 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -588,10 +588,22 @@ void puglWin32ShowCentered(PuglView* const view) PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) { - world->impl->dispatchingEvents = true; - const PuglStatus st = dispatchX11Events(world); - world->impl->dispatchingEvents = false; - return st; + const bool wasDispatchingEvents = world->impl->dispatchingEvents; + world->impl->dispatchingEvents = true; + PuglStatus st = PUGL_SUCCESS; + + const double startTime = puglGetTime(world); + const double endTime = startTime + 0.03; + + for (double t = startTime; !st && t < endTime; t = puglGetTime(world)) + { + if (!(st = pollX11Socket(world, endTime - t))) { + st = dispatchX11Events(world); + } + } + + world->impl->dispatchingEvents = wasDispatchingEvents; + return st; } // -------------------------------------------------------------------------------------------------------------------- From a652e711eb4b0be13bf9c821870fab308da981d5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 12 Jul 2022 10:51:24 +0100 Subject: [PATCH 460/504] Wait a maximum of 2 seconds for clipboard Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 6 ++++-- dgl/src/pugl.cpp | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index a5d0db49..60b81957 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -786,7 +786,8 @@ const void* Window::PrivateData::getClipboard(size_t& dataSize) #ifdef DGL_USING_X11 // wait for type request, clipboardTypeId must be != 0 to be valid - while (clipboardTypeId == 0 && waitingForClipboardData) + int retry = static_cast(2 / 0.03); + while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0) { if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) break; @@ -802,7 +803,8 @@ const void* Window::PrivateData::getClipboard(size_t& dataSize) #ifdef DGL_USING_X11 // wait for actual data (assumes offer was accepted) - while (waitingForClipboardData) + retry = static_cast(2 / 0.03); + while (waitingForClipboardData && --retry >= 0) { if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) break; diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index e54c224f..5ed3b5b2 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -597,9 +597,8 @@ PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) for (double t = startTime; !st && t < endTime; t = puglGetTime(world)) { - if (!(st = pollX11Socket(world, endTime - t))) { - st = dispatchX11Events(world); - } + pollX11Socket(world, endTime - t); + st = dispatchX11Events(world); } world->impl->dispatchingEvents = wasDispatchingEvents; From 1241390730867aaff6e0b748fc18a323513a966c Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Jul 2022 11:36:28 +0100 Subject: [PATCH 461/504] Fix some warnings under macOS --- Makefile | 3 --- dgl/Makefile | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 8eae693c..df63731e 100644 --- a/Makefile +++ b/Makefile @@ -40,9 +40,6 @@ endif ifeq ($(CAN_GENERATE_TTL),true) gen: examples utils/lv2_ttl_generator @$(CURDIR)/utils/generate-ttl.sh -ifeq ($(MACOS),true) - @$(CURDIR)/utils/generate-vst-bundles.sh -endif utils/lv2_ttl_generator: $(MAKE) -C utils/lv2-ttl-generator diff --git a/dgl/Makefile b/dgl/Makefile index 867d36de..58e2057b 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -13,6 +13,10 @@ BUILD_CXX_FLAGS += $(DGL_FLAGS) -I. -Isrc -DDONT_SET_USING_DGL_NAMESPACE -Wno-un BUILD_CXX_FLAGS += -Isrc/pugl-upstream/include LINK_FLAGS += $(DGL_LIBS) +ifeq ($(MACOS),true) +BUILD_CXX_FLAGS += -Wno-deprecated-declarations +endif + # TODO fix these after pugl-upstream is done BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers ifneq ($(MACOS),true) From 56cbf3b3ca3ab5ec7677d30978cf796a41302789 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Jul 2022 12:13:03 +0100 Subject: [PATCH 462/504] Fix MIDI handling in standalone native bridges without real MIDI --- distrho/src/jackbridge/NativeBridge.hpp | 59 +++++++++++++++++-------- distrho/src/jackbridge/WebBridge.hpp | 12 ----- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp index c83262b8..30e26db7 100644 --- a/distrho/src/jackbridge/NativeBridge.hpp +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -53,6 +53,9 @@ struct NativeBridge { float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS]; float* audioBufferStorage; #endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool midiAvailable; +#endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT static constexpr const uint32_t kMaxMIDIInputMessageSize = 3; uint8_t midiDataStorage[kMaxMIDIInputMessageSize]; @@ -78,6 +81,9 @@ struct NativeBridge { , audioBuffers() , audioBufferStorage(nullptr) #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + , midiAvailable(false) + #endif { #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 std::memset(audioBuffers, 0, sizeof(audioBuffers)); @@ -109,7 +115,6 @@ struct NativeBridge { } virtual bool supportsBufferSizeChanges() const { return false; } - virtual bool supportsMIDI() const { return false; } virtual bool isMIDIEnabled() const { return false; } virtual bool requestAudioInput() { return false; } virtual bool requestBufferSizeChange(uint32_t) { return false; } @@ -120,22 +125,34 @@ struct NativeBridge { return bufferSize; } + bool supportsMIDI() const noexcept + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + return midiAvailable; + #else + return false; + #endif + } + uint32_t getEventCount() { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - // NOTE: this function is only called once per run - midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending); - return midiInBufferCurrent.getReadableDataSize() / (kMaxMIDIInputMessageSize + 1u); - #else - return 0; + if (midiAvailable) + { + // NOTE: this function is only called once per run + midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending); + return midiInBufferCurrent.getReadableDataSize() / (kMaxMIDIInputMessageSize + 1u); + } #endif + + return 0; } bool getEvent(jack_midi_event_t* const event) { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT // NOTE: this function is called for all events in index succession - if (midiInBufferCurrent.getReadableDataSize() >= (kMaxMIDIInputMessageSize + 1u)) + if (midiAvailable && midiInBufferCurrent.getReadableDataSize() >= (kMaxMIDIInputMessageSize + 1u)) { event->time = 0; // TODO event->size = midiInBufferCurrent.readByte(); @@ -151,7 +168,8 @@ struct NativeBridge { void clearEventBuffer() { #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - midiOutBuffer.clearData(); + if (midiAvailable) + midiOutBuffer.clearData(); #endif } @@ -159,23 +177,28 @@ struct NativeBridge { { if (size > 3) return false; + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - if (midiOutBuffer.writeByte(size) && midiOutBuffer.writeCustomData(data, size)) + if (midiAvailable) { - bool fail = false; - // align - switch (size) + if (midiOutBuffer.writeByte(size) && midiOutBuffer.writeCustomData(data, size)) { - case 1: fail |= !midiOutBuffer.writeByte(0); - // fall-through - case 2: fail |= !midiOutBuffer.writeByte(0); + bool fail = false; + // align + switch (size) + { + case 1: fail |= !midiOutBuffer.writeByte(0); + // fall-through + case 2: fail |= !midiOutBuffer.writeByte(0); + } + fail |= !midiOutBuffer.writeUInt(time); + midiOutBuffer.commitWrite(); + return !fail; } - fail |= !midiOutBuffer.writeUInt(time); midiOutBuffer.commitWrite(); - return !fail; } - midiOutBuffer.commitWrite(); #endif + return false; // maybe unused (void)data; diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp index 1a970a1c..71154bb9 100644 --- a/distrho/src/jackbridge/WebBridge.hpp +++ b/distrho/src/jackbridge/WebBridge.hpp @@ -27,9 +27,6 @@ struct WebBridge : NativeBridge { #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 bool playbackAvailable = false; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - bool midiAvailable = false; #endif bool active = false; double timestamp = 0; @@ -330,15 +327,6 @@ struct WebBridge : NativeBridge { return true; } - bool supportsMIDI() const override - { - #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - return midiAvailable; - #else - return false; - #endif - } - bool isMIDIEnabled() const override { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT From a11c301718fd2b0aa398ad6e0f7247bfc27b8483 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Jul 2022 20:15:44 +0100 Subject: [PATCH 463/504] Allow v3_funknown_iid for factory class creation, fixes Cubase --- distrho/src/DistrhoPluginVST3.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 23a7b06f..bab3266a 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -4425,7 +4425,8 @@ struct dpf_factory : v3_plugin_factory_cpp { v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &hostApplication); // create component - if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid)) + if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && (v3_tuid_match(iid, v3_component_iid) || + v3_tuid_match(iid, v3_funknown_iid))) { dpf_component** const componentptr = new dpf_component*; *componentptr = new dpf_component(hostApplication); @@ -4435,7 +4436,8 @@ struct dpf_factory : v3_plugin_factory_cpp { #if DPF_VST3_USES_SEPARATE_CONTROLLER // create edit controller - if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_controller) && v3_tuid_match(iid, v3_edit_controller_iid)) + if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_controller) && (v3_tuid_match(iid, v3_edit_controller_iid) || + v3_tuid_match(iid, v3_funknown_iid))) { dpf_edit_controller** const controllerptr = new dpf_edit_controller*; *controllerptr = new dpf_edit_controller(hostApplication); From ead029fdd7e93ccf5963f980ee7a1b615c195b14 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Jul 2022 20:42:15 +0100 Subject: [PATCH 464/504] Add V3 ids seen in use but unknown. If you find them, please tell --- distrho/src/DistrhoPluginVST3.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index bab3266a..ce8d9c04 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -126,7 +126,37 @@ const char* tuid2str(const v3_tuid iid) { V3_ID(0xF89F8CDF,0x699E4BA5,0x96AAC9A4,0x81452B01), "{v3_unit_handler2|NOT}" }, { V3_ID(0x3D4BD6B5,0x913A4FD2,0xA886E768,0xA5EB92C1), "{v3_unit_info|NOT}" }, // misc + { V3_ID(0x309ECE78,0xEB7D4FAE,0x8B2225D9,0x09FD08B6), "{v3_audio_presentation_latency|NOT}" }, + { V3_ID(0xB4E8287F,0x1BB346AA,0x83A46667,0x68937BAB), "{v3_automation_state|NOT}" }, { V3_ID(0x0F194781,0x8D984ADA,0xBBA0C1EF,0xC011D8D0), "{v3_info_listener|NOT}" }, + { V3_ID(0x6D21E1DC,0x91199D4B,0xA2A02FEF,0x6C1AE55C), "{v3_parameter_function_name|NOT}" }, + { V3_ID(0x8AE54FDA,0xE93046B9,0xA28555BC,0xDC98E21E), "{v3_prefetchable_support|NOT}" }, + { V3_ID(0xA81A0471,0x48C34DC4,0xAC30C9E1,0x3C8393D5), "{v3_xml_representation_stream|NOT}" }, + /* + // seen in the wild but unknown, related to component + { V3_ID(0x6548D671,0x997A4EA5,0x9B336A6F,0xB3E93B50), "{v3_|NOT}" }, + { V3_ID(0xC2B7896B,0x069844D5,0x8F06E937,0x33A35FF7), "{v3_|NOT}" }, + { V3_ID(0xE123DE93,0xE0F642A4,0xAE53867E,0x53F059EE), "{v3_|NOT}" }, + { V3_ID(0x83850D7B,0xC12011D8,0xA143000A,0x959B31C6), "{v3_|NOT}" }, + { V3_ID(0x9598D418,0xA00448AC,0x9C6D8248,0x065B2E5C), "{v3_|NOT}" }, + { V3_ID(0xBD386132,0x45174BAD,0xA324390B,0xFD297506), "{v3_|NOT}" }, + { V3_ID(0xD7296A84,0x23B1419C,0xAAD0FAA3,0x53BB16B7), "{v3_|NOT}" }, + { V3_ID(0x181A0AF6,0xA10947BA,0x8A6F7C7C,0x3FF37129), "{v3_|NOT}" }, + { V3_ID(0xC2B7896B,0x69A844D5,0x8F06E937,0x33A35FF7), "{v3_|NOT}" }, + // seen in the wild but unknown, related to edit controller + { V3_ID(0x1F2F76D3,0xBFFB4B96,0xB99527A5,0x5EBCCEF4), "{v3_|NOT}" }, + { V3_ID(0x6B2449CC,0x419740B5,0xAB3C79DA,0xC5FE5C86), "{v3_|NOT}" }, + { V3_ID(0x67800560,0x5E784D90,0xB97BAB4C,0x8DC5BAA3), "{v3_|NOT}" }, + { V3_ID(0xDB51DA00,0x8FD5416D,0xB84894D8,0x7FDE73E4), "{v3_|NOT}" }, + { V3_ID(0xE90FC54F,0x76F24235,0x8AF8BD15,0x68C663D6), "{v3_|NOT}" }, + { V3_ID(0x07938E89,0xBA0D4CA8,0x8C7286AB,0xA9DDA95B), "{v3_|NOT}" }, + { V3_ID(0x42879094,0xA2F145ED,0xAC90E82A,0x99458870), "{v3_|NOT}" }, + // seen in the wild but unknown, related to view + { V3_ID(0xAA3E50FF,0xB78840EE,0xADCD48E8,0x094CEDB7), "{v3_|NOT}" }, + { V3_ID(0x2CAE14DB,0x4DE04C6E,0x8BD2E611,0x1B31A9C2), "{v3_|NOT}" }, + { V3_ID(0xD868D61D,0x20F445F4,0x947D069E,0xC811D1E4), "{v3_|NOT}" }, + { V3_ID(0xEE49E3CA,0x6FCB44FB,0xAEBEE6C3,0x48625122), "{v3_|NOT}" }, + */ }; if (v3_tuid_match(iid, v3_audio_processor_iid)) From be5b1eba605ea25b1d781799ae1b8f2eeb058959 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 13 Jul 2022 21:43:03 +0100 Subject: [PATCH 465/504] Save host context during component initialize, fixes Studio One --- distrho/src/DistrhoPluginVST3.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index ce8d9c04..c3473329 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -151,6 +151,14 @@ const char* tuid2str(const v3_tuid iid) { V3_ID(0xE90FC54F,0x76F24235,0x8AF8BD15,0x68C663D6), "{v3_|NOT}" }, { V3_ID(0x07938E89,0xBA0D4CA8,0x8C7286AB,0xA9DDA95B), "{v3_|NOT}" }, { V3_ID(0x42879094,0xA2F145ED,0xAC90E82A,0x99458870), "{v3_|NOT}" }, + { V3_ID(0xC3B17BC0,0x2C174494,0x80293402,0xFBC4BBF8), "{v3_|NOT}" }, + { V3_ID(0x31E29A7A,0xE55043AD,0x8B95B9B8,0xDA1FBE1E), "{v3_|NOT}" }, + { V3_ID(0x8E3C292C,0x95924F9D,0xB2590B1E,0x100E4198), "{v3_|NOT}" }, + { V3_ID(0x50553FD9,0x1D2C4C24,0xB410F484,0xC5FB9F3F), "{v3_|NOT}" }, + { V3_ID(0xF185556C,0x5EE24FC7,0x92F28754,0xB7759EA8), "{v3_|NOT}" }, + { V3_ID(0xD2CE9317,0xF24942C9,0x9742E82D,0xB10CCC52), "{v3_|NOT}" }, + { V3_ID(0xDA57E6D1,0x1F3242D1,0xAD9C1A82,0xFDB95695), "{v3_|NOT}" }, + { V3_ID(0x3ABDFC3E,0x4B964A66,0xFCD86F10,0x0D554023), "{v3_|NOT}" }, // seen in the wild but unknown, related to view { V3_ID(0xAA3E50FF,0xB78840EE,0xADCD48E8,0x094CEDB7), "{v3_|NOT}" }, { V3_ID(0x2CAE14DB,0x4DE04C6E,0x8BD2E611,0x1B31A9C2), "{v3_|NOT}" }, @@ -3133,6 +3141,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { v3_host_application** const hostApplicationFromFactory; #if !DPF_VST3_USES_SEPARATE_CONTROLLER v3_host_application** const hostApplicationFromComponent; + v3_host_application** hostApplicationFromComponentInitialize; #endif v3_host_application** hostApplicationFromInitialize; @@ -3150,6 +3159,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { hostApplicationFromFactory(hostApp), #if !DPF_VST3_USES_SEPARATE_CONTROLLER hostApplicationFromComponent(hostComp), + hostApplicationFromComponentInitialize(nullptr), #endif hostApplicationFromInitialize(nullptr) { @@ -3576,6 +3586,8 @@ struct dpf_edit_controller : v3_edit_controller_cpp { #if !DPF_VST3_USES_SEPARATE_CONTROLLER : controller->hostApplicationFromComponent != nullptr ? controller->hostApplicationFromComponent + : controller->hostApplicationFromComponentInitialize != nullptr + ? controller->hostApplicationFromComponentInitialize #endif : controller->hostApplicationFromFactory; DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); @@ -4057,6 +4069,12 @@ struct dpf_component : v3_component_cpp { // save it for later so we can unref it component->hostApplicationFromInitialize = hostApplication; + #if !DPF_VST3_USES_SEPARATE_CONTROLLER + // save it in edit controller too, needed for some hosts + if (component->controller != nullptr) + component->controller->hostApplicationFromComponentInitialize = hostApplication; + #endif + // provide the factory application to the plugin if this new one is missing if (hostApplication == nullptr) hostApplication = component->hostApplicationFromFactory; @@ -4095,6 +4113,12 @@ struct dpf_component : v3_component_cpp { // delete actual plugin component->vst3 = nullptr; + #if !DPF_VST3_USES_SEPARATE_CONTROLLER + // remove previous host application saved during initialize + if (component->controller != nullptr) + component->controller->hostApplicationFromComponentInitialize = nullptr; + #endif + // unref host application received during initialize if (component->hostApplicationFromInitialize != nullptr) { From 34a62b6e284ce1009fbf2b27d366657a442ffcf7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 14 Jul 2022 20:16:17 +0100 Subject: [PATCH 466/504] Bring back ways to disable native fallback --- distrho/src/jackbridge/JackBridge.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index d417b5e2..02c3049b 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -43,7 +43,19 @@ # include "WebBridge.hpp" #endif -#if defined(HAVE_RTAUDIO) && defined(DISTRHO_PROPER_CPP11_SUPPORT) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 +#ifndef DISTRHO_PROPER_CPP11_SUPPORT +# undef HAVE_RTAUDIO +#endif + +#ifdef DPF_JACK_STANDALONE_SKIP_RTAUDIO_FALLBACK +# undef HAVE_RTAUDIO +#endif + +#ifdef DPF_JACK_STANDALONE_SKIP_SDL2_FALLBACK +# undef HAVE_SDL2 +#endif + +#if defined(HAVE_RTAUDIO) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 # include "RtAudioBridge.hpp" # ifdef RTAUDIO_API_TYPE # include "rtaudio/RtAudio.cpp" From 7f486d3836504874e1561dc4d46188b6ee637034 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Jul 2022 00:04:54 +0100 Subject: [PATCH 467/504] Share VST2 and VST3 keycode handling and a few other things --- distrho/src/DistrhoPluginInternal.hpp | 4 +- ...rhoPluginVST3.hpp => DistrhoPluginVST.hpp} | 145 +++++++++++++- distrho/src/DistrhoPluginVST2.cpp | 179 +++--------------- distrho/src/DistrhoPluginVST3.cpp | 20 +- distrho/src/DistrhoUIInternal.hpp | 51 ++--- distrho/src/DistrhoUIPrivateData.hpp | 2 +- distrho/src/DistrhoUIVST3.cpp | 86 ++++----- 7 files changed, 236 insertions(+), 251 deletions(-) rename distrho/src/{DistrhoPluginVST3.hpp => DistrhoPluginVST.hpp} (68%) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 9a94bb1b..37cdb7c9 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -20,7 +20,7 @@ #include "../DistrhoPlugin.hpp" #ifdef DISTRHO_PLUGIN_TARGET_VST3 -# include "DistrhoPluginVST3.hpp" +# include "DistrhoPluginVST.hpp" #endif #include diff --git a/distrho/src/DistrhoPluginVST3.hpp b/distrho/src/DistrhoPluginVST.hpp similarity index 68% rename from distrho/src/DistrhoPluginVST3.hpp rename to distrho/src/DistrhoPluginVST.hpp index a67c2218..7d08adc8 100644 --- a/distrho/src/DistrhoPluginVST3.hpp +++ b/distrho/src/DistrhoPluginVST.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -14,8 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef DISTRHO_PLUGIN_VST3_HPP_INCLUDED -#define DISTRHO_PLUGIN_VST3_HPP_INCLUDED +#ifndef DISTRHO_PLUGIN_VST_HPP_INCLUDED +#define DISTRHO_PLUGIN_VST_HPP_INCLUDED #include "DistrhoPluginChecks.h" #include "../DistrhoUtils.hpp" @@ -33,6 +33,10 @@ # define DISTRHO_PLUGIN_HAS_UI 0 #endif +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# include "Base.hpp" +#endif + #if DISTRHO_PLUGIN_HAS_UI == 1 && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS == 0 # define DPF_VST3_USES_SEPARATE_CONTROLLER 1 #else @@ -219,6 +223,18 @@ static void snprintf_utf16_t(int16_t* const dst, const T value, const char* cons std::free(tmpbuf); } +static inline +void snprintf_f32(char* const dst, const float value, const size_t size) +{ + return snprintf_t(dst, value, "%f", size); +} + +static inline +void snprintf_i32(char* const dst, const int32_t value, const size_t size) +{ + return snprintf_t(dst, value, "%d", size); +} + static inline void snprintf_u32(char* const dst, const uint32_t value, const size_t size) { @@ -232,11 +248,128 @@ void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size } static inline -void snprintf_i32_utf16(int16_t* const dst, const int value, const size_t size) +void snprintf_i32_utf16(int16_t* const dst, const int32_t value, const size_t size) +{ + return snprintf_utf16_t(dst, value, "%d", size); +} + +static inline +void snprintf_u32_utf16(int16_t* const dst, const uint32_t value, const size_t size) { - return snprintf_utf16_t(dst, value, "%d", size); + return snprintf_utf16_t(dst, value, "%u", size); } +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +// -------------------------------------------------------------------------------------------------------------------- +// translate a vstgui-based key character and code to matching values used by DPF + +static inline +uint translateVstKeyCode(bool& special, const int16_t keychar, const int16_t keycode) noexcept +{ + using namespace DGL_NAMESPACE; + + // special stuff first + special = true; + switch (keycode) + { + case 1: return kKeyBackspace; + // 2 \t (handled below) + // 3 clear + // 4 \r (handled below) + case 6: return kKeyEscape; + // 7 space (handled below) + // 8 next + // 17 select + // 18 print + // 19 \n (handled below) + // 20 snapshot + case 22: return kKeyDelete; + // 23 help + // 57 = (handled below) + // numpad stuff follows + // 24 0 (handled below) + // 25 1 (handled below) + // 26 2 (handled below) + // 27 3 (handled below) + // 28 4 (handled below) + // 29 5 (handled below) + // 30 6 (handled below) + // 31 7 (handled below) + // 32 8 (handled below) + // 33 9 (handled below) + // 34 * (handled below) + // 35 + (handled below) + // 36 separator + // 37 - (handled below) + // 38 . (handled below) + // 39 / (handled below) + // handle rest of special keys + /* these special keys are missing: + - kKeySuper + - kKeyCapsLock + - kKeyPrintScreen + */ + case 40: return kKeyF1; + case 41: return kKeyF2; + case 42: return kKeyF3; + case 43: return kKeyF4; + case 44: return kKeyF5; + case 45: return kKeyF6; + case 46: return kKeyF7; + case 47: return kKeyF8; + case 48: return kKeyF9; + case 49: return kKeyF10; + case 50: return kKeyF11; + case 51: return kKeyF12; + case 11: return kKeyLeft; + case 12: return kKeyUp; + case 13: return kKeyRight; + case 14: return kKeyDown; + case 15: return kKeyPageUp; + case 16: return kKeyPageDown; + case 10: return kKeyHome; + case 9: return kKeyEnd; + case 21: return kKeyInsert; + case 54: return kKeyShift; + case 55: return kKeyControl; + case 56: return kKeyAlt; + case 58: return kKeyMenu; + case 52: return kKeyNumLock; + case 53: return kKeyScrollLock; + case 5: return kKeyPause; + } + + // regular keys next + special = false; + switch (keycode) + { + case 2: return '\t'; + case 4: return '\r'; + case 7: return ' '; + case 19: return '\n'; + case 57: return '='; + case 24: return '0'; + case 25: return '1'; + case 26: return '2'; + case 27: return '3'; + case 28: return '4'; + case 29: return '5'; + case 30: return '6'; + case 31: return '7'; + case 32: return '8'; + case 33: return '9'; + case 34: return '*'; + case 35: return '+'; + case 37: return '-'; + case 38: return '.'; + case 39: return '/'; + } + + // fallback + return keychar; +} +#endif + // -------------------------------------------------------------------------------------------------------------------- // handy way to create a utf16 string from a utf8 one on the current function scope, used for message strings @@ -285,4 +418,4 @@ END_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- -#endif // DISTRHO_PLUGIN_VST3_HPP_INCLUDED +#endif // DISTRHO_PLUGIN_VST_HPP_INCLUDED diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index b158629d..8f47a98a 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -15,20 +15,11 @@ */ #include "DistrhoPluginInternal.hpp" +#include "DistrhoPluginVST.hpp" #include "../DistrhoPluginUtils.hpp" #include "../extra/ScopedSafeLocale.hpp" #include "../extra/ScopedPointer.hpp" -#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" # include "../extra/RingBuffer.hpp" @@ -85,37 +76,6 @@ static const requestParameterValueChangeFunc requestParameterValueChangeCallback // ----------------------------------------------------------------------- -void strncpy(char* const dst, const char* const src, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - - if (const size_t len = std::min(std::strlen(src), size-1U)) - { - std::memcpy(dst, src, len); - dst[len] = '\0'; - } - else - { - dst[0] = '\0'; - } -} - -void snprintf_param(char* const dst, const float value, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - std::snprintf(dst, size-1, "%f", value); - dst[size-1] = '\0'; -} - -void snprintf_iparam(char* const dst, const int32_t value, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - std::snprintf(dst, size-1, "%d", value); - dst[size-1] = '\0'; -} - -// ----------------------------------------------------------------------- - struct ParameterAndNotesHelper { float* parameterValues; @@ -257,85 +217,16 @@ public: # endif # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - int handlePluginKeyEvent(const bool down, int32_t index, const intptr_t value) + int handlePluginKeyEvent(const bool down, const int32_t index, const intptr_t value) { d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value); using namespace DGL_NAMESPACE; - switch (value) - { - // convert some VST2 special values to normal keys - case 1: index = kKeyBackspace; break; - case 2: index = '\t'; break; - // 3 clear - case 4: index = '\r'; break; - case 6: index = kKeyEscape; break; - case 7: index = ' '; break; - // 8 next - // 17 select - // 18 print - case 19: index = '\n'; break; - // 20 snapshot - case 22: index = kKeyDelete; break; - // 23 help - case 57: index = '='; break; - - // numpad stuff follows - case 24: index = '0'; break; - case 25: index = '1'; break; - case 26: index = '2'; break; - case 27: index = '3'; break; - case 28: index = '4'; break; - case 29: index = '5'; break; - case 30: index = '6'; break; - case 31: index = '7'; break; - case 32: index = '8'; break; - case 33: index = '9'; break; - case 34: index = '*'; break; - case 35: index = '+'; break; - // 36 separator - case 37: index = '-'; break; - case 38: index = '.'; break; - case 39: index = '/'; break; - - // handle rest of special keys - /* these special keys are missing: - - kKeySuper - - kKeyCapsLock - - kKeyPrintScreen - */ - case 40: index = kKeyF1; break; - case 41: index = kKeyF2; break; - case 42: index = kKeyF3; break; - case 43: index = kKeyF4; break; - case 44: index = kKeyF5; break; - case 45: index = kKeyF6; break; - case 46: index = kKeyF7; break; - case 47: index = kKeyF8; break; - case 48: index = kKeyF9; break; - case 49: index = kKeyF10; break; - case 50: index = kKeyF11; break; - case 51: index = kKeyF12; break; - case 11: index = kKeyLeft; break; - case 12: index = kKeyUp; break; - case 13: index = kKeyRight; break; - case 14: index = kKeyDown; break; - case 15: index = kKeyPageUp; break; - case 16: index = kKeyPageDown; break; - case 10: index = kKeyHome; break; - case 9: index = kKeyEnd; break; - case 21: index = kKeyInsert; break; - case 54: index = kKeyShift; break; - case 55: index = kKeyControl; break; - case 56: index = kKeyAlt; break; - case 58: index = kKeyMenu; break; - case 52: index = kKeyNumLock; break; - case 53: index = kKeyScrollLock; break; - case 5: index = kKeyPause; break; - } + bool special; + const uint key = translateVstKeyCode(special, index, static_cast(value)); - switch (index) + switch (key) { case kKeyShift: if (down) @@ -357,17 +248,9 @@ public: break; } - if (index > 0) - { - // keyboard events must always be lowercase - if (index >= 'A' && index <= 'Z') - index += 'a' - 'A'; // A-Z -> a-z - - fUI.handlePluginKeyboardVST2(down, static_cast(index), fKeyboardModifiers); - return 1; - } - - return 0; + return fUI.handlePluginKeyboardVST(down, special, key, + value >= 0 ? static_cast(value) : 0, + fKeyboardModifiers) ? 1 : 0; } # endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -490,7 +373,7 @@ public: fAudioMaster(audioMaster), fEffect(effect) { - std::memset(fProgramName, 0, sizeof(char)*(32+1)); + std::memset(fProgramName, 0, sizeof(fProgramName)); std::strcpy(fProgramName, "Default"); const uint32_t parameterCount = fPlugin.getParameterCount(); @@ -575,7 +458,7 @@ public: case effSetProgramName: if (char* const programName = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(fProgramName, programName, 32); + strncpy(fProgramName, programName, 32); return 1; } break; @@ -583,7 +466,7 @@ public: case effGetProgramName: if (char* const programName = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24); + strncpy(programName, fProgramName, 24); return 1; } break; @@ -591,7 +474,7 @@ public: case effGetProgramNameIndexed: if (char* const programName = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24); + strncpy(programName, fProgramName, 24); return 1; } break; @@ -621,14 +504,14 @@ public: if (d_isNotEqual(value, enumValues.values[i].value)) continue; - DISTRHO_NAMESPACE::strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24); + strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24); return 1; } if (hints & kParameterIsInteger) - DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)value, 24); + snprintf_i32((char*)ptr, (int32_t)value, 24); else - DISTRHO_NAMESPACE::snprintf_param((char*)ptr, value, 24); + snprintf_f32((char*)ptr, value, 24); return 1; } @@ -1196,7 +1079,7 @@ private: AEffect* const fEffect; // Temporary data - char fProgramName[32+1]; + char fProgramName[32]; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t fMidiEventCount; @@ -1477,7 +1360,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetParamLabel: if (ptr != nullptr && index < static_cast(sPlugin->getParameterCount())) { - DISTRHO_NAMESPACE::strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8); + strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8); return 1; } return 0; @@ -1487,9 +1370,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t { const String& shortName(sPlugin->getParameterShortName(index)); if (shortName.isNotEmpty()) - DISTRHO_NAMESPACE::strncpy((char*)ptr, shortName, 16); + strncpy((char*)ptr, shortName, 16); else - DISTRHO_NAMESPACE::strncpy((char*)ptr, sPlugin->getParameterName(index), 16); + strncpy((char*)ptr, sPlugin->getParameterName(index), 16); return 1; } return 0; @@ -1502,17 +1385,17 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t memset(properties, 0, sizeof(VstParameterProperties)); // full name - DISTRHO_NAMESPACE::strncpy(properties->label, - sPlugin->getParameterName(index), - sizeof(properties->label)); + strncpy(properties->label, + sPlugin->getParameterName(index), + sizeof(properties->label)); // short name const String& shortName(sPlugin->getParameterShortName(index)); if (shortName.isNotEmpty()) - DISTRHO_NAMESPACE::strncpy(properties->shortLabel, - sPlugin->getParameterShortName(index), - sizeof(properties->shortLabel)); + strncpy(properties->shortLabel, + sPlugin->getParameterShortName(index), + sizeof(properties->shortLabel)); // parameter hints const uint32_t hints = sPlugin->getParameterHints(index); @@ -1551,9 +1434,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t if (portGroup.groupId == groupId) { properties->category = i + 1; - DISTRHO_NAMESPACE::strncpy(properties->categoryLabel, - portGroup.name.buffer(), - sizeof(properties->categoryLabel)); + strncpy(properties->categoryLabel, + portGroup.name.buffer(), + sizeof(properties->categoryLabel)); break; } } @@ -1581,7 +1464,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetEffectName: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getName(), 32); + strncpy(cptr, sPlugin->getName(), 32); return 1; } return 0; @@ -1589,7 +1472,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetVendorString: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getMaker(), 32); + strncpy(cptr, sPlugin->getMaker(), 32); return 1; } return 0; @@ -1597,7 +1480,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetProductString: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getLabel(), 32); + strncpy(cptr, sPlugin->getLabel(), 32); return 1; } return 0; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index c3473329..c22706e6 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1019,22 +1019,22 @@ public: buffer[sizeof(buffer)-1] = '\xff'; v3_result res; - for (int32_t pos = 0, term = 0, read; term == 0; pos += read) + for (int32_t terminated = 0, read; terminated == 0;) { + read = -1; res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); if (read == 0) return V3_OK; - DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); - for (int32_t i = 0; i < read; ++i) { // found terminator, stop here if (buffer[i] == '\xfe') { - term = 1; + terminated = 1; break; } @@ -1808,9 +1808,9 @@ public: info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN; info->step_count = 127; char ccstr[24]; - snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", static_cast(index / 130) + 1, index % 130); + snprintf(ccstr, sizeof(ccstr), "MIDI Ch. %d CC %d", static_cast(index / 130) + 1, index % 130); strncpy_utf16(info->title, ccstr, 128); - snprintf(ccstr, sizeof(ccstr)-1, "Ch.%d CC%d", index / 130 + 1, index % 130); + snprintf(ccstr, sizeof(ccstr), "Ch.%d CC%d", index / 130 + 1, index % 130); strncpy_utf16(info->short_title, ccstr+5, 128); return V3_OK; } @@ -4300,10 +4300,10 @@ static const char* getPluginVersion() const uint32_t versionNum = getPluginInfo().getVersion(); char versionBuf[64]; - snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d", - (versionNum >> 16) & 0xff, - (versionNum >> 8) & 0xff, - (versionNum >> 0) & 0xff); + std::snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d", + (versionNum >> 16) & 0xff, + (versionNum >> 8) & 0xff, + (versionNum >> 0) & 0xff); versionBuf[sizeof(versionBuf)-1] = '\0'; version = versionBuf; } diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 4c1c877d..a2368011 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -329,31 +329,7 @@ public: } #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - bool handlePluginKeyboardVST2(const bool press, const uint key, const uint16_t mods) - { - DGL_NAMESPACE::Widget::KeyboardEvent ev; - ev.mod = mods; - ev.press = press; - ev.key = key; - - const bool ret = ui->onKeyboard(ev); - - if (! press) - return ret; - - DGL_NAMESPACE::Widget::CharacterInputEvent cev; - cev.mod = mods; - cev.character = key; - - // if shift modifier is on, convert a-z -> A-Z for character input - if (key >= 'a' && key <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) - cev.character -= 'a' - 'A'; - - ui->onCharacterInput(cev); - return ret; - } - - bool handlePluginKeyboardVST3(const bool press, const uint keychar, const uint keycode, const uint16_t mods) + bool handlePluginKeyboardVST(const bool press, const bool special, const uint keychar, const uint keycode, const uint16_t mods) { DGL_NAMESPACE::Widget::KeyboardEvent ev; ev.mod = mods; @@ -361,21 +337,26 @@ public: ev.key = keychar; ev.keycode = keycode; + // keyboard events must always be lowercase + if (ev.key >= 'A' && ev.key <= 'Z') + ev.key += 'a' - 'A'; // A-Z -> a-z + const bool ret = ui->onKeyboard(ev); - if (! press) - return ret; + if (press && !special && (mods & (kModifierControl|kModifierAlt|kModifierSuper)) == 0x0) + { + DGL_NAMESPACE::Widget::CharacterInputEvent cev; + cev.mod = mods; + cev.character = keychar; + cev.keycode = keycode; - DGL_NAMESPACE::Widget::CharacterInputEvent cev; - cev.mod = mods; - cev.keycode = keycode; - cev.character = keychar; + // if shift modifier is on, convert a-z -> A-Z for character input + if (cev.character >= 'a' && cev.character <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) + cev.character -= 'a' - 'A'; - // if shift modifier is on, convert a-z -> A-Z for character input - if (keychar >= 'a' && keychar <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) - cev.character -= 'a' - 'A'; + ui->onCharacterInput(cev); + } - ui->onCharacterInput(cev); return ret; } #endif diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 1cc23ccf..ee4503e0 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -20,7 +20,7 @@ #include "../DistrhoUI.hpp" #ifdef DISTRHO_PLUGIN_TARGET_VST3 -# include "DistrhoPluginVST3.hpp" +# include "DistrhoPluginVST.hpp" #endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 9e6674b2..3f7bb341 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -32,7 +32,6 @@ /* TODO items: * - mousewheel event - * - key down/up events * - file request? */ @@ -106,6 +105,31 @@ static void applyGeometryConstraints(const uint minimumWidth, // -------------------------------------------------------------------------------------------------------------------- +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +static uint translateVST3Modifiers(const int64_t modifiers) noexcept +{ + uint dglmods = 0; + if (modifiers & (1 << 0)) + dglmods |= kModifierShift; + if (modifiers & (1 << 1)) + dglmods |= kModifierAlt; + #ifdef DISTRHO_OS_MAC + if (modifiers & (1 << 2)) + dglmods |= kModifierSuper; + if (modifiers & (1 << 3)) + dglmods |= kModifierControl; + #else + if (modifiers & (1 << 2)) + dglmods |= kModifierControl; + if (modifiers & (1 << 3)) + dglmods |= kModifierSuper; + #endif + return dglmods; +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + /** * Helper class for getting a native idle timer, either through pugl or via native APIs. */ @@ -330,64 +354,28 @@ public: v3_result onKeyDown(const int16_t keychar, const int16_t keycode, const int16_t modifiers) { - d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); - using namespace DGL_NAMESPACE; + bool special; + const uint key = translateVstKeyCode(special, keychar, keycode); + d_debug("onKeyDown %d %d %x -> %d %d", keychar, keycode, modifiers, special, key); - // TODO - uint dglcode = 0; - - // TODO verify these - uint dglmods = 0; - if (modifiers & (1 << 0)) - dglmods |= kModifierShift; - if (modifiers & (1 << 1)) - dglmods |= kModifierAlt; - #ifdef DISTRHO_OS_MAC - if (modifiers & (1 << 2)) - dglmods |= kModifierSuper; - if (modifiers & (1 << 3)) - dglmods |= kModifierControl; - #else - if (modifiers & (1 << 2)) - dglmods |= kModifierControl; - if (modifiers & (1 << 3)) - dglmods |= kModifierSuper; - #endif - - return fUI.handlePluginKeyboardVST3(true, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; + return fUI.handlePluginKeyboardVST(true, special, key, + keycode >= 0 ? static_cast(keycode) : 0, + translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE; } v3_result onKeyUp(const int16_t keychar, const int16_t keycode, const int16_t modifiers) { - d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); - using namespace DGL_NAMESPACE; - - // TODO - uint dglcode = 0; - - // TODO verify these - uint dglmods = 0; - if (modifiers & (1 << 0)) - dglmods |= kModifierShift; - if (modifiers & (1 << 1)) - dglmods |= kModifierAlt; - #ifdef DISTRHO_OS_MAC - if (modifiers & (1 << 2)) - dglmods |= kModifierSuper; - if (modifiers & (1 << 3)) - dglmods |= kModifierControl; - #else - if (modifiers & (1 << 2)) - dglmods |= kModifierControl; - if (modifiers & (1 << 3)) - dglmods |= kModifierSuper; - #endif + bool special; + const uint key = translateVstKeyCode(special, keychar, keycode); + d_debug("onKeyUp %d %d %x -> %d %d", keychar, keycode, modifiers, special, key); - return fUI.handlePluginKeyboardVST3(false, static_cast(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; + return fUI.handlePluginKeyboardVST(false, special, key, + keycode >= 0 ? static_cast(keycode) : 0, + translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE; } v3_result onFocus(const bool state) From 45713e805e54d1b148bf59d80ce95016d517ab18 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Jul 2022 00:28:07 +0100 Subject: [PATCH 468/504] Fix build with without global namespace --- distrho/src/DistrhoUIInternal.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index a2368011..b4702246 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -331,7 +331,9 @@ public: #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI bool handlePluginKeyboardVST(const bool press, const bool special, const uint keychar, const uint keycode, const uint16_t mods) { - DGL_NAMESPACE::Widget::KeyboardEvent ev; + using namespace DGL_NAMESPACE; + + Widget::KeyboardEvent ev; ev.mod = mods; ev.press = press; ev.key = keychar; @@ -343,15 +345,15 @@ public: const bool ret = ui->onKeyboard(ev); - if (press && !special && (mods & (kModifierControl|kModifierAlt|kModifierSuper)) == 0x0) + if (press && !special && (mods & (kModifierControl|kModifierAlt|kModifierSuper)) == 0) { - DGL_NAMESPACE::Widget::CharacterInputEvent cev; + Widget::CharacterInputEvent cev; cev.mod = mods; cev.character = keychar; cev.keycode = keycode; // if shift modifier is on, convert a-z -> A-Z for character input - if (cev.character >= 'a' && cev.character <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) + if (cev.character >= 'a' && cev.character <= 'z' && (mods & kModifierShift) != 0) cev.character -= 'a' - 'A'; ui->onCharacterInput(cev); From 3a6995163a6807d127be76a953b2726726f833ca Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Jul 2022 00:45:58 +0100 Subject: [PATCH 469/504] One more fix --- distrho/src/DistrhoUIVST3.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 3f7bb341..f3c76224 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -108,6 +108,8 @@ static void applyGeometryConstraints(const uint minimumWidth, #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI static uint translateVST3Modifiers(const int64_t modifiers) noexcept { + using namespace DGL_NAMESPACE; + uint dglmods = 0; if (modifiers & (1 << 0)) dglmods |= kModifierShift; @@ -124,6 +126,7 @@ static uint translateVST3Modifiers(const int64_t modifiers) noexcept if (modifiers & (1 << 3)) dglmods |= kModifierSuper; #endif + return dglmods; } #endif From 98ecaa0255b1ec55b1d4161470d4969ed2c79d0c Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Jul 2022 10:25:15 +0100 Subject: [PATCH 470/504] Modify asserts from nanovg failure --- dgl/src/NanoVG.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index 15c1b6a2..ee616adb 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -318,12 +318,12 @@ NanoVG::NanoVG(int flags) fInFrame(false), fIsSubWidget(false) { - DISTRHO_SAFE_ASSERT(fContext); + DISTRHO_CUSTOM_SAFE_ASSERT("Failed to create NanoVG context, expect a black screen", fContext != nullptr); } NanoVG::~NanoVG() { - DISTRHO_SAFE_ASSERT(! fInFrame); + DISTRHO_CUSTOM_SAFE_ASSERT("Destroying NanoVG context with still active frame", ! fInFrame); if (fContext != nullptr && ! fIsSubWidget) nvgDeleteGL(fContext); From 41da9887ed1a38f680202909f3fd36a18dd91df7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Jul 2022 10:38:04 +0100 Subject: [PATCH 471/504] Add fallback functions for old Windows drivers --- dgl/src/NanoVG.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/dgl/src/NanoVG.cpp b/dgl/src/NanoVG.cpp index ee616adb..42c0fcde 100644 --- a/dgl/src/NanoVG.cpp +++ b/dgl/src/NanoVG.cpp @@ -55,9 +55,9 @@ DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) # ifdef DGL_USE_NANOVG_FBO +DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) -DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) @@ -140,6 +140,11 @@ NVGcontext* nvgCreateGL(int flags) # define DGL_EXT(PROC, func) \ if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); +# define DGL_EXT2(PROC, func, fallback) \ + if (needsInit) { \ + func = (PROC) wglGetProcAddress ( #func ); \ + if (func == nullptr) func = (PROC) wglGetProcAddress ( #fallback ); \ + } DISTRHO_SAFE_ASSERT_RETURN(func != nullptr, nullptr); DGL_EXT(PFNGLACTIVETEXTUREPROC, glActiveTexture) DGL_EXT(PFNGLATTACHSHADERPROC, glAttachShader) DGL_EXT(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation) @@ -169,16 +174,16 @@ DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) # ifdef DGL_USE_NANOVG_FBO -DGL_EXT(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer) -DGL_EXT(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) DGL_EXT(PFNGLCHECKFRAMEBUFFERSTATUSPROC, glCheckFramebufferStatus) -DGL_EXT(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers) -DGL_EXT(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers) -DGL_EXT(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D) -DGL_EXT(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) -DGL_EXT(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers) -DGL_EXT(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) -DGL_EXT(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) +DGL_EXT2(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer, glBindFramebufferEXT) +DGL_EXT2(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer, glBindRenderbufferEXT) +DGL_EXT2(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers, glDeleteFramebuffersEXT) +DGL_EXT2(PFNGLDELETERENDERBUFFERSPROC, glDeleteRenderbuffers, glDeleteRenderbuffersEXT) +DGL_EXT2(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D, glFramebufferTexture2DEXT) +DGL_EXT2(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer, glFramebufferRenderbufferEXT) +DGL_EXT2(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers, glGenFramebuffersEXT) +DGL_EXT2(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers, glGenRenderbuffersEXT) +DGL_EXT2(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage, glRenderbufferStorageEXT) # endif # ifdef DGL_USE_OPENGL3 DGL_EXT(PFNGLBINDBUFFERRANGEPROC, glBindBufferRange) @@ -190,6 +195,7 @@ DGL_EXT(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays) DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding) # endif # undef DGL_EXT +# undef DGL_EXT2 needsInit = false; # if defined(__GNUC__) && (__GNUC__ >= 9) # pragma GCC diagnostic pop From aaa0f5a5fe62d7e898dc69ddc36fd2800e419da4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Jul 2022 13:53:03 +0100 Subject: [PATCH 472/504] Remove wrong macro for macOS build, was unused anyway --- Makefile.plugins.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index c848d9d7..d419de76 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -400,7 +400,7 @@ $(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTR $(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUI_macOS.mm ($*)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -ObjC++ -c -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) From 0c0a4e401c2a552145720904b916da21846c8fc1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 15 Jul 2022 14:38:30 +0100 Subject: [PATCH 473/504] Fix a pugl-based build warning Signed-off-by: falkTX --- dgl/Makefile | 36 ++++++++++++++++++++++-------------- dgl/src/pugl-upstream | 2 +- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/dgl/Makefile b/dgl/Makefile index 58e2057b..be92632c 100644 --- a/dgl/Makefile +++ b/dgl/Makefile @@ -15,12 +15,8 @@ LINK_FLAGS += $(DGL_LIBS) ifeq ($(MACOS),true) BUILD_CXX_FLAGS += -Wno-deprecated-declarations -endif - -# TODO fix these after pugl-upstream is done -BUILD_CXX_FLAGS += -Wno-attributes -Wno-extra -Wno-missing-field-initializers -ifneq ($(MACOS),true) -BUILD_CXX_FLAGS += -Wno-narrowing +else +PUGL_EXTRA_FLAGS = -Wno-extra -Wmissing-field-initializers endif # ifneq ($(MACOS_OLD),true) @@ -190,51 +186,63 @@ vulkan: ../build/libdgl-vulkan.a # --------------------------------------------------------------------------------------------------------------------- +../build/dgl/pugl.cpp.o: src/pugl.cpp + -@mkdir -p ../build/dgl + @echo "Compiling $<" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -o $@ + +../build/dgl/pugl.mm.o: src/pugl.mm + -@mkdir -p ../build/dgl + @echo "Compiling $<" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -ObjC++ -o $@ + +# --------------------------------------------------------------------------------------------------------------------- + ../build/dgl/%.cpp.cairo.o: src/%.cpp -@mkdir -p ../build/dgl @echo "Compiling $< (Cairo variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ ../build/dgl/%.mm.cairo.o: src/%.mm -@mkdir -p ../build/dgl @echo "Compiling $< (Cairo variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- ../build/dgl/%.cpp.opengl.o: src/%.cpp -@mkdir -p ../build/dgl @echo "Compiling $< (OpenGL variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ ../build/dgl/%.mm.opengl.o: src/%.mm -@mkdir -p ../build/dgl @echo "Compiling $< (OpenGL variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- ../build/dgl/%.cpp.opengl3.o: src/%.cpp -@mkdir -p ../build/dgl @echo "Compiling $< (OpenGL3 variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ ../build/dgl/%.mm.opengl3.o: src/%.mm -@mkdir -p ../build/dgl @echo "Compiling $< (OpenGL3 variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- ../build/dgl/%.cpp.vulkan.o: src/%.cpp -@mkdir -p ../build/dgl @echo "Compiling $< (Vulkan variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@ ../build/dgl/%.mm.vulkan.o: src/%.mm -@mkdir -p ../build/dgl @echo "Compiling $< (Vulkan variant)" - $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 1c669b15..856c759e 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 1c669b15877b04c1fc8cff9d37bc336ef0ff7808 +Subproject commit 856c759e1e360b0a766f61bb2b2cfffe15790368 From 1aa5892a13d7797935964443ae7dda6e08c470cd Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 00:29:17 +0100 Subject: [PATCH 474/504] Align nanovg font kerning to integer pixel positions if needed Signed-off-by: falkTX --- dgl/src/nanovg/nanovg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dgl/src/nanovg/nanovg.c b/dgl/src/nanovg/nanovg.c index 0c75b17f..c92af25b 100644 --- a/dgl/src/nanovg/nanovg.c +++ b/dgl/src/nanovg/nanovg.c @@ -2592,6 +2592,11 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* nvgTransformPoint(&c[6],&c[7], state->xform, q.x0*invscale, q.y1*invscale); // Create triangles if (nverts+6 <= cverts) { +#if NVG_FONT_TEXTURE_FLAGS + // align font kerning to integer pixel positions + for (int i = 0; i < 8; ++i) + c[i] = (int)(c[i] + 0.5f); +#endif nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++; nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++; nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0); nverts++; From 17eecabd4011e087a49a2144164bee1ec3645ade Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 06:46:08 +0100 Subject: [PATCH 475/504] Deal a bit more with VST3 buses and arrangements Signed-off-by: falkTX --- distrho/DistrhoPlugin.hpp | 3 + distrho/src/DistrhoPluginInternal.hpp | 26 ++ distrho/src/DistrhoPluginVST3.cpp | 383 ++++++++++++++++++-------- 3 files changed, 299 insertions(+), 113 deletions(-) diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 964acdc7..aed60510 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -636,6 +636,9 @@ struct Parameter { A group can be applied to both inputs and outputs (at the same time). The same group cannot be used in audio ports and parameters. + When both audio and parameter groups are used, audio groups MUST be defined first. + That is, group indexes start with audio ports, then parameters. + An audio port group logically combines ports which should be considered part of the same stream.@n For example, two audio ports in a group may form a stereo stream. diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 37cdb7c9..932019fe 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -674,6 +674,32 @@ public: return fData->portGroupCount; } + uint32_t getPortCountWithGroupId(const bool input, const uint32_t groupId) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); + + uint32_t numPorts = 0; + + if (input) + { + for (uint32_t i=0; i<(input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS); ++i) + { + if (fData->audioPorts[i].groupId == groupId) + ++numPorts; + } + } + else + { + for (uint32_t i=0; i<(input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS); ++i) + { + if (fData->audioPorts[i + DISTRHO_PLUGIN_NUM_INPUTS].groupId == groupId) + ++numPorts; + } + } + + return numPorts; + } + const PortGroupWithId& getPortGroupById(const uint32_t groupId) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && fData->portGroupCount != 0, sFallbackPortGroup); diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index c22706e6..2ad54c22 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -276,13 +276,15 @@ class PluginVst3 uint32_t numMainAudio; uint32_t numSidechain; uint32_t numCV; + uint32_t numGroups; BusInfo() : audio(0), sidechain(0), numMainAudio(0), numSidechain(0), - numCV(0) {} + numCV(0), + numGroups(0) {} } inputBuses, outputBuses; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -601,16 +603,28 @@ public: #endif { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + std::vector visitedInputPortGroups; for (uint32_t i=0; i::iterator end = visitedInputPortGroups.end(); + if (std::find(visitedInputPortGroups.begin(), end, port.groupId) == end) + { + visitedInputPortGroups.push_back(port.groupId); + ++inputBuses.numGroups; + } + continue; + } - if (hints & kAudioPortIsCV) + if (port.hints & kAudioPortIsCV) ++inputBuses.numCV; else ++inputBuses.numMainAudio; - if (hints & kAudioPortIsSidechain) + if (port.hints & kAudioPortIsSidechain) ++inputBuses.numSidechain; } @@ -624,25 +638,46 @@ public: { AudioPortWithBusId& port(fPlugin.getAudioPort(true, i)); - if (port.hints & kAudioPortIsCV) - port.busId = inputBuses.audio + inputBuses.sidechain + cvInputBusId++; - else if (port.hints & kAudioPortIsSidechain) - port.busId = inputBuses.audio; + if (port.groupId != kPortGroupNone) + { + port.busId = port.groupId; + } else - port.busId = 0; + { + if (port.hints & kAudioPortIsCV) + port.busId = inputBuses.audio + inputBuses.sidechain + cvInputBusId++; + else if (port.hints & kAudioPortIsSidechain) + port.busId = inputBuses.audio; + else + port.busId = 0; + + port.busId += inputBuses.numGroups; + } } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + std::vector visitedOutputPortGroups; for (uint32_t i=0; i::iterator end = visitedOutputPortGroups.end(); + if (std::find(visitedOutputPortGroups.begin(), end, port.groupId) == end) + { + visitedOutputPortGroups.push_back(port.groupId); + ++outputBuses.numGroups; + } + continue; + } - if (hints & kAudioPortIsCV) + if (port.hints & kAudioPortIsCV) ++outputBuses.numCV; else ++outputBuses.numMainAudio; - if (hints & kAudioPortIsSidechain) + if (port.hints & kAudioPortIsSidechain) ++outputBuses.numSidechain; } @@ -656,12 +691,21 @@ public: { AudioPortWithBusId& port(fPlugin.getAudioPort(false, i)); - if (port.hints & kAudioPortIsCV) - port.busId = outputBuses.audio + outputBuses.sidechain + cvOutputBusId++; - else if (port.hints & kAudioPortIsSidechain) - port.busId = outputBuses.audio; + if (port.groupId != kPortGroupNone) + { + port.busId = port.groupId; + } else - port.busId = 0; + { + if (port.hints & kAudioPortIsCV) + port.busId = outputBuses.audio + outputBuses.sidechain + cvOutputBusId++; + else if (port.hints & kAudioPortIsSidechain) + port.busId = outputBuses.audio; + else + port.busId = 0; + + port.busId += outputBuses.numGroups; + } } #endif @@ -783,9 +827,9 @@ public: { case V3_AUDIO: if (busDirection == V3_INPUT) - return inputBuses.audio + inputBuses.sidechain + inputBuses.numCV; + return inputBuses.audio + inputBuses.sidechain + inputBuses.numCV + inputBuses.numGroups; if (busDirection == V3_OUTPUT) - return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV; + return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV + outputBuses.numGroups; break; case V3_EVENT: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -812,7 +856,7 @@ public: DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG); #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 || DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - const uint32_t busId = static_cast(busIndex); + uint32_t busId = static_cast(busIndex); #endif if (mediaType == V3_AUDIO) @@ -826,49 +870,96 @@ public: if (busDirection == V3_INPUT) { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - switch (busId) + if (busId < inputBuses.numGroups) { - case 0: - if (inputBuses.audio) - { - numChannels = inputBuses.numMainAudio; - busType = V3_MAIN; - flags = V3_DEFAULT_ACTIVE; - break; - } - // fall-through - case 1: - if (inputBuses.sidechain) - { - numChannels = inputBuses.numSidechain; - busType = V3_AUX; - flags = 0; - break; - } - // fall-through - default: - numChannels = 1; + numChannels = fPlugin.getPortCountWithGroupId(true, busId); busType = V3_AUX; - flags = V3_IS_CONTROL_VOLTAGE; - break; - } + flags = 0; - if (busType == V3_MAIN) - { - strncpy_utf16(busName, "Audio Input", 128); - } - else - { for (uint32_t i=0; i 0 - switch (busId) + if (busId < outputBuses.numGroups) { - case 0: - if (outputBuses.audio) - { - numChannels = outputBuses.numMainAudio; - busType = V3_MAIN; - flags = V3_DEFAULT_ACTIVE; - break; - } - // fall-through - case 1: - if (outputBuses.sidechain) - { - numChannels = outputBuses.numSidechain; - busType = V3_AUX; - flags = 0; - break; - } - // fall-through - default: - numChannels = 1; + numChannels = fPlugin.getPortCountWithGroupId(false, busId); busType = V3_AUX; - flags = V3_IS_CONTROL_VOLTAGE; - break; - } + flags = 0; - if (busType == V3_MAIN) - { - strncpy_utf16(busName, "Audio Output", 128); - } - else - { for (uint32_t i=0; i= 0, busId, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(speaker != nullptr, V3_INVALID_ARG); #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - const uint32_t ubusId = static_cast(busId); + uint32_t busId = static_cast(busIndex); #endif if (busDirection == V3_INPUT) @@ -1344,7 +1483,7 @@ public: { AudioPortWithBusId& port(fPlugin.getAudioPort(true, i)); - if (port.busId != ubusId) + if (port.busId != busId) continue; v3_speaker_arrangement arr; @@ -1358,25 +1497,34 @@ public: arr = V3_SPEAKER_L | V3_SPEAKER_R; break; default: - return V3_INVALID_ARG; - /* - if (inputBuses.audio != 0 && ubusId == 0) + if (busId < inputBuses.numGroups) { + const uint32_t numPortsInBus = fPlugin.getPortCountWithGroupId(true, busId); arr = 0x0; - for (uint32_t j=0; j Date: Fri, 22 Jul 2022 07:08:19 +0100 Subject: [PATCH 476/504] Fix build after last changes Signed-off-by: falkTX --- distrho/src/DistrhoPluginInternal.hpp | 56 ++++++++++++++------------- distrho/src/DistrhoPluginVST3.cpp | 12 +++--- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 932019fe..94868aee 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -523,6 +523,36 @@ public: { return getAudioPort(input, index).hints; } + + uint32_t getAudioPortCountWithGroupId(const bool input, const uint32_t groupId) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); + + uint32_t numPorts = 0; + + if (input) + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; iaudioPorts[i].groupId == groupId) + ++numPorts; + } + #endif + } + else + { + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + for (uint32_t i=0; iaudioPorts[i + DISTRHO_PLUGIN_NUM_INPUTS].groupId == groupId) + ++numPorts; + } + #endif + } + + return numPorts; + } #endif uint32_t getParameterCount() const noexcept @@ -674,32 +704,6 @@ public: return fData->portGroupCount; } - uint32_t getPortCountWithGroupId(const bool input, const uint32_t groupId) const noexcept - { - DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); - - uint32_t numPorts = 0; - - if (input) - { - for (uint32_t i=0; i<(input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS); ++i) - { - if (fData->audioPorts[i].groupId == groupId) - ++numPorts; - } - } - else - { - for (uint32_t i=0; i<(input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS); ++i) - { - if (fData->audioPorts[i + DISTRHO_PLUGIN_NUM_INPUTS].groupId == groupId) - ++numPorts; - } - } - - return numPorts; - } - const PortGroupWithId& getPortGroupById(const uint32_t groupId) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && fData->portGroupCount != 0, sFallbackPortGroup); diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 2ad54c22..a73f860c 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -872,7 +872,7 @@ public: #if DISTRHO_PLUGIN_NUM_INPUTS > 0 if (busId < inputBuses.numGroups) { - numChannels = fPlugin.getPortCountWithGroupId(true, busId); + numChannels = fPlugin.getAudioPortCountWithGroupId(true, busId); busType = V3_AUX; flags = 0; @@ -972,7 +972,7 @@ public: #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 if (busId < outputBuses.numGroups) { - numChannels = fPlugin.getPortCountWithGroupId(false, busId); + numChannels = fPlugin.getAudioPortCountWithGroupId(false, busId); busType = V3_AUX; flags = 0; @@ -1499,7 +1499,7 @@ public: default: if (busId < inputBuses.numGroups) { - const uint32_t numPortsInBus = fPlugin.getPortCountWithGroupId(true, busId); + const uint32_t numPortsInBus = fPlugin.getAudioPortCountWithGroupId(true, busId); arr = 0x0; for (uint32_t j=0; j Date: Fri, 22 Jul 2022 15:38:14 +0100 Subject: [PATCH 477/504] Implement buffer size changes in RtAudio native audio fallback --- distrho/src/jackbridge/RtAudioBridge.hpp | 44 +++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index 146ed149..f80f7d1f 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -42,6 +42,7 @@ # include "rtaudio/RtAudio.h" # undef Point # include "../../extra/ScopedPointer.hpp" +# include "../../extra/String.hpp" using DISTRHO_NAMESPACE::ScopedPointer; @@ -49,6 +50,10 @@ struct RtAudioBridge : NativeBridge { // pointer to RtAudio instance ScopedPointer handle; + // for buffer size changes + String name; + uint nextBufferSize = 512; + const char* getVersion() const noexcept { return RTAUDIO_VERSION; @@ -62,7 +67,7 @@ struct RtAudioBridge : NativeBridge { rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE); } DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false); - uint rtAudioBufferFrames = 512; + uint rtAudioBufferFrames = nextBufferSize; #if DISTRHO_PLUGIN_NUM_INPUTS > 0 RtAudio::StreamParameters inParams; @@ -94,6 +99,7 @@ struct RtAudioBridge : NativeBridge { return false; } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); + name = clientName; handle = rtAudio; bufferSize = rtAudioBufferFrames; sampleRate = handle->getStreamSampleRate(); @@ -137,6 +143,42 @@ struct RtAudioBridge : NativeBridge { return true; } + /* RtAudio in macOS uses a different than usual way to handle audio block size, + * where RTAUDIO_MINIMIZE_LATENCY makes CoreAudio use very low latencies (around 15 samples). + * As such, dynamic buffer sizes are meaningless there. + */ + #ifndef DISTRHO_OS_MAC + bool supportsBufferSizeChanges() const override + { + return true; + } + + bool requestBufferSizeChange(const uint32_t newBufferSize) override + { + // stop audio first + deactivate(); + close(); + + // try to open with new buffer size + nextBufferSize = newBufferSize; + + const bool ok = open(name); + + if (!ok) + { + // revert to old buffer size if new one failed + nextBufferSize = bufferSize; + open(name); + } + + if (bufferSizeCallback != nullptr) + bufferSizeCallback(bufferSize, jackBufferSizeArg); + + activate(); + return ok; + } + #endif + static int RtAudioCallback(void* const outputBuffer, #if DISTRHO_PLUGIN_NUM_INPUTS > 0 void* const inputBuffer, From 25f35624837319827d40432dea07e01c54f4cba4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 15:38:37 +0100 Subject: [PATCH 478/504] Add isUsingNativeAudio() to standalone function utils --- distrho/DistrhoStandaloneUtils.hpp | 19 ++++++++++++++++--- distrho/src/jackbridge/JackBridge.cpp | 9 +++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/distrho/DistrhoStandaloneUtils.hpp b/distrho/DistrhoStandaloneUtils.hpp index 00d3ada9..8fffcdaa 100644 --- a/distrho/DistrhoStandaloneUtils.hpp +++ b/distrho/DistrhoStandaloneUtils.hpp @@ -25,11 +25,24 @@ START_NAMESPACE_DISTRHO * Standalone plugin related utilities */ /** - @defgroup PluginRelatedUtilities Plugin related utilities + @defgroup StandalonePluginRelatedUtilities Plugin related utilities + + When the plugin is running as standalone and JACK is not available, a native audio handling is in place. + It is a very simple handling, auto-connecting to the default audio interface for outputs. + + !!EXPERIMENTAL!! + + Still under development and testing. @{ */ +/** + Check if the current standalone is using native audio methods. + If this function returns false, you MUST NOT use any other function from this group. +*/ +bool isUsingNativeAudio() noexcept; + /** Check if the current standalone supports audio input. */ @@ -58,7 +71,7 @@ bool isMIDIEnabled(); /** Get the current buffer size. */ -uint32_t getBufferSize(); +uint getBufferSize(); /** Request permissions to use audio input. @@ -69,7 +82,7 @@ bool requestAudioInput(); /** Request change to a new buffer size. */ -bool requestBufferSizeChange(uint32_t newBufferSize); +bool requestBufferSizeChange(uint newBufferSize); /** Request permissions to use MIDI. diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 02c3049b..e57da3e1 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -2265,6 +2265,15 @@ bool jackbridge_set_property_change_callback(jack_client_t* client, JackProperty START_NAMESPACE_DISTRHO +bool isUsingNativeAudio() noexcept +{ +#if defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT) + return false; +#else + return usingNativeBridge; +#endif +} + bool supportsAudioInput() { #if defined(JACKBRIDGE_DUMMY) From ab2cde1758d457bf481747e720f245c879ef678d Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 15:43:32 +0100 Subject: [PATCH 479/504] Make standalone util symbols visible for any build --- distrho/src/DistrhoUtils.cpp | 15 +++++++++++++++ distrho/src/jackbridge/JackBridge.cpp | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/distrho/src/DistrhoUtils.cpp b/distrho/src/DistrhoUtils.cpp index fc0ff25a..aeb7b2b4 100644 --- a/distrho/src/DistrhoUtils.cpp +++ b/distrho/src/DistrhoUtils.cpp @@ -19,6 +19,7 @@ #endif #include "../extra/String.hpp" +#include "../DistrhoStandaloneUtils.hpp" #ifdef DISTRHO_OS_WINDOWS # include @@ -141,6 +142,20 @@ const char* getResourcePath(const char* const bundlePath) noexcept return nullptr; } +#ifndef DISTRHO_PLUGIN_TARGET_JACK +// all these are null for non-standalone targets +bool isUsingNativeAudio() noexcept { return false; } +bool supportsAudioInput() { return false; } +bool supportsBufferSizeChanges() { return false; } +bool supportsMIDI() { return false; } +bool isAudioInputEnabled() { return false; } +bool isMIDIEnabled() { return false; } +uint getBufferSize() { return 0; } +bool requestAudioInput() { return false; } +bool requestBufferSizeChange(uint) { return false; } +bool requestMIDI() { return false; } +#endif + // ----------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index e57da3e1..1b51ca5f 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -2327,7 +2327,7 @@ bool isMIDIEnabled() return true; } -uint32_t getBufferSize() +uint getBufferSize() { #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) if (usingNativeBridge) @@ -2345,7 +2345,7 @@ bool requestAudioInput() return false; } -bool requestBufferSizeChange(const uint32_t newBufferSize) +bool requestBufferSizeChange(const uint newBufferSize) { #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) if (usingNativeBridge) From f2be04905f987f35d69f952cebb9ba8e5f80b6d5 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 15:57:30 +0100 Subject: [PATCH 480/504] Fix no namespace build --- distrho/src/jackbridge/RtAudioBridge.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index f80f7d1f..af00ec25 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -45,6 +45,7 @@ # include "../../extra/String.hpp" using DISTRHO_NAMESPACE::ScopedPointer; +using DISTRHO_NAMESPACE::String; struct RtAudioBridge : NativeBridge { // pointer to RtAudio instance From d200db475b74a20e244bbfc2031527666c614eb4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 21:10:40 +0100 Subject: [PATCH 481/504] Make RtAudio capture opt-in, add MIDI input --- Makefile.plugins.mk | 2 +- distrho/src/jackbridge/JackBridge.cpp | 8 + distrho/src/jackbridge/NativeBridge.hpp | 8 +- distrho/src/jackbridge/RtAudioBridge.hpp | 268 +- distrho/src/jackbridge/SDL2Bridge.hpp | 2 +- distrho/src/jackbridge/WebBridge.hpp | 6 +- distrho/src/jackbridge/rtmidi/LICENSE | 27 + distrho/src/jackbridge/rtmidi/RtMidi.cpp | 3930 ++++++++++++++++++++++ distrho/src/jackbridge/rtmidi/RtMidi.h | 658 ++++ 9 files changed, 4856 insertions(+), 53 deletions(-) create mode 100644 distrho/src/jackbridge/rtmidi/LICENSE create mode 100644 distrho/src/jackbridge/rtmidi/RtMidi.cpp create mode 100644 distrho/src/jackbridge/rtmidi/RtMidi.h diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index d419de76..d1c0c955 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -81,7 +81,7 @@ endif else ifneq ($(SKIP_RTAUDIO_FALLBACK),true) ifeq ($(MACOS),true) -JACK_LIBS += -framework CoreAudio -framework CoreFoundation +JACK_LIBS += -framework CoreAudio -framework CoreFoundation -framework CoreMIDI else ifeq ($(WINDOWS),true) JACK_LIBS += -lole32 -lwinmm # DirectSound diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 1b51ca5f..a73d7e91 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -56,10 +56,18 @@ #endif #if defined(HAVE_RTAUDIO) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 +// fix conflict between DGL and macOS names +# define Point CorePoint +# define Size CoreSize # include "RtAudioBridge.hpp" # ifdef RTAUDIO_API_TYPE # include "rtaudio/RtAudio.cpp" # endif +# ifdef RTMIDI_API_TYPE +# include "rtmidi/RtMidi.cpp" +# endif +# undef Point +# undef Size #endif #if defined(HAVE_SDL2) && DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp index 30e26db7..b30e8bf8 100644 --- a/distrho/src/jackbridge/NativeBridge.hpp +++ b/distrho/src/jackbridge/NativeBridge.hpp @@ -205,10 +205,12 @@ struct NativeBridge { (void)time; } - void allocBuffers() + void allocBuffers(const bool audio, const bool midi) { DISTRHO_SAFE_ASSERT_RETURN(bufferSize != 0,); + if (audio) + { #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; @@ -219,7 +221,10 @@ struct NativeBridge { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS); #endif + } + if (midi) + { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512); midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512); @@ -227,6 +232,7 @@ struct NativeBridge { #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT midiOutBuffer.createBuffer(2048); #endif + } } void freeBuffers() diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index af00ec25..5c72f549 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -26,21 +26,24 @@ #if defined(DISTRHO_OS_MAC) # define __MACOSX_CORE__ # define RTAUDIO_API_TYPE MACOSX_CORE +# define RTMIDI_API_TYPE MACOSX_CORE #elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) # define __WINDOWS_DS__ # define RTAUDIO_API_TYPE WINDOWS_DS +# define RTMIDI_API_TYPE WINDOWS_MM #elif defined(HAVE_PULSEAUDIO) # define __LINUX_PULSE__ # define RTAUDIO_API_TYPE LINUX_PULSE +# define RTMIDI_API_TYPE LINUX_ALSA #elif defined(HAVE_ALSA) # define __LINUX_ALSA__ # define RTAUDIO_API_TYPE LINUX_ALSA +# define RTMIDI_API_TYPE LINUX_ALSA #endif #ifdef RTAUDIO_API_TYPE -# define Point CorePoint /* fix conflict between DGL and macOS Point name */ # include "rtaudio/RtAudio.h" -# undef Point +# include "rtmidi/RtMidi.h" # include "../../extra/ScopedPointer.hpp" # include "../../extra/String.hpp" @@ -50,11 +53,27 @@ using DISTRHO_NAMESPACE::String; struct RtAudioBridge : NativeBridge { // pointer to RtAudio instance ScopedPointer handle; +#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + bool captureEnabled = false; +#endif + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT + std::vector midiIns; + #endif + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + std::vector midiOuts; + #endif - // for buffer size changes + // caching String name; uint nextBufferSize = 512; + RtAudioBridge() + { + #if defined(RTMIDI_API_TYPE) && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) + midiAvailable = true; + #endif + } + const char* getVersion() const noexcept { return RTAUDIO_VERSION; @@ -62,49 +81,8 @@ struct RtAudioBridge : NativeBridge { bool open(const char* const clientName) override { - ScopedPointer rtAudio; - - try { - rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE); - } DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false); - - uint rtAudioBufferFrames = nextBufferSize; - - #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - RtAudio::StreamParameters inParams; - inParams.deviceId = rtAudio->getDefaultInputDevice(); - inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS; - RtAudio::StreamParameters* const inParamsPtr = &inParams; - #else - RtAudio::StreamParameters* const inParamsPtr = nullptr; - #endif - - #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - RtAudio::StreamParameters outParams; - outParams.deviceId = rtAudio->getDefaultOutputDevice(); - outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS; - RtAudio::StreamParameters* const outParamsPtr = &outParams; - #else - RtAudio::StreamParameters* const outParamsPtr = nullptr; - #endif - - RtAudio::StreamOptions opts; - opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_ALSA_USE_DEFAULT; - opts.streamName = clientName; - - try { - rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, - RtAudioCallback, this, &opts, nullptr); - } catch (const RtAudioError& err) { - d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); - return false; - } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); - name = clientName; - handle = rtAudio; - bufferSize = rtAudioBufferFrames; - sampleRate = handle->getStreamSampleRate(); - return true; + return _open(false); } bool close() override @@ -118,6 +96,9 @@ struct RtAudioBridge : NativeBridge { } DISTRHO_SAFE_EXCEPTION("handle->abortStream()"); } + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + freeBuffers(); + #endif handle = nullptr; return true; } @@ -144,6 +125,130 @@ struct RtAudioBridge : NativeBridge { return true; } + bool isAudioInputEnabled() const override + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + return captureEnabled; + #else + return false; + #endif + } + + bool requestAudioInput() override + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + // stop audio first + deactivate(); + close(); + + // try to open with capture enabled + const bool ok = _open(true); + + if (ok) + captureEnabled = true; + else + _open(false); + + activate(); + return ok; + #else + return false; + #endif + } + + bool isMIDIEnabled() const override + { + d_stdout("%s %d", __PRETTY_FUNCTION__, __LINE__); + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (!midiIns.empty()) + return true; + #endif + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (!midiOuts.empty()) + return true; + #endif + return false; + } + + bool requestMIDI() override + { + d_stdout("%s %d", __PRETTY_FUNCTION__, __LINE__); + // clear ports in use first + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (!midiIns.empty()) + { + try { + midiIns.clear(); + } catch (const RtMidiError& err) { + d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); + return false; + } DISTRHO_SAFE_EXCEPTION_RETURN("midiIns.clear()", false); + } + #endif + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (!midiOuts.size()) + { + try { + midiOuts.clear(); + } catch (const RtMidiError& err) { + d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); + return false; + } DISTRHO_SAFE_EXCEPTION_RETURN("midiOuts.clear()", false); + } + #endif + + // query port count + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT + uint midiInCount; + try { + RtMidiIn midiIn(RtMidi::RTMIDI_API_TYPE, name.buffer()); + midiInCount = midiIn.getPortCount(); + } catch (const RtMidiError& err) { + d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); + return false; + } DISTRHO_SAFE_EXCEPTION_RETURN("midiIn.getPortCount()", false); + #endif + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + uint midiOutCount; + try { + RtMidiOut midiOut(RtMidi::RTMIDI_API_TYPE, name.buffer()); + midiOutCount = midiOut.getPortCount(); + } catch (const RtMidiError& err) { + d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); + return false; + } DISTRHO_SAFE_EXCEPTION_RETURN("midiOut.getPortCount()", false); + #endif + + // open all possible ports + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT + for (uint i=0; i rtAudio; + + try { + rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE); + } DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false); + + uint rtAudioBufferFrames = nextBufferSize; + + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + RtAudio::StreamParameters inParams; + #endif + RtAudio::StreamParameters* inParamsPtr = nullptr; + + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + if (withInput) + { + inParams.deviceId = rtAudio->getDefaultInputDevice(); + inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS; + inParamsPtr = &inParams; + } + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + RtAudio::StreamParameters outParams; + outParams.deviceId = rtAudio->getDefaultOutputDevice(); + outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS; + RtAudio::StreamParameters* const outParamsPtr = &outParams; + #else + RtAudio::StreamParameters* const outParamsPtr = nullptr; + #endif + + RtAudio::StreamOptions opts; + opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_ALSA_USE_DEFAULT; + opts.streamName = name.buffer(); + + try { + rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, + RtAudioCallback, this, &opts, nullptr); + } catch (const RtAudioError& err) { + d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); + return false; + } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); + + handle = rtAudio; + bufferSize = rtAudioBufferFrames; + sampleRate = handle->getStreamSampleRate(); + allocBuffers(!withInput, true); + return true; + } + static int RtAudioCallback(void* const outputBuffer, #if DISTRHO_PLUGIN_NUM_INPUTS > 0 void* const inputBuffer, @@ -220,6 +377,23 @@ struct RtAudioBridge : NativeBridge { return 0; } + + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void RtMidiCallback(double timeStamp, std::vector* message, void* userData) + { + const size_t len = message->size(); + DISTRHO_SAFE_ASSERT_RETURN(len > 0 && len <= kMaxMIDIInputMessageSize,); + + RtAudioBridge* const self = static_cast(userData); + + // TODO timestamp handling + self->midiInBufferPending.writeByte(static_cast(len)); + self->midiInBufferPending.writeCustomData(message->data(), len); + for (uint8_t i=0; imidiInBufferPending.writeByte(0); + self->midiInBufferPending.commitWrite(); + } + #endif }; #endif // RTAUDIO_API_TYPE diff --git a/distrho/src/jackbridge/SDL2Bridge.hpp b/distrho/src/jackbridge/SDL2Bridge.hpp index 88cfd64e..e4fb08b3 100644 --- a/distrho/src/jackbridge/SDL2Bridge.hpp +++ b/distrho/src/jackbridge/SDL2Bridge.hpp @@ -131,7 +131,7 @@ struct SDL2Bridge : NativeBridge { sampleRate = receivedPlayback.freq; #endif - allocBuffers(); + allocBuffers(true, false); return true; } diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp index 71154bb9..1353e518 100644 --- a/distrho/src/jackbridge/WebBridge.hpp +++ b/distrho/src/jackbridge/WebBridge.hpp @@ -52,7 +52,7 @@ struct WebBridge : NativeBridge { return 0; }) != 0; #endif - + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT midiAvailable = EM_ASM_INT({ return typeof(navigator.requestMIDIAccess) === 'function' ? 1 : 0; @@ -117,7 +117,7 @@ struct WebBridge : NativeBridge { return WAB.audioContext.sampleRate; }); - allocBuffers(); + allocBuffers(true, true); EM_ASM({ var numInputs = $0; @@ -279,7 +279,7 @@ struct WebBridge : NativeBridge { bufferSize = newBufferSize; freeBuffers(); - allocBuffers(); + allocBuffers(true, true); if (bufferSizeCallback != nullptr) bufferSizeCallback(newBufferSize, jackBufferSizeArg); diff --git a/distrho/src/jackbridge/rtmidi/LICENSE b/distrho/src/jackbridge/rtmidi/LICENSE new file mode 100644 index 00000000..313fd19b --- /dev/null +++ b/distrho/src/jackbridge/rtmidi/LICENSE @@ -0,0 +1,27 @@ + +RtMidi: realtime MIDI i/o C++ classes +Copyright (c) 2003-2021 Gary P. Scavone + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +Any person wishing to distribute modifications to the Software is +asked to send the modifications to the original developer so that +they can be incorporated into the canonical version. This is, +however, not a binding provision of this license. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/distrho/src/jackbridge/rtmidi/RtMidi.cpp b/distrho/src/jackbridge/rtmidi/RtMidi.cpp new file mode 100644 index 00000000..62781f0c --- /dev/null +++ b/distrho/src/jackbridge/rtmidi/RtMidi.cpp @@ -0,0 +1,3930 @@ +/**********************************************************************/ +/*! \class RtMidi + \brief An abstract base class for realtime MIDI input/output. + + This class implements some common functionality for the realtime + MIDI input/output subclasses RtMidiIn and RtMidiOut. + + RtMidi GitHub site: https://github.com/thestk/rtmidi + RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/ + + RtMidi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2021 Gary P. Scavone + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/**********************************************************************/ + +#include "RtMidi.h" +#include +#if defined(__APPLE__) +#include +#endif + +#if (TARGET_OS_IPHONE == 1) + + #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime + #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos + + #include + class CTime2nsFactor + { + public: + CTime2nsFactor() + { + mach_timebase_info_data_t tinfo; + mach_timebase_info(&tinfo); + Factor = (double)tinfo.numer / tinfo.denom; + } + static double Factor; + }; + double CTime2nsFactor::Factor; + static CTime2nsFactor InitTime2nsFactor; + #undef AudioGetCurrentHostTime + #undef AudioConvertHostTimeToNanos + #define AudioGetCurrentHostTime (uint64_t) mach_absolute_time + #define AudioConvertHostTimeToNanos(t) t *CTime2nsFactor::Factor + #define EndianS32_BtoN(n) n + +#endif + +// Default for Windows is to add an identifier to the port names; this +// flag can be defined (e.g. in your project file) to disable this behaviour. +//#define RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES + +// **************************************************************** // +// +// MidiInApi and MidiOutApi subclass prototypes. +// +// **************************************************************** // + +#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(TARGET_IPHONE_OS) && !defined(__WEB_MIDI_API__) + #define __RTMIDI_DUMMY__ +#endif + +#if defined(__MACOSX_CORE__) +#include + +class MidiInCore: public MidiInApi +{ + public: + MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ); + ~MidiInCore( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw(); + void initialize( const std::string& clientName ); +}; + +class MidiOutCore: public MidiOutApi +{ + public: + MidiOutCore( const std::string &clientName ); + ~MidiOutCore( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( const unsigned char *message, size_t size ); + + protected: + MIDIClientRef getCoreMidiClientSingleton(const std::string& clientName) throw(); + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__UNIX_JACK__) + +class MidiInJack: public MidiInApi +{ + public: + MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ); + ~MidiInJack( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + std::string clientName; + + void connect( void ); + void initialize( const std::string& clientName ); +}; + +class MidiOutJack: public MidiOutApi +{ + public: + MidiOutJack( const std::string &clientName ); + ~MidiOutJack( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( const unsigned char *message, size_t size ); + + protected: + std::string clientName; + + void connect( void ); + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__LINUX_ALSA__) + +class MidiInAlsa: public MidiInApi +{ + public: + MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ); + ~MidiInAlsa( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + void initialize( const std::string& clientName ); +}; + +class MidiOutAlsa: public MidiOutApi +{ + public: + MidiOutAlsa( const std::string &clientName ); + ~MidiOutAlsa( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( const unsigned char *message, size_t size ); + + protected: + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__WINDOWS_MM__) + +class MidiInWinMM: public MidiInApi +{ + public: + MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ); + ~MidiInWinMM( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + void initialize( const std::string& clientName ); +}; + +class MidiOutWinMM: public MidiOutApi +{ + public: + MidiOutWinMM( const std::string &clientName ); + ~MidiOutWinMM( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( const unsigned char *message, size_t size ); + + protected: + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__WEB_MIDI_API__) + +class MidiInWeb : public MidiInApi +{ + std::string client_name{}; + std::string web_midi_id{}; + int open_port_number{-1}; + + public: + MidiInWeb(const std::string &/*clientName*/, unsigned int queueSizeLimit ); + ~MidiInWeb( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::WEB_MIDI_API; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + void onMidiMessage( uint8_t* data, double domHishResTimeStamp ); + + protected: + void initialize( const std::string& clientName ); +}; + +class MidiOutWeb: public MidiOutApi +{ + std::string client_name{}; + std::string web_midi_id{}; + int open_port_number{-1}; + + public: + MidiOutWeb( const std::string &clientName ); + ~MidiOutWeb( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::WEB_MIDI_API; }; + void openPort( unsigned int portNumber, const std::string &portName ); + void openVirtualPort( const std::string &portName ); + void closePort( void ); + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( const unsigned char *message, size_t size ); + + protected: + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__RTMIDI_DUMMY__) + +class MidiInDummy: public MidiInApi +{ + public: + MidiInDummy( const std::string &/*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } + RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } + void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} + void openVirtualPort( const std::string &/*portName*/ ) {} + void closePort( void ) {} + void setClientName( const std::string &/*clientName*/ ) {}; + void setPortName( const std::string &/*portName*/ ) {}; + unsigned int getPortCount( void ) { return 0; } + std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } + + protected: + void initialize( const std::string& /*clientName*/ ) {} +}; + +class MidiOutDummy: public MidiOutApi +{ + public: + MidiOutDummy( const std::string &/*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } + RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } + void openPort( unsigned int /*portNumber*/, const std::string &/*portName*/ ) {} + void openVirtualPort( const std::string &/*portName*/ ) {} + void closePort( void ) {} + void setClientName( const std::string &/*clientName*/ ) {}; + void setPortName( const std::string &/*portName*/ ) {}; + unsigned int getPortCount( void ) { return 0; } + std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } + void sendMessage( const unsigned char * /*message*/, size_t /*size*/ ) {} + + protected: + void initialize( const std::string& /*clientName*/ ) {} +}; + +#endif + +//*********************************************************************// +// RtMidi Definitions +//*********************************************************************// + +RtMidi :: RtMidi() + : rtapi_(0) +{ +} + +RtMidi :: ~RtMidi() +{ + delete rtapi_; + rtapi_ = 0; +} + +RtMidi::RtMidi(RtMidi&& other) noexcept { + rtapi_ = other.rtapi_; + other.rtapi_ = nullptr; +} + +std::string RtMidi :: getVersion( void ) throw() +{ + return std::string( RTMIDI_VERSION ); +} + +// Define API names and display names. +// Must be in same order as API enum. +extern "C" { +const char* rtmidi_api_names[][2] = { + { "unspecified" , "Unknown" }, + { "core" , "CoreMidi" }, + { "alsa" , "ALSA" }, + { "jack" , "Jack" }, + { "winmm" , "Windows MultiMedia" }, + { "web" , "Web MIDI API" }, + { "dummy" , "Dummy" }, +}; +const unsigned int rtmidi_num_api_names = + sizeof(rtmidi_api_names)/sizeof(rtmidi_api_names[0]); + +// The order here will control the order of RtMidi's API search in +// the constructor. +extern "C" const RtMidi::Api rtmidi_compiled_apis[] = { +#if defined(__MACOSX_CORE__) + RtMidi::MACOSX_CORE, +#endif +#if defined(__LINUX_ALSA__) + RtMidi::LINUX_ALSA, +#endif +#if defined(__UNIX_JACK__) + RtMidi::UNIX_JACK, +#endif +#if defined(__WINDOWS_MM__) + RtMidi::WINDOWS_MM, +#endif +#if defined(__WEB_MIDI_API__) + RtMidi::WEB_MIDI_API, +#endif +#if defined(__RTMIDI_DUMMY__) + RtMidi::RTMIDI_DUMMY, +#endif + RtMidi::UNSPECIFIED, +}; +extern "C" const unsigned int rtmidi_num_compiled_apis = + sizeof(rtmidi_compiled_apis)/sizeof(rtmidi_compiled_apis[0])-1; +} + +void RtMidi :: getCompiledApi( std::vector &apis ) throw() +{ + apis = std::vector(rtmidi_compiled_apis, + rtmidi_compiled_apis + rtmidi_num_compiled_apis); +} + +std::string RtMidi :: getApiName( RtMidi::Api api ) +{ + if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS) + return ""; + return rtmidi_api_names[api][0]; +} + +std::string RtMidi :: getApiDisplayName( RtMidi::Api api ) +{ + if (api < RtMidi::UNSPECIFIED || api >= RtMidi::NUM_APIS) + return "Unknown"; + return rtmidi_api_names[api][1]; +} + +RtMidi::Api RtMidi :: getCompiledApiByName( const std::string &name ) +{ + unsigned int i=0; + for (i = 0; i < rtmidi_num_compiled_apis; ++i) + if (name == rtmidi_api_names[rtmidi_compiled_apis[i]][0]) + return rtmidi_compiled_apis[i]; + return RtMidi::UNSPECIFIED; +} + +void RtMidi :: setClientName( const std::string &clientName ) +{ + rtapi_->setClientName( clientName ); +} + +void RtMidi :: setPortName( const std::string &portName ) +{ + rtapi_->setPortName( portName ); +} + + +//*********************************************************************// +// RtMidiIn Definitions +//*********************************************************************// + +void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) +{ + delete rtapi_; + rtapi_ = 0; + +#if defined(__UNIX_JACK__) + if ( api == UNIX_JACK ) + rtapi_ = new MidiInJack( clientName, queueSizeLimit ); +#endif +#if defined(__LINUX_ALSA__) + if ( api == LINUX_ALSA ) + rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); +#endif +#if defined(__WINDOWS_MM__) + if ( api == WINDOWS_MM ) + rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); +#endif +#if defined(__MACOSX_CORE__) + if ( api == MACOSX_CORE ) + rtapi_ = new MidiInCore( clientName, queueSizeLimit ); +#endif +#if defined(__WEB_MIDI_API__) + if ( api == WEB_MIDI_API ) + rtapi_ = new MidiInWeb( clientName, queueSizeLimit ); +#endif +#if defined(__RTMIDI_DUMMY__) + if ( api == RTMIDI_DUMMY ) + rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); +#endif +} + +RTMIDI_DLL_PUBLIC RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ) + : RtMidi() +{ + if ( api != UNSPECIFIED ) { + // Attempt to open the specified API. + openMidiApi( api, clientName, queueSizeLimit ); + if ( rtapi_ ) return; + + // No compiled support for specified API value. Issue a warning + // and continue as if no API was specified. + std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl; + } + + // Iterate through the compiled APIs and return as soon as we find + // one with at least one port or we reach the end of the list. + std::vector< RtMidi::Api > apis; + getCompiledApi( apis ); + for ( unsigned int i=0; igetPortCount() ) break; + } + + if ( rtapi_ ) return; + + // It should not be possible to get here because the preprocessor + // definition __RTMIDI_DUMMY__ is automatically defined if no + // API-specific definitions are passed to the compiler. But just in + // case something weird happens, we'll throw an error. + std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!"; + throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); +} + +RtMidiIn :: ~RtMidiIn() throw() +{ +} + + +//*********************************************************************// +// RtMidiOut Definitions +//*********************************************************************// + +void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string &clientName ) +{ + delete rtapi_; + rtapi_ = 0; + +#if defined(__UNIX_JACK__) + if ( api == UNIX_JACK ) + rtapi_ = new MidiOutJack( clientName ); +#endif +#if defined(__LINUX_ALSA__) + if ( api == LINUX_ALSA ) + rtapi_ = new MidiOutAlsa( clientName ); +#endif +#if defined(__WINDOWS_MM__) + if ( api == WINDOWS_MM ) + rtapi_ = new MidiOutWinMM( clientName ); +#endif +#if defined(__MACOSX_CORE__) + if ( api == MACOSX_CORE ) + rtapi_ = new MidiOutCore( clientName ); +#endif +#if defined(__WEB_MIDI_API__) + if ( api == WEB_MIDI_API ) + rtapi_ = new MidiOutWeb( clientName ); +#endif +#if defined(__RTMIDI_DUMMY__) + if ( api == RTMIDI_DUMMY ) + rtapi_ = new MidiOutDummy( clientName ); +#endif +} + +RTMIDI_DLL_PUBLIC RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string &clientName) +{ + if ( api != UNSPECIFIED ) { + // Attempt to open the specified API. + openMidiApi( api, clientName ); + if ( rtapi_ ) return; + + // No compiled support for specified API value. Issue a warning + // and continue as if no API was specified. + std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl; + } + + // Iterate through the compiled APIs and return as soon as we find + // one with at least one port or we reach the end of the list. + std::vector< RtMidi::Api > apis; + getCompiledApi( apis ); + for ( unsigned int i=0; igetPortCount() ) break; + } + + if ( rtapi_ ) return; + + // It should not be possible to get here because the preprocessor + // definition __RTMIDI_DUMMY__ is automatically defined if no + // API-specific definitions are passed to the compiler. But just in + // case something weird happens, we'll thrown an error. + std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!"; + throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); +} + +RtMidiOut :: ~RtMidiOut() throw() +{ +} + +//*********************************************************************// +// Common MidiApi Definitions +//*********************************************************************// + +MidiApi :: MidiApi( void ) + : apiData_( 0 ), connected_( false ), errorCallback_(0), firstErrorOccurred_(false), errorCallbackUserData_(0) +{ +} + +MidiApi :: ~MidiApi( void ) +{ +} + +void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) +{ + errorCallback_ = errorCallback; + errorCallbackUserData_ = userData; +} + +void MidiApi :: error( RtMidiError::Type type, std::string errorString ) +{ + if ( errorCallback_ ) { + + if ( firstErrorOccurred_ ) + return; + + firstErrorOccurred_ = true; + const std::string errorMessage = errorString; + + errorCallback_( type, errorMessage, errorCallbackUserData_ ); + firstErrorOccurred_ = false; + return; + } + + if ( type == RtMidiError::WARNING ) { + std::cerr << '\n' << errorString << "\n\n"; + } + else if ( type == RtMidiError::DEBUG_WARNING ) { +#if defined(__RTMIDI_DEBUG__) + std::cerr << '\n' << errorString << "\n\n"; +#endif + } + else { + std::cerr << '\n' << errorString << "\n\n"; + throw RtMidiError( errorString, type ); + } +} + +//*********************************************************************// +// Common MidiInApi Definitions +//*********************************************************************// + +MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) + : MidiApi() +{ + // Allocate the MIDI queue. + inputData_.queue.ringSize = queueSizeLimit; + if ( inputData_.queue.ringSize > 0 ) + inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; +} + +MidiInApi :: ~MidiInApi( void ) +{ + // Delete the MIDI queue. + if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; +} + +void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) +{ + if ( inputData_.usingCallback ) { + errorString_ = "MidiInApi::setCallback: a callback function is already set!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + if ( !callback ) { + errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + inputData_.userCallback = callback; + inputData_.userData = userData; + inputData_.usingCallback = true; +} + +void MidiInApi :: cancelCallback() +{ + if ( !inputData_.usingCallback ) { + errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + inputData_.userCallback = 0; + inputData_.userData = 0; + inputData_.usingCallback = false; +} + +void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) +{ + inputData_.ignoreFlags = 0; + if ( midiSysex ) inputData_.ignoreFlags = 0x01; + if ( midiTime ) inputData_.ignoreFlags |= 0x02; + if ( midiSense ) inputData_.ignoreFlags |= 0x04; +} + +double MidiInApi :: getMessage( std::vector *message ) +{ + message->clear(); + + if ( inputData_.usingCallback ) { + errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; + error( RtMidiError::WARNING, errorString_ ); + return 0.0; + } + + double timeStamp; + if ( !inputData_.queue.pop( message, &timeStamp ) ) + return 0.0; + + return timeStamp; +} + +void MidiInApi :: setBufferSize( unsigned int size, unsigned int count ) +{ + inputData_.bufferSize = size; + inputData_.bufferCount = count; +} + +unsigned int MidiInApi::MidiQueue::size( unsigned int *__back, + unsigned int *__front ) +{ + // Access back/front members exactly once and make stack copies for + // size calculation + unsigned int _back = back, _front = front, _size; + if ( _back >= _front ) + _size = _back - _front; + else + _size = ringSize - _front + _back; + + // Return copies of back/front so no new and unsynchronized accesses + // to member variables are needed. + if ( __back ) *__back = _back; + if ( __front ) *__front = _front; + return _size; +} + +// As long as we haven't reached our queue size limit, push the message. +bool MidiInApi::MidiQueue::push( const MidiInApi::MidiMessage& msg ) +{ + // Local stack copies of front/back + unsigned int _back, _front, _size; + + // Get back/front indexes exactly once and calculate current size + _size = size( &_back, &_front ); + + if ( _size < ringSize-1 ) + { + ring[_back] = msg; + back = (back+1)%ringSize; + return true; + } + + return false; +} + +bool MidiInApi::MidiQueue::pop( std::vector *msg, double* timeStamp ) +{ + // Local stack copies of front/back + unsigned int _back, _front, _size; + + // Get back/front indexes exactly once and calculate current size + _size = size( &_back, &_front ); + + if ( _size == 0 ) + return false; + + // Copy queued message to the vector pointer argument and then "pop" it. + msg->assign( ring[_front].bytes.begin(), ring[_front].bytes.end() ); + *timeStamp = ring[_front].timeStamp; + + // Update front + front = (front+1)%ringSize; + return true; +} + +//*********************************************************************// +// Common MidiOutApi Definitions +//*********************************************************************// + +MidiOutApi :: MidiOutApi( void ) + : MidiApi() +{ +} + +MidiOutApi :: ~MidiOutApi( void ) +{ +} + +// *************************************************** // +// +// OS/API-specific methods. +// +// *************************************************** // + +#if defined(__MACOSX_CORE__) + +// The CoreMIDI API is based on the use of a callback function for +// MIDI input. We convert the system specific time stamps to delta +// time values. + +// These are not available on iOS. +#if (TARGET_OS_IPHONE == 0) + #include + #include +#endif + +// A structure to hold variables related to the CoreMIDI API +// implementation. +struct CoreMidiData { + MIDIClientRef client; + MIDIPortRef port; + MIDIEndpointRef endpoint; + MIDIEndpointRef destinationId; + unsigned long long lastTime; + MIDISysexSendRequest sysexreq; +}; + +static MIDIClientRef CoreMidiClientSingleton = 0; + +void RtMidi_setCoreMidiClientSingleton(MIDIClientRef client){ + CoreMidiClientSingleton = client; +} + +void RtMidi_disposeCoreMidiClientSingleton(){ + if (CoreMidiClientSingleton == 0){ + return; + } + MIDIClientDispose( CoreMidiClientSingleton ); + CoreMidiClientSingleton = 0; +} + +//*********************************************************************// +// API: OS-X +// Class Definitions: MidiInCore +//*********************************************************************// + +static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ ) +{ + MidiInApi::RtMidiInData *data = static_cast (procRef); + CoreMidiData *apiData = static_cast (data->apiData); + + unsigned char status; + unsigned short nBytes, iByte, size; + unsigned long long time; + + bool& continueSysex = data->continueSysex; + MidiInApi::MidiMessage& message = data->message; + + const MIDIPacket *packet = &list->packet[0]; + for ( unsigned int i=0; inumPackets; ++i ) { + + // My interpretation of the CoreMIDI documentation: all message + // types, except sysex, are complete within a packet and there may + // be several of them in a single packet. Sysex messages can be + // broken across multiple packets and PacketLists but are bundled + // alone within each packet (these packets do not contain other + // message types). If sysex messages are split across multiple + // MIDIPacketLists, they must be handled by multiple calls to this + // function. + + nBytes = packet->length; + if ( nBytes == 0 ) { + packet = MIDIPacketNext( packet ); + continue; + } + + // Calculate time stamp. + if ( data->firstMessage ) { + message.timeStamp = 0.0; + data->firstMessage = false; + } + else { + time = packet->timeStamp; + if ( time == 0 ) { // this happens when receiving asynchronous sysex messages + time = AudioGetCurrentHostTime(); + } + time -= apiData->lastTime; + time = AudioConvertHostTimeToNanos( time ); + if ( !continueSysex ) + message.timeStamp = time * 0.000000001; + } + + // Track whether any non-filtered messages were found in this + // packet for timestamp calculation + bool foundNonFiltered = false; + + iByte = 0; + if ( continueSysex ) { + // We have a continuing, segmented sysex message. + if ( !( data->ignoreFlags & 0x01 ) ) { + // If we're not ignoring sysex messages, copy the entire packet. + for ( unsigned int j=0; jdata[j] ); + } + continueSysex = packet->data[nBytes-1] != 0xF7; + + if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( !data->queue.push( message ) ) + std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + } + else { + while ( iByte < nBytes ) { + size = 0; + // We are expecting that the next byte in the packet is a status byte. + status = packet->data[iByte]; + if ( !(status & 0x80) ) break; + // Determine the number of bytes in the MIDI message. + if ( status < 0xC0 ) size = 3; + else if ( status < 0xE0 ) size = 2; + else if ( status < 0xF0 ) size = 3; + else if ( status == 0xF0 ) { + // A MIDI sysex + if ( data->ignoreFlags & 0x01 ) { + size = 0; + iByte = nBytes; + } + else size = nBytes - iByte; + continueSysex = packet->data[nBytes-1] != 0xF7; + } + else if ( status == 0xF1 ) { + // A MIDI time code message + if ( data->ignoreFlags & 0x02 ) { + size = 0; + iByte += 2; + } + else size = 2; + } + else if ( status == 0xF2 ) size = 3; + else if ( status == 0xF3 ) size = 2; + else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { + // A MIDI timing tick message and we're ignoring it. + size = 0; + iByte += 1; + } + else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { + // A MIDI active sensing message and we're ignoring it. + size = 0; + iByte += 1; + } + else size = 1; + + // Copy the MIDI data to our vector. + if ( size ) { + foundNonFiltered = true; + message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] ); + if ( !continueSysex ) { + // If not a continuing sysex message, invoke the user callback function or queue the message. + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( !data->queue.push( message ) ) + std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; + } + message.bytes.clear(); + } + iByte += size; + } + } + } + + // Save the time of the last non-filtered message + if ( foundNonFiltered ) { + apiData->lastTime = packet->timeStamp; + if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages + apiData->lastTime = AudioGetCurrentHostTime(); + } + } + + packet = MIDIPacketNext(packet); + } +} + +MidiInCore :: MidiInCore( const std::string &clientName, unsigned int queueSizeLimit ) + : MidiInApi( queueSizeLimit ) +{ + MidiInCore::initialize( clientName ); +} + +MidiInCore :: ~MidiInCore( void ) +{ + // Close a connection if it exists. + MidiInCore::closePort(); + + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); + delete data; +} + +MIDIClientRef MidiInCore::getCoreMidiClientSingleton(const std::string& clientName) throw() { + + if (CoreMidiClientSingleton == 0){ + // Set up our client. + MIDIClientRef client; + + CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); + if ( result != noErr ) { + std::ostringstream ost; + ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; + errorString_ = ost.str(); + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return 0; + } + CFRelease( name ); + + CoreMidiClientSingleton = client; + } + + return CoreMidiClientSingleton; +} + +void MidiInCore :: initialize( const std::string& clientName ) +{ + // Set up our client. + MIDIClientRef client = getCoreMidiClientSingleton(clientName); + + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData; + data->client = client; + data->endpoint = 0; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; +} + +void MidiInCore :: openPort( unsigned int portNumber, const std::string &portName ) +{ + if ( connected_ ) { + errorString_ = "MidiInCore::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + unsigned int nSrc = MIDIGetNumberOfSources(); + if ( nSrc < 1 ) { + errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; + } + + if ( portNumber >= nSrc ) { + std::ostringstream ost; + ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; + } + + MIDIPortRef port; + CoreMidiData *data = static_cast (apiData_); + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIInputPortCreate( data->client, + portNameRef, + midiInputCallback, (void *)&inputData_, &port ); + CFRelease( portNameRef ); + + if ( result != noErr ) { + errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Get the desired input source identifier. + MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); + if ( endpoint == 0 ) { + MIDIPortDispose( port ); + errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Make the connection. + result = MIDIPortConnectSource( port, endpoint, NULL ); + if ( result != noErr ) { + MIDIPortDispose( port ); + errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Save our api-specific port information. + data->port = port; + + connected_ = true; +} + +void MidiInCore :: openVirtualPort( const std::string &portName ) +{ + CoreMidiData *data = static_cast (apiData_); + + // Create a virtual MIDI input destination. + MIDIEndpointRef endpoint; + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIDestinationCreate( data->client, + portNameRef, + midiInputCallback, (void *)&inputData_, &endpoint ); + CFRelease( portNameRef ); + + if ( result != noErr ) { + errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Save our api-specific connection information. + data->endpoint = endpoint; +} + +void MidiInCore :: closePort( void ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + MIDIEndpointDispose( data->endpoint ); + data->endpoint = 0; + } + + if ( data->port ) { + MIDIPortDispose( data->port ); + data->port = 0; + } + + connected_ = false; +} + +void MidiInCore :: setClientName ( const std::string& ) +{ + + errorString_ = "MidiInCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiInCore :: setPortName ( const std::string& ) +{ + + errorString_ = "MidiInCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +unsigned int MidiInCore :: getPortCount() +{ + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + return MIDIGetNumberOfSources(); +} + +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + + // Begin with the endpoint's name. + str = NULL; + MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + + // some MIDI devices have a leading space in endpoint name. trim + CFStringRef space = CFStringCreateWithCString(NULL, " ", kCFStringEncodingUTF8); + CFStringTrim(result, space); + CFRelease(space); + + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity( endpoint, &entity ); + if ( entity == 0 ) + // probably virtual + return result; + + if ( CFStringGetLength( result ) == 0 ) { + // endpoint name has zero length -- try the entity + str = NULL; + MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + } + // now consider the device's name + MIDIDeviceRef device = 0; + MIDIEntityGetDevice( entity, &device ); + if ( device == 0 ) + return result; + + str = NULL; + MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); + if ( CFStringGetLength( result ) == 0 ) { + CFRelease( result ); + return str; + } + if ( str != NULL ) { + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { + CFRelease( result ); + return str; + } else { + if ( CFStringGetLength( str ) == 0 ) { + CFRelease( str ); + return result; + } + // does the entity name already start with the device name? + // (some drivers do this though they shouldn't) + // if so, do not prepend + if ( CFStringCompareWithOptions( result, /* endpoint name */ + str /* device name */, + CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { + // prepend the device name to the entity name + if ( CFStringGetLength( result ) > 0 ) + CFStringInsert( result, 0, CFSTR(" ") ); + + CFStringInsert( result, 0, str ); + } + CFRelease( str ); + } + } + return result; +} + +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + OSStatus err; + int i; + + // Does the endpoint have connections? + CFDataRef connections = NULL; + int nConnected = 0; + bool anyStrings = false; + err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); + if ( connections != NULL ) { + // It has connections, follow them + // Concatenate the names of all connected devices + nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); + if ( nConnected ) { + const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); + for ( i=0; i= MIDIGetNumberOfSources() ) { + std::ostringstream ost; + ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + portRef = MIDIGetSource( portNumber ); + nameRef = ConnectedEndpointName( portRef ); + CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); + CFRelease( nameRef ); + + return stringName = name; +} + +//*********************************************************************// +// API: OS-X +// Class Definitions: MidiOutCore +//*********************************************************************// + +MidiOutCore :: MidiOutCore( const std::string &clientName ) + : MidiOutApi() +{ + MidiOutCore::initialize( clientName ); +} + +MidiOutCore :: ~MidiOutCore( void ) +{ + // Close a connection if it exists. + MidiOutCore::closePort(); + + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); + delete data; +} + +MIDIClientRef MidiOutCore::getCoreMidiClientSingleton(const std::string& clientName) throw() { + + if (CoreMidiClientSingleton == 0){ + // Set up our client. + MIDIClientRef client; + + CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); + if ( result != noErr ) { + std::ostringstream ost; + ost << "MidiInCore::initialize: error creating OS-X MIDI client object (" << result << ")."; + errorString_ = ost.str(); + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return 0; + } + CFRelease( name ); + + CoreMidiClientSingleton = client; + } + + return CoreMidiClientSingleton; +} + +void MidiOutCore :: initialize( const std::string& clientName ) +{ + // Set up our client. + MIDIClientRef client = getCoreMidiClientSingleton(clientName); + + // Save our api-specific connection information. + CoreMidiData *data = (CoreMidiData *) new CoreMidiData; + data->client = client; + data->endpoint = 0; + apiData_ = (void *) data; +} + +unsigned int MidiOutCore :: getPortCount() +{ + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + return MIDIGetNumberOfDestinations(); +} + +std::string MidiOutCore :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + char name[128]; + + std::string stringName; + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + if ( portNumber >= MIDIGetNumberOfDestinations() ) { + std::ostringstream ost; + ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + portRef = MIDIGetDestination( portNumber ); + nameRef = ConnectedEndpointName(portRef); + CFStringGetCString( nameRef, name, sizeof(name), kCFStringEncodingUTF8 ); + CFRelease( nameRef ); + + return stringName = name; +} + +void MidiOutCore :: openPort( unsigned int portNumber, const std::string &portName ) +{ + if ( connected_ ) { + errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + unsigned int nDest = MIDIGetNumberOfDestinations(); + if (nDest < 1) { + errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; + } + + if ( portNumber >= nDest ) { + std::ostringstream ost; + ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; + } + + MIDIPortRef port; + CoreMidiData *data = static_cast (apiData_); + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIOutputPortCreate( data->client, portNameRef, &port ); + CFRelease( portNameRef ); + if ( result != noErr ) { + errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Get the desired output port identifier. + MIDIEndpointRef destination = MIDIGetDestination( portNumber ); + if ( destination == 0 ) { + MIDIPortDispose( port ); + errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Save our api-specific connection information. + data->port = port; + data->destinationId = destination; + connected_ = true; +} + +void MidiOutCore :: closePort( void ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + MIDIEndpointDispose( data->endpoint ); + data->endpoint = 0; + } + + if ( data->port ) { + MIDIPortDispose( data->port ); + data->port = 0; + } + + connected_ = false; +} + +void MidiOutCore :: setClientName ( const std::string& ) +{ + + errorString_ = "MidiOutCore::setClientName: this function is not implemented for the MACOSX_CORE API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiOutCore :: setPortName ( const std::string& ) +{ + + errorString_ = "MidiOutCore::setPortName: this function is not implemented for the MACOSX_CORE API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiOutCore :: openVirtualPort( const std::string &portName ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + // Create a virtual MIDI output source. + MIDIEndpointRef endpoint; + CFStringRef portNameRef = CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDISourceCreate( data->client, portNameRef, &endpoint ); + CFRelease( portNameRef ); + + if ( result != noErr ) { + errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Save our api-specific connection information. + data->endpoint = endpoint; +} + +void MidiOutCore :: sendMessage( const unsigned char *message, size_t size ) +{ + // We use the MIDISendSysex() function to asynchronously send sysex + // messages. Otherwise, we use a single CoreMidi MIDIPacket. + unsigned int nBytes = static_cast (size); + if ( nBytes == 0 ) { + errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + if ( message[0] != 0xF0 && nBytes > 3 ) { + errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); + CoreMidiData *data = static_cast (apiData_); + OSStatus result; + + ByteCount bufsize = nBytes > 65535 ? 65535 : nBytes; + Byte buffer[bufsize+16]; // pad for other struct members + ByteCount listSize = sizeof( buffer ); + MIDIPacketList *packetList = (MIDIPacketList*)buffer; + + ByteCount remainingBytes = nBytes; + while ( remainingBytes ) { + MIDIPacket *packet = MIDIPacketListInit( packetList ); + // A MIDIPacketList can only contain a maximum of 64K of data, so if our message is longer, + // break it up into chunks of 64K or less and send out as a MIDIPacketList with only one + // MIDIPacket. Here, we reuse the memory allocated above on the stack for all. + ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; + const Byte* dataStartPtr = (const Byte *) &message[nBytes - remainingBytes]; + packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr ); + remainingBytes -= bytesForPacket; + + if ( !packet ) { + errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Send to any destinations that may have connected to us. + if ( data->endpoint ) { + result = MIDIReceived( data->endpoint, packetList ); + if ( result != noErr ) { + errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; + error( RtMidiError::WARNING, errorString_ ); + } + } + + // And send to an explicit destination port if we're connected. + if ( connected_ ) { + result = MIDISend( data->port, data->destinationId, packetList ); + if ( result != noErr ) { + errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; + error( RtMidiError::WARNING, errorString_ ); + } + } + } +} + +#endif // __MACOSX_CORE__ + + +//*********************************************************************// +// API: LINUX ALSA SEQUENCER +//*********************************************************************// + +// API information found at: +// - http://www.alsa-project.org/documentation.php#Library + +#if defined(__LINUX_ALSA__) + +// The ALSA Sequencer API is based on the use of a callback function for +// MIDI input. +// +// Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer +// time stamps and other assorted fixes!!! + +// If you don't need timestamping for incoming MIDI events, define the +// preprocessor definition AVOID_TIMESTAMPING to save resources +// associated with the ALSA sequencer queues. + +#include +#include + +// ALSA header file. +#include + +// A structure to hold variables related to the ALSA API +// implementation. +struct AlsaMidiData { + snd_seq_t *seq; + unsigned int portNum; + int vport; + snd_seq_port_subscribe_t *subscription; + snd_midi_event_t *coder; + unsigned int bufferSize; + unsigned int requestedBufferSize; + unsigned char *buffer; + pthread_t thread; + pthread_t dummy_thread_id; + snd_seq_real_time_t lastTime; + int queue_id; // an input queue is needed to get timestamped events + int trigger_fds[2]; +}; + +#define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) + +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: MidiInAlsa +//*********************************************************************// + +static void *alsaMidiHandler( void *ptr ) +{ + MidiInApi::RtMidiInData *data = static_cast (ptr); + AlsaMidiData *apiData = static_cast (data->apiData); + + long nBytes; + double time; + bool continueSysex = false; + bool doDecode = false; + MidiInApi::MidiMessage message; + int poll_fd_count; + struct pollfd *poll_fds; + + snd_seq_event_t *ev; + int result; + result = snd_midi_event_new( 0, &apiData->coder ); + if ( result < 0 ) { + data->doInput = false; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; + return 0; + } + unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); + if ( buffer == NULL ) { + data->doInput = false; + snd_midi_event_free( apiData->coder ); + apiData->coder = 0; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; + return 0; + } + snd_midi_event_init( apiData->coder ); + snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages + + poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; + poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); + snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); + poll_fds[0].fd = apiData->trigger_fds[0]; + poll_fds[0].events = POLLIN; + + while ( data->doInput ) { + + if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { + // No data pending + if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { + if ( poll_fds[0].revents & POLLIN ) { + bool dummy; + int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); + (void) res; + } + } + continue; + } + + // If here, there should be data. + result = snd_seq_event_input( apiData->seq, &ev ); + if ( result == -ENOSPC ) { + std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; + continue; + } + else if ( result <= 0 ) { + std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; + perror("System reports"); + continue; + } + + // This is a bit weird, but we now have to decode an ALSA MIDI + // event (back) into MIDI bytes. We'll ignore non-MIDI types. + if ( !continueSysex ) message.bytes.clear(); + + doDecode = false; + switch ( ev->type ) { + + case SND_SEQ_EVENT_PORT_SUBSCRIBED: +#if defined(__RTMIDI_DEBUG__) + std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n"; +#endif + break; + + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: +#if defined(__RTMIDI_DEBUG__) + std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; + std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" + << (int) ev->data.connect.sender.port + << ", dest = " << (int) ev->data.connect.dest.client << ":" + << (int) ev->data.connect.dest.port + << std::endl; +#endif + break; + + case SND_SEQ_EVENT_QFRAME: // MIDI time code + if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; + break; + + case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick + if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; + break; + + case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick + if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; + break; + + case SND_SEQ_EVENT_SENSING: // Active sensing + if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; + break; + + case SND_SEQ_EVENT_SYSEX: + if ( (data->ignoreFlags & 0x01) ) break; + if ( ev->data.ext.len > apiData->bufferSize ) { + apiData->bufferSize = ev->data.ext.len; + free( buffer ); + buffer = (unsigned char *) malloc( apiData->bufferSize ); + if ( buffer == NULL ) { + data->doInput = false; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; + break; + } + } + doDecode = true; + break; + + default: + doDecode = true; + } + + if ( doDecode ) { + + nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); + if ( nBytes > 0 ) { + // The ALSA sequencer has a maximum buffer size for MIDI sysex + // events of 256 bytes. If a device sends sysex messages larger + // than this, they are segmented into 256 byte chunks. So, + // we'll watch for this and concatenate sysex chunks into a + // single sysex message if necessary. + if ( !continueSysex ) + message.bytes.assign( buffer, &buffer[nBytes] ); + else + message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); + + continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); + if ( !continueSysex ) { + + // Calculate the time stamp: + message.timeStamp = 0.0; + + // Method 1: Use the system time. + //(void)gettimeofday(&tv, (struct timezone *)NULL); + //time = (tv.tv_sec * 1000000) + tv.tv_usec; + + // Method 2: Use the ALSA sequencer event time data. + // (thanks to Pedro Lopez-Cabanillas!). + + // Using method from: + // https://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + + // Perform the carry for the later subtraction by updating y. + // Temp var y is timespec because computation requires signed types, + // while snd_seq_real_time_t has unsigned types. + snd_seq_real_time_t &x( ev->time.time ); + struct timespec y; + y.tv_nsec = apiData->lastTime.tv_nsec; + y.tv_sec = apiData->lastTime.tv_sec; + if ( x.tv_nsec < y.tv_nsec ) { + int nsec = (y.tv_nsec - (int)x.tv_nsec) / 1000000000 + 1; + y.tv_nsec -= 1000000000 * nsec; + y.tv_sec += nsec; + } + if ( x.tv_nsec - y.tv_nsec > 1000000000 ) { + int nsec = ((int)x.tv_nsec - y.tv_nsec) / 1000000000; + y.tv_nsec += 1000000000 * nsec; + y.tv_sec -= nsec; + } + + // Compute the time difference. + time = (int)x.tv_sec - y.tv_sec + ((int)x.tv_nsec - y.tv_nsec)*1e-9; + + apiData->lastTime = ev->time.time; + + if ( data->firstMessage == true ) + data->firstMessage = false; + else + message.timeStamp = time; + } + else { +#if defined(__RTMIDI_DEBUG__) + std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; +#endif + } + } + } + + snd_seq_free_event( ev ); + if ( message.bytes.size() == 0 || continueSysex ) continue; + + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( !data->queue.push( message ) ) + std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; + } + } + + if ( buffer ) free( buffer ); + snd_midi_event_free( apiData->coder ); + apiData->coder = 0; + apiData->thread = apiData->dummy_thread_id; + return 0; +} + +MidiInAlsa :: MidiInAlsa( const std::string &clientName, unsigned int queueSizeLimit ) + : MidiInApi( queueSizeLimit ) +{ + MidiInAlsa::initialize( clientName ); +} + +MidiInAlsa :: ~MidiInAlsa() +{ + // Close a connection if it exists. + MidiInAlsa::closePort(); + + // Shutdown the input thread. + AlsaMidiData *data = static_cast (apiData_); + if ( inputData_.doInput ) { + inputData_.doInput = false; + int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); + (void) res; + if ( !pthread_equal(data->thread, data->dummy_thread_id) ) + pthread_join( data->thread, NULL ); + } + + // Cleanup. + close ( data->trigger_fds[0] ); + close ( data->trigger_fds[1] ); + if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); +#ifndef AVOID_TIMESTAMPING + snd_seq_free_queue( data->seq, data->queue_id ); +#endif + snd_seq_close( data->seq ); + delete data; +} + +void MidiInAlsa :: initialize( const std::string& clientName ) +{ + // Set up the ALSA sequencer client. + snd_seq_t *seq; + int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK ); + if ( result < 0 ) { + errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Set client name. + snd_seq_set_client_name( seq, clientName.c_str() ); + + // Save our api-specific connection information. + AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; + data->seq = seq; + data->portNum = -1; + data->vport = -1; + data->subscription = 0; + data->dummy_thread_id = pthread_self(); + data->thread = data->dummy_thread_id; + data->trigger_fds[0] = -1; + data->trigger_fds[1] = -1; + data->bufferSize = inputData_.bufferSize; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; + + if ( pipe(data->trigger_fds) == -1 ) { + errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Create the input queue +#ifndef AVOID_TIMESTAMPING + data->queue_id = snd_seq_alloc_named_queue( seq, "RtMidi Queue" ); + // Set arbitrary tempo (mm=100) and resolution (240) + snd_seq_queue_tempo_t *qtempo; + snd_seq_queue_tempo_alloca( &qtempo ); + snd_seq_queue_tempo_set_tempo( qtempo, 600000 ); + snd_seq_queue_tempo_set_ppq( qtempo, 240 ); + snd_seq_set_queue_tempo( data->seq, data->queue_id, qtempo ); + snd_seq_drain_output( data->seq ); +#endif +} + +// This function is used to count or get the pinfo structure for a given port number. +unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) +{ + snd_seq_client_info_t *cinfo; + int client; + int count = 0; + snd_seq_client_info_alloca( &cinfo ); + + snd_seq_client_info_set_client( cinfo, -1 ); + while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { + client = snd_seq_client_info_get_client( cinfo ); + if ( client == 0 ) continue; + // Reset query info + snd_seq_port_info_set_client( pinfo, client ); + snd_seq_port_info_set_port( pinfo, -1 ); + while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { + unsigned int atyp = snd_seq_port_info_get_type( pinfo ); + if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) && + ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) && + ( ( atyp & SND_SEQ_PORT_TYPE_APPLICATION ) == 0 ) ) continue; + + unsigned int caps = snd_seq_port_info_get_capability( pinfo ); + if ( ( caps & type ) != type ) continue; + if ( count == portNumber ) return 1; + ++count; + } + } + + // If a negative portNumber was used, return the port count. + if ( portNumber < 0 ) return count; + return 0; +} + +unsigned int MidiInAlsa :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); +} + +std::string MidiInAlsa :: getPortName( unsigned int portNumber ) +{ + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_client_info_alloca( &cinfo ); + snd_seq_port_info_alloca( &pinfo ); + + std::string stringName; + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { + int cnum = snd_seq_port_info_get_client( pinfo ); + snd_seq_get_any_client_info( data->seq, cnum, cinfo ); + std::ostringstream os; + os << snd_seq_client_info_get_name( cinfo ); + os << ":"; + os << snd_seq_port_info_get_name( pinfo ); + os << " "; // These lines added to make sure devices are listed + os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names + os << ":"; + os << snd_seq_port_info_get_port( pinfo ); + stringName = os.str(); + return stringName; + } + + // If we get here, we didn't find a match. + errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; + error( RtMidiError::WARNING, errorString_ ); + return stringName; +} + +void MidiInAlsa :: openPort( unsigned int portNumber, const std::string &portName ) +{ + if ( connected_ ) { + errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + unsigned int nSrc = this->getPortCount(); + if ( nSrc < 1 ) { + errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; + } + + snd_seq_port_info_t *src_pinfo; + snd_seq_port_info_alloca( &src_pinfo ); + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { + std::ostringstream ost; + ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; + } + + snd_seq_addr_t sender, receiver; + sender.client = snd_seq_port_info_get_client( src_pinfo ); + sender.port = snd_seq_port_info_get_port( src_pinfo ); + receiver.client = snd_seq_client_id( data->seq ); + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + if ( data->vport < 0 ) { + snd_seq_port_info_set_client( pinfo, 0 ); + snd_seq_port_info_set_port( pinfo, 0 ); + snd_seq_port_info_set_capability( pinfo, + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE ); + snd_seq_port_info_set_type( pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); + snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING + snd_seq_port_info_set_timestamping( pinfo, 1 ); + snd_seq_port_info_set_timestamp_real( pinfo, 1 ); + snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); +#endif + snd_seq_port_info_set_name( pinfo, portName.c_str() ); + data->vport = snd_seq_create_port( data->seq, pinfo ); + + if ( data->vport < 0 ) { + errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + data->vport = snd_seq_port_info_get_port( pinfo ); + } + + receiver.port = data->vport; + + if ( !data->subscription ) { + // Make subscription + if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { + errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + snd_seq_port_subscribe_set_sender( data->subscription, &sender ); + snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); + if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + errorString_ = "MidiInAlsa::openPort: ALSA error making port connection."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + } + + if ( inputData_.doInput == false ) { + // Start the input queue +#ifndef AVOID_TIMESTAMPING + snd_seq_start_queue( data->seq, data->queue_id, NULL ); + snd_seq_drain_output( data->seq ); +#endif + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); + + inputData_.doInput = true; + int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); + pthread_attr_destroy( &attr ); + if ( err ) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + inputData_.doInput = false; + errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; + error( RtMidiError::THREAD_ERROR, errorString_ ); + return; + } + } + + connected_ = true; +} + +void MidiInAlsa :: openVirtualPort( const std::string &portName ) +{ + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport < 0 ) { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + snd_seq_port_info_set_capability( pinfo, + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE ); + snd_seq_port_info_set_type( pinfo, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); + snd_seq_port_info_set_midi_channels( pinfo, 16 ); +#ifndef AVOID_TIMESTAMPING + snd_seq_port_info_set_timestamping( pinfo, 1 ); + snd_seq_port_info_set_timestamp_real( pinfo, 1 ); + snd_seq_port_info_set_timestamp_queue( pinfo, data->queue_id ); +#endif + snd_seq_port_info_set_name( pinfo, portName.c_str() ); + data->vport = snd_seq_create_port( data->seq, pinfo ); + + if ( data->vport < 0 ) { + errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + data->vport = snd_seq_port_info_get_port( pinfo ); + } + + if ( inputData_.doInput == false ) { + // Wait for old thread to stop, if still running + if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) + pthread_join( data->thread, NULL ); + + // Start the input queue +#ifndef AVOID_TIMESTAMPING + snd_seq_start_queue( data->seq, data->queue_id, NULL ); + snd_seq_drain_output( data->seq ); +#endif + // Start our MIDI input thread. + pthread_attr_t attr; + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); + + inputData_.doInput = true; + int err = pthread_create( &data->thread, &attr, alsaMidiHandler, &inputData_ ); + pthread_attr_destroy( &attr ); + if ( err ) { + if ( data->subscription ) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + } + inputData_.doInput = false; + errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; + error( RtMidiError::THREAD_ERROR, errorString_ ); + return; + } + } +} + +void MidiInAlsa :: closePort( void ) +{ + AlsaMidiData *data = static_cast (apiData_); + + if ( connected_ ) { + if ( data->subscription ) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + } + // Stop the input queue +#ifndef AVOID_TIMESTAMPING + snd_seq_stop_queue( data->seq, data->queue_id, NULL ); + snd_seq_drain_output( data->seq ); +#endif + connected_ = false; + } + + // Stop thread to avoid triggering the callback, while the port is intended to be closed + if ( inputData_.doInput ) { + inputData_.doInput = false; + int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof( inputData_.doInput ) ); + (void) res; + if ( !pthread_equal( data->thread, data->dummy_thread_id ) ) + pthread_join( data->thread, NULL ); + } +} + +void MidiInAlsa :: setClientName( const std::string &clientName ) +{ + + AlsaMidiData *data = static_cast ( apiData_ ); + snd_seq_set_client_name( data->seq, clientName.c_str() ); + +} + +void MidiInAlsa :: setPortName( const std::string &portName ) +{ + AlsaMidiData *data = static_cast (apiData_); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + snd_seq_get_port_info( data->seq, data->vport, pinfo ); + snd_seq_port_info_set_name( pinfo, portName.c_str() ); + snd_seq_set_port_info( data->seq, data->vport, pinfo ); +} + +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: MidiOutAlsa +//*********************************************************************// + +MidiOutAlsa :: MidiOutAlsa( const std::string &clientName ) : MidiOutApi() +{ + MidiOutAlsa::initialize( clientName ); +} + +MidiOutAlsa :: ~MidiOutAlsa() +{ + // Close a connection if it exists. + MidiOutAlsa::closePort(); + + // Cleanup. + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); + if ( data->coder ) snd_midi_event_free( data->coder ); + if ( data->buffer ) free( data->buffer ); + snd_seq_close( data->seq ); + delete data; +} + +void MidiOutAlsa :: initialize( const std::string& clientName ) +{ + // Set up the ALSA sequencer client. + snd_seq_t *seq; + int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); + if ( result1 < 0 ) { + errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Set client name. + snd_seq_set_client_name( seq, clientName.c_str() ); + + // Save our api-specific connection information. + AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; + data->seq = seq; + data->portNum = -1; + data->vport = -1; + data->bufferSize = 32; + data->coder = 0; + data->buffer = 0; + int result = snd_midi_event_new( data->bufferSize, &data->coder ); + if ( result < 0 ) { + delete data; + errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + data->buffer = (unsigned char *) malloc( data->bufferSize ); + if ( data->buffer == NULL ) { + delete data; + errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; + error( RtMidiError::MEMORY_ERROR, errorString_ ); + return; + } + snd_midi_event_init( data->coder ); + apiData_ = (void *) data; +} + +unsigned int MidiOutAlsa :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); +} + +std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) +{ + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_client_info_alloca( &cinfo ); + snd_seq_port_info_alloca( &pinfo ); + + std::string stringName; + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { + int cnum = snd_seq_port_info_get_client( pinfo ); + snd_seq_get_any_client_info( data->seq, cnum, cinfo ); + std::ostringstream os; + os << snd_seq_client_info_get_name( cinfo ); + os << ":"; + os << snd_seq_port_info_get_name( pinfo ); + os << " "; // These lines added to make sure devices are listed + os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names + os << ":"; + os << snd_seq_port_info_get_port( pinfo ); + stringName = os.str(); + return stringName; + } + + // If we get here, we didn't find a match. + errorString_ = "MidiOutAlsa::getPortName: error looking for port name!"; + error( RtMidiError::WARNING, errorString_ ); + return stringName; +} + +void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string &portName ) +{ + if ( connected_ ) { + errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + unsigned int nSrc = this->getPortCount(); + if ( nSrc < 1 ) { + errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; + } + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { + std::ostringstream ost; + ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; + } + + snd_seq_addr_t sender, receiver; + receiver.client = snd_seq_port_info_get_client( pinfo ); + receiver.port = snd_seq_port_info_get_port( pinfo ); + sender.client = snd_seq_client_id( data->seq ); + + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); + if ( data->vport < 0 ) { + errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + } + + sender.port = data->vport; + + // Make subscription + if ( snd_seq_port_subscribe_malloc( &data->subscription ) < 0 ) { + snd_seq_port_subscribe_free( data->subscription ); + errorString_ = "MidiOutAlsa::openPort: error allocating port subscription."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + snd_seq_port_subscribe_set_sender( data->subscription, &sender ); + snd_seq_port_subscribe_set_dest( data->subscription, &receiver ); + snd_seq_port_subscribe_set_time_update( data->subscription, 1 ); + snd_seq_port_subscribe_set_time_real( data->subscription, 1 ); + if ( snd_seq_subscribe_port( data->seq, data->subscription ) ) { + snd_seq_port_subscribe_free( data->subscription ); + errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + connected_ = true; +} + +void MidiOutAlsa :: closePort( void ) +{ + if ( connected_ ) { + AlsaMidiData *data = static_cast (apiData_); + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + connected_ = false; + } +} + +void MidiOutAlsa :: setClientName( const std::string &clientName ) +{ + + AlsaMidiData *data = static_cast ( apiData_ ); + snd_seq_set_client_name( data->seq, clientName.c_str() ); + +} + +void MidiOutAlsa :: setPortName( const std::string &portName ) +{ + AlsaMidiData *data = static_cast (apiData_); + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + snd_seq_get_port_info( data->seq, data->vport, pinfo ); + snd_seq_port_info_set_name( pinfo, portName.c_str() ); + snd_seq_set_port_info( data->seq, data->vport, pinfo ); +} + +void MidiOutAlsa :: openVirtualPort( const std::string &portName ) +{ + AlsaMidiData *data = static_cast (apiData_); + if ( data->vport < 0 ) { + data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); + + if ( data->vport < 0 ) { + errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + } + } +} + +void MidiOutAlsa :: sendMessage( const unsigned char *message, size_t size ) +{ + long result; + AlsaMidiData *data = static_cast (apiData_); + unsigned int nBytes = static_cast (size); + if ( nBytes > data->bufferSize ) { + data->bufferSize = nBytes; + result = snd_midi_event_resize_buffer( data->coder, nBytes ); + if ( result != 0 ) { + errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + free (data->buffer); + data->buffer = (unsigned char *) malloc( data->bufferSize ); + if ( data->buffer == NULL ) { + errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; + error( RtMidiError::MEMORY_ERROR, errorString_ ); + return; + } + } + + for ( unsigned int i=0; ibuffer[i] = message[i]; + + unsigned int offset = 0; + while (offset < nBytes) { + snd_seq_event_t ev; + snd_seq_ev_clear( &ev ); + snd_seq_ev_set_source( &ev, data->vport ); + snd_seq_ev_set_subs( &ev ); + snd_seq_ev_set_direct( &ev ); + result = snd_midi_event_encode( data->coder, data->buffer + offset, + (long)(nBytes - offset), &ev ); + if ( result < 0 ) { + errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + if ( ev.type == SND_SEQ_EVENT_NONE ) { + errorString_ = "MidiOutAlsa::sendMessage: incomplete message!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + offset += result; + + // Send the event. + result = snd_seq_event_output( data->seq, &ev ); + if ( result < 0 ) { + errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port."; + error( RtMidiError::WARNING, errorString_ ); + return; + } + } + snd_seq_drain_output( data->seq ); +} + +#endif // __LINUX_ALSA__ + + +//*********************************************************************// +// API: Windows Multimedia Library (MM) +//*********************************************************************// + +// API information deciphered from: +// - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp + +// Thanks to Jean-Baptiste Berruchon for the sysex code. + +#if defined(__WINDOWS_MM__) + +// The Windows MM API is based on the use of a callback function for +// MIDI input. We convert the system specific time stamps to delta +// time values. + +// Windows MM MIDI header files. +#include +#include + +// Convert a null-terminated wide string or ANSI-encoded string to UTF-8. +static std::string ConvertToUTF8(const TCHAR *str) +{ + std::string u8str; + const WCHAR *wstr = L""; +#if defined( UNICODE ) || defined( _UNICODE ) + wstr = str; +#else + // Convert from ANSI encoding to wide string + int wlength = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + std::wstring wstrtemp; + if ( wlength ) + { + wstrtemp.assign( wlength - 1, 0 ); + MultiByteToWideChar( CP_ACP, 0, str, -1, &wstrtemp[0], wlength ); + wstr = &wstrtemp[0]; + } +#endif + // Convert from wide string to UTF-8 + int length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL ); + if ( length ) + { + u8str.assign( length - 1, 0 ); + length = WideCharToMultiByte( CP_UTF8, 0, wstr, -1, &u8str[0], length, NULL, NULL ); + } + return u8str; +} + +// A structure to hold variables related to the CoreMIDI API +// implementation. +struct WinMidiData { + HMIDIIN inHandle; // Handle to Midi Input Device + HMIDIOUT outHandle; // Handle to Midi Output Device + DWORD lastTime; + MidiInApi::MidiMessage message; + std::vector sysexBuffer; + CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo +}; + +//*********************************************************************// +// API: Windows MM +// Class Definitions: MidiInWinMM +//*********************************************************************// + +static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, + UINT inputStatus, + DWORD_PTR instancePtr, + DWORD_PTR midiMessage, + DWORD timestamp ) +{ + if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; + + //MidiInApi::RtMidiInData *data = static_cast (instancePtr); + MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; + WinMidiData *apiData = static_cast (data->apiData); + + // Calculate time stamp. + if ( data->firstMessage == true ) { + apiData->message.timeStamp = 0.0; + data->firstMessage = false; + } + else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; + + if ( inputStatus == MIM_DATA ) { // Channel or system message + + // Make sure the first byte is a status byte. + unsigned char status = (unsigned char) (midiMessage & 0x000000FF); + if ( !(status & 0x80) ) return; + + // Determine the number of bytes in the MIDI message. + unsigned short nBytes = 1; + if ( status < 0xC0 ) nBytes = 3; + else if ( status < 0xE0 ) nBytes = 2; + else if ( status < 0xF0 ) nBytes = 3; + else if ( status == 0xF1 ) { + if ( data->ignoreFlags & 0x02 ) return; + else nBytes = 2; + } + else if ( status == 0xF2 ) nBytes = 3; + else if ( status == 0xF3 ) nBytes = 2; + else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { + // A MIDI timing tick message and we're ignoring it. + return; + } + else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { + // A MIDI active sensing message and we're ignoring it. + return; + } + + // Copy bytes to our MIDI message. + unsigned char *ptr = (unsigned char *) &midiMessage; + for ( int i=0; imessage.bytes.push_back( *ptr++ ); + } + else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) + MIDIHDR *sysex = ( MIDIHDR *) midiMessage; + if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { + // Sysex message and we're not ignoring it + for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) + apiData->message.bytes.push_back( sysex->lpData[i] ); + } + + // The WinMM API requires that the sysex buffer be requeued after + // input of each sysex message. Even if we are ignoring sysex + // messages, we still need to requeue the buffer in case the user + // decides to not ignore sysex messages in the future. However, + // it seems that WinMM calls this function with an empty sysex + // buffer when an application closes and in this case, we should + // avoid requeueing it, else the computer suddenly reboots after + // one or two minutes. + if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { + //if ( sysex->dwBytesRecorded > 0 ) { + EnterCriticalSection( &(apiData->_mutex) ); + MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); + LeaveCriticalSection( &(apiData->_mutex) ); + if ( result != MMSYSERR_NOERROR ) + std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; + + if ( data->ignoreFlags & 0x01 ) return; + } + else return; + } + + // Save the time of the last non-filtered message + apiData->lastTime = timestamp; + + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( !data->queue.push( apiData->message ) ) + std::cerr << "\nMidiInWinMM: message queue limit reached!!\n\n"; + } + + // Clear the vector for the next input message. + apiData->message.bytes.clear(); +} + +MidiInWinMM :: MidiInWinMM( const std::string &clientName, unsigned int queueSizeLimit ) + : MidiInApi( queueSizeLimit ) +{ + MidiInWinMM::initialize( clientName ); +} + +MidiInWinMM :: ~MidiInWinMM() +{ + // Close a connection if it exists. + MidiInWinMM::closePort(); + + WinMidiData *data = static_cast (apiData_); + DeleteCriticalSection( &(data->_mutex) ); + + // Cleanup. + delete data; +} + +void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) +{ + // We'll issue a warning here if no devices are available but not + // throw an error since the user can plugin something later. + unsigned int nDevices = midiInGetNumDevs(); + if ( nDevices == 0 ) { + errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available."; + error( RtMidiError::WARNING, errorString_ ); + } + + // Save our api-specific connection information. + WinMidiData *data = (WinMidiData *) new WinMidiData; + apiData_ = (void *) data; + inputData_.apiData = (void *) data; + data->message.bytes.clear(); // needs to be empty for first input message + + if ( !InitializeCriticalSectionAndSpinCount( &(data->_mutex), 0x00000400 ) ) { + errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed."; + error( RtMidiError::WARNING, errorString_ ); + } +} + +void MidiInWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) +{ + if ( connected_ ) { + errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + unsigned int nDevices = midiInGetNumDevs(); + if (nDevices == 0) { + errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; + } + + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; + } + + WinMidiData *data = static_cast (apiData_); + MMRESULT result = midiInOpen( &data->inHandle, + portNumber, + (DWORD_PTR)&midiInputCallback, + (DWORD_PTR)&inputData_, + CALLBACK_FUNCTION ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Allocate and init the sysex buffers. + data->sysexBuffer.resize( inputData_.bufferCount ); + for ( unsigned int i=0; i < inputData_.bufferCount; ++i ) { + data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; + data->sysexBuffer[i]->lpData = new char[ inputData_.bufferSize ]; + data->sysexBuffer[i]->dwBufferLength = inputData_.bufferSize; + data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator + data->sysexBuffer[i]->dwFlags = 0; + + result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + data->inHandle = 0; + errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Register the buffer. + result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + data->inHandle = 0; + errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + } + + result = midiInStart( data->inHandle ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + data->inHandle = 0; + errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + connected_ = true; +} + +void MidiInWinMM :: openVirtualPort( const std::string &/*portName*/ ) +{ + // This function cannot be implemented for the Windows MM MIDI API. + errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); +} + +void MidiInWinMM :: closePort( void ) +{ + if ( connected_ ) { + WinMidiData *data = static_cast (apiData_); + EnterCriticalSection( &(data->_mutex) ); + midiInReset( data->inHandle ); + midiInStop( data->inHandle ); + + for ( size_t i=0; i < data->sysexBuffer.size(); ++i ) { + int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); + delete [] data->sysexBuffer[i]->lpData; + delete [] data->sysexBuffer[i]; + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + data->inHandle = 0; + errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + } + + midiInClose( data->inHandle ); + data->inHandle = 0; + connected_ = false; + LeaveCriticalSection( &(data->_mutex) ); + } +} + +void MidiInWinMM :: setClientName ( const std::string& ) +{ + + errorString_ = "MidiInWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiInWinMM :: setPortName ( const std::string& ) +{ + + errorString_ = "MidiInWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +unsigned int MidiInWinMM :: getPortCount() +{ + return midiInGetNumDevs(); +} + +std::string MidiInWinMM :: getPortName( unsigned int portNumber ) +{ + std::string stringName; + unsigned int nDevices = midiInGetNumDevs(); + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + MIDIINCAPS deviceCaps; + midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); + stringName = ConvertToUTF8( deviceCaps.szPname ); + + // Next lines added to add the portNumber to the name so that + // the device's names are sure to be listed with individual names + // even when they have the same brand name +#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES + std::ostringstream os; + os << " "; + os << portNumber; + stringName += os.str(); +#endif + + return stringName; +} + +//*********************************************************************// +// API: Windows MM +// Class Definitions: MidiOutWinMM +//*********************************************************************// + +MidiOutWinMM :: MidiOutWinMM( const std::string &clientName ) : MidiOutApi() +{ + MidiOutWinMM::initialize( clientName ); +} + +MidiOutWinMM :: ~MidiOutWinMM() +{ + // Close a connection if it exists. + MidiOutWinMM::closePort(); + + // Cleanup. + WinMidiData *data = static_cast (apiData_); + delete data; +} + +void MidiOutWinMM :: initialize( const std::string& /*clientName*/ ) +{ + // We'll issue a warning here if no devices are available but not + // throw an error since the user can plug something in later. + unsigned int nDevices = midiOutGetNumDevs(); + if ( nDevices == 0 ) { + errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available."; + error( RtMidiError::WARNING, errorString_ ); + } + + // Save our api-specific connection information. + WinMidiData *data = (WinMidiData *) new WinMidiData; + apiData_ = (void *) data; +} + +unsigned int MidiOutWinMM :: getPortCount() +{ + return midiOutGetNumDevs(); +} + +std::string MidiOutWinMM :: getPortName( unsigned int portNumber ) +{ + std::string stringName; + unsigned int nDevices = midiOutGetNumDevs(); + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + MIDIOUTCAPS deviceCaps; + midiOutGetDevCaps( portNumber, &deviceCaps, sizeof( MIDIOUTCAPS ) ); + stringName = ConvertToUTF8( deviceCaps.szPname ); + + // Next lines added to add the portNumber to the name so that + // the device's names are sure to be listed with individual names + // even when they have the same brand name + std::ostringstream os; +#ifndef RTMIDI_DO_NOT_ENSURE_UNIQUE_PORTNAMES + os << " "; + os << portNumber; + stringName += os.str(); +#endif + + return stringName; +} + +void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string &/*portName*/ ) +{ + if ( connected_ ) { + errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + unsigned int nDevices = midiOutGetNumDevs(); + if ( nDevices < 1 ) { + errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; + } + + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; + } + + WinMidiData *data = static_cast (apiData_); + MMRESULT result = midiOutOpen( &data->outHandle, + portNumber, + (DWORD)NULL, + (DWORD)NULL, + CALLBACK_NULL ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + connected_ = true; +} + +void MidiOutWinMM :: closePort( void ) +{ + if ( connected_ ) { + WinMidiData *data = static_cast (apiData_); + // Disabled because midiOutReset triggers 0x7b (if any note was ON) and 0x79 "Reset All + // Controllers" (to all 16 channels) CC messages which is undesirable (see issue #222) + // midiOutReset( data->outHandle ); + + midiOutClose( data->outHandle ); + data->outHandle = 0; + connected_ = false; + } +} + +void MidiOutWinMM :: setClientName ( const std::string& ) +{ + + errorString_ = "MidiOutWinMM::setClientName: this function is not implemented for the WINDOWS_MM API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiOutWinMM :: setPortName ( const std::string& ) +{ + + errorString_ = "MidiOutWinMM::setPortName: this function is not implemented for the WINDOWS_MM API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiOutWinMM :: openVirtualPort( const std::string &/*portName*/ ) +{ + // This function cannot be implemented for the Windows MM MIDI API. + errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); +} + +void MidiOutWinMM :: sendMessage( const unsigned char *message, size_t size ) +{ + if ( !connected_ ) return; + + unsigned int nBytes = static_cast(size); + if ( nBytes == 0 ) { + errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + MMRESULT result; + WinMidiData *data = static_cast (apiData_); + if ( message[0] == 0xF0 ) { // Sysex message + + // Allocate buffer for sysex data. + char *buffer = (char *) malloc( nBytes ); + if ( buffer == NULL ) { + errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!"; + error( RtMidiError::MEMORY_ERROR, errorString_ ); + return; + } + + // Copy data to buffer. + for ( unsigned int i=0; ioutHandle, &sysex, sizeof( MIDIHDR ) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Send the message. + result = midiOutLongMsg( data->outHandle, &sysex, sizeof( MIDIHDR ) ); + if ( result != MMSYSERR_NOERROR ) { + free( buffer ); + errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Unprepare the buffer and MIDIHDR. + while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof ( MIDIHDR ) ) ) Sleep( 1 ); + free( buffer ); + } + else { // Channel or system message. + + // Make sure the message size isn't too big. + if ( nBytes > 3 ) { + errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + // Pack MIDI bytes into double word. + DWORD packet; + unsigned char *ptr = (unsigned char *) &packet; + for ( unsigned int i=0; ioutHandle, packet ); + if ( result != MMSYSERR_NOERROR ) { + errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + } + } +} + +#endif // __WINDOWS_MM__ + + +//*********************************************************************// +// API: UNIX JACK +// +// Written primarily by Alexander Svetalkin, with updates for delta +// time by Gary Scavone, April 2011. +// +// *********************************************************************// + +#if defined(__UNIX_JACK__) + +// JACK header files +#include +#include +#include +#include +#include +#ifdef HAVE_SEMAPHORE + #include +#endif + +#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer + +struct JackMidiData { + jack_client_t *client; + jack_port_t *port; + jack_ringbuffer_t *buff; + int buffMaxWrite; // actual writable size, usually 1 less than ringbuffer + jack_time_t lastTime; +#ifdef HAVE_SEMAPHORE + sem_t sem_cleanup; + sem_t sem_needpost; +#endif + MidiInApi :: RtMidiInData *rtMidiIn; + }; + +//*********************************************************************// +// API: JACK +// Class Definitions: MidiInJack +//*********************************************************************// + +static int jackProcessIn( jack_nframes_t nframes, void *arg ) +{ + JackMidiData *jData = (JackMidiData *) arg; + MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn; + jack_midi_event_t event; + jack_time_t time; + + // Is port created? + if ( jData->port == NULL ) return 0; + + void *buff = jack_port_get_buffer( jData->port, nframes ); + bool& continueSysex = rtData->continueSysex; + unsigned char& ignoreFlags = rtData->ignoreFlags; + + // We have midi events in buffer + int evCount = jack_midi_get_event_count( buff ); + for (int j = 0; j < evCount; j++) { + MidiInApi::MidiMessage& message = rtData->message; + jack_midi_event_get( &event, buff, j ); + + // Compute the delta time. + time = jack_get_time(); + if ( rtData->firstMessage == true ) { + message.timeStamp = 0.0; + rtData->firstMessage = false; + } else + message.timeStamp = ( time - jData->lastTime ) * 0.000001; + + jData->lastTime = time; + + if ( !continueSysex ) + message.bytes.clear(); + + if ( !( ( continueSysex || event.buffer[0] == 0xF0 ) && ( ignoreFlags & 0x01 ) ) ) { + // Unless this is a (possibly continued) SysEx message and we're ignoring SysEx, + // copy the event buffer into the MIDI message struct. + for ( unsigned int i = 0; i < event.size; i++ ) + message.bytes.push_back( event.buffer[i] ); + } + + switch ( event.buffer[0] ) { + case 0xF0: + // Start of a SysEx message + continueSysex = event.buffer[event.size - 1] != 0xF7; + if ( ignoreFlags & 0x01 ) continue; + break; + case 0xF1: + case 0xF8: + // MIDI Time Code or Timing Clock message + if ( ignoreFlags & 0x02 ) continue; + break; + case 0xFE: + // Active Sensing message + if ( ignoreFlags & 0x04 ) continue; + break; + default: + if ( continueSysex ) { + // Continuation of a SysEx message + continueSysex = event.buffer[event.size - 1] != 0xF7; + if ( ignoreFlags & 0x01 ) continue; + } + // All other MIDI messages + } + + if ( !continueSysex ) { + // If not a continuation of a SysEx message, + // invoke the user callback function or queue the message. + if ( rtData->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback; + callback( message.timeStamp, &message.bytes, rtData->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( !rtData->queue.push( message ) ) + std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; + } + } + } + + return 0; +} + +MidiInJack :: MidiInJack( const std::string &clientName, unsigned int queueSizeLimit ) + : MidiInApi( queueSizeLimit ) +{ + MidiInJack::initialize( clientName ); +} + +void MidiInJack :: initialize( const std::string& clientName ) +{ + JackMidiData *data = new JackMidiData; + apiData_ = (void *) data; + + data->rtMidiIn = &inputData_; + data->port = NULL; + data->client = NULL; + this->clientName = clientName; + + connect(); +} + +void MidiInJack :: connect() +{ + JackMidiData *data = static_cast (apiData_); + if ( data->client ) + return; + + // Initialize JACK client + if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { + errorString_ = "MidiInJack::initialize: JACK server not running?"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + jack_set_process_callback( data->client, jackProcessIn, data ); + jack_activate( data->client ); +} + +MidiInJack :: ~MidiInJack() +{ + JackMidiData *data = static_cast (apiData_); + MidiInJack::closePort(); + + if ( data->client ) + jack_client_close( data->client ); + delete data; +} + +void MidiInJack :: openPort( unsigned int portNumber, const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); + + connect(); + + // Creating new port + if ( data->port == NULL ) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + + if ( data->port == NULL ) { + errorString_ = "MidiInJack::openPort: JACK error creating port"; + if (portName.size() >= (size_t)jack_port_name_size()) + errorString_ += " (port name too long?)"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Connecting to the output + std::string name = getPortName( portNumber ); + jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); + + connected_ = true; +} + +void MidiInJack :: openVirtualPort( const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); + + connect(); + if ( data->port == NULL ) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + + if ( data->port == NULL ) { + errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; + if (portName.size() >= (size_t)jack_port_name_size()) + errorString_ += " (port name too long?)"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + } +} + +unsigned int MidiInJack :: getPortCount() +{ + int count = 0; + JackMidiData *data = static_cast (apiData_); + connect(); + if ( !data->client ) + return 0; + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); + + if ( ports == NULL ) return 0; + while ( ports[count] != NULL ) + count++; + + free( ports ); + + return count; +} + +std::string MidiInJack :: getPortName( unsigned int portNumber ) +{ + JackMidiData *data = static_cast (apiData_); + std::string retStr( "" ); + + connect(); + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); + + // Check port validity + if ( ports == NULL ) { + errorString_ = "MidiInJack::getPortName: no ports available!"; + error( RtMidiError::WARNING, errorString_ ); + return retStr; + } + + unsigned int i; + for ( i=0; i (apiData_); + + if ( data->port == NULL ) return; + jack_port_unregister( data->client, data->port ); + data->port = NULL; + + connected_ = false; +} + +void MidiInJack:: setClientName( const std::string& ) +{ + + errorString_ = "MidiInJack::setClientName: this function is not implemented for the UNIX_JACK API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiInJack :: setPortName( const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); +#ifdef JACK_HAS_PORT_RENAME + jack_port_rename( data->client, data->port, portName.c_str() ); +#else + jack_port_set_name( data->port, portName.c_str() ); +#endif +} + +//*********************************************************************// +// API: JACK +// Class Definitions: MidiOutJack +//*********************************************************************// + +// Jack process callback +static int jackProcessOut( jack_nframes_t nframes, void *arg ) +{ + JackMidiData *data = (JackMidiData *) arg; + jack_midi_data_t *midiData; + int space; + + // Is port created? + if ( data->port == NULL ) return 0; + + void *buff = jack_port_get_buffer( data->port, nframes ); + jack_midi_clear_buffer( buff ); + + while ( jack_ringbuffer_peek( data->buff, (char *) &space, sizeof( space ) ) == sizeof(space) && + jack_ringbuffer_read_space( data->buff ) >= sizeof(space) + space ) { + jack_ringbuffer_read_advance( data->buff, sizeof(space) ); + + midiData = jack_midi_event_reserve( buff, 0, space ); + if ( midiData ) + jack_ringbuffer_read( data->buff, (char *) midiData, (size_t) space ); + else + jack_ringbuffer_read_advance( data->buff, (size_t) space ); + } + +#ifdef HAVE_SEMAPHORE + if ( !sem_trywait( &data->sem_needpost ) ) + sem_post( &data->sem_cleanup ); +#endif + + return 0; +} + +MidiOutJack :: MidiOutJack( const std::string &clientName ) : MidiOutApi() +{ + MidiOutJack::initialize( clientName ); +} + +void MidiOutJack :: initialize( const std::string& clientName ) +{ + JackMidiData *data = new JackMidiData; + apiData_ = (void *) data; + + data->port = NULL; + data->client = NULL; +#ifdef HAVE_SEMAPHORE + sem_init( &data->sem_cleanup, 0, 0 ); + sem_init( &data->sem_needpost, 0, 0 ); +#endif + this->clientName = clientName; + + connect(); +} + +void MidiOutJack :: connect() +{ + JackMidiData *data = static_cast (apiData_); + if ( data->client ) + return; + + // Initialize output ringbuffers + data->buff = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); + data->buffMaxWrite = (int) jack_ringbuffer_write_space( data->buff ); + + // Initialize JACK client + if ( ( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL ) ) == 0 ) { + errorString_ = "MidiOutJack::initialize: JACK server not running?"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + jack_set_process_callback( data->client, jackProcessOut, data ); + jack_activate( data->client ); +} + +MidiOutJack :: ~MidiOutJack() +{ + JackMidiData *data = static_cast (apiData_); + MidiOutJack::closePort(); + + // Cleanup + jack_ringbuffer_free( data->buff ); + if ( data->client ) { + jack_client_close( data->client ); + } + +#ifdef HAVE_SEMAPHORE + sem_destroy( &data->sem_cleanup ); + sem_destroy( &data->sem_needpost ); +#endif + + delete data; +} + +void MidiOutJack :: openPort( unsigned int portNumber, const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); + + connect(); + + // Creating new port + if ( data->port == NULL ) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); + + if ( data->port == NULL ) { + errorString_ = "MidiOutJack::openPort: JACK error creating port"; + if (portName.size() >= (size_t)jack_port_name_size()) + errorString_ += " (port name too long?)"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Connecting to the output + std::string name = getPortName( portNumber ); + jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); + + connected_ = true; +} + +void MidiOutJack :: openVirtualPort( const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); + + connect(); + if ( data->port == NULL ) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); + + if ( data->port == NULL ) { + errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; + if (portName.size() >= (size_t)jack_port_name_size()) + errorString_ += " (port name too long?)"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + } +} + +unsigned int MidiOutJack :: getPortCount() +{ + int count = 0; + JackMidiData *data = static_cast (apiData_); + connect(); + if ( !data->client ) + return 0; + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); + + if ( ports == NULL ) return 0; + while ( ports[count] != NULL ) + count++; + + free( ports ); + + return count; +} + +std::string MidiOutJack :: getPortName( unsigned int portNumber ) +{ + JackMidiData *data = static_cast (apiData_); + std::string retStr(""); + + connect(); + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); + + // Check port validity + if ( ports == NULL ) { + errorString_ = "MidiOutJack::getPortName: no ports available!"; + error( RtMidiError::WARNING, errorString_ ); + return retStr; + } + + if ( ports[portNumber] == NULL ) { + std::ostringstream ost; + ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + } + else retStr.assign( ports[portNumber] ); + + free( ports ); + return retStr; +} + +void MidiOutJack :: closePort() +{ + JackMidiData *data = static_cast (apiData_); + + if ( data->port == NULL ) return; + +#ifdef HAVE_SEMAPHORE + struct timespec ts; + if ( clock_gettime( CLOCK_REALTIME, &ts ) != -1 ) { + ts.tv_sec += 1; // wait max one second + sem_post( &data->sem_needpost ); + sem_timedwait( &data->sem_cleanup, &ts ); + } +#endif + + jack_port_unregister( data->client, data->port ); + data->port = NULL; + + connected_ = false; +} + +void MidiOutJack:: setClientName( const std::string& ) +{ + + errorString_ = "MidiOutJack::setClientName: this function is not implemented for the UNIX_JACK API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiOutJack :: setPortName( const std::string &portName ) +{ + JackMidiData *data = static_cast (apiData_); +#ifdef JACK_HAS_PORT_RENAME + jack_port_rename( data->client, data->port, portName.c_str() ); +#else + jack_port_set_name( data->port, portName.c_str() ); +#endif +} + +void MidiOutJack :: sendMessage( const unsigned char *message, size_t size ) +{ + int nBytes = static_cast(size); + JackMidiData *data = static_cast (apiData_); + + if ( size + sizeof(nBytes) > (size_t) data->buffMaxWrite ) + return; + + while ( jack_ringbuffer_write_space(data->buff) < sizeof(nBytes) + size ) + sched_yield(); + + // Write full message to buffer + jack_ringbuffer_write( data->buff, ( char * ) &nBytes, sizeof( nBytes ) ); + jack_ringbuffer_write( data->buff, ( const char * ) message, nBytes ); +} + +#endif // __UNIX_JACK__ + +//*********************************************************************// +// API: Web MIDI +// +// Written primarily by Atsushi Eno, February 2020. +// +// *********************************************************************// + +#if defined(__WEB_MIDI_API__) + +#include + +//*********************************************************************// +// API: WEB MIDI +// Class Definitions: WebMidiAccessShim +//*********************************************************************// + +class WebMidiAccessShim +{ +public: + WebMidiAccessShim(); + ~WebMidiAccessShim(); + std::string getPortName( unsigned int portNumber, bool isInput ); +}; + +std::unique_ptr shim{nullptr}; + +void ensureShim() +{ + if ( shim.get() != nullptr ) + return; + shim.reset( new WebMidiAccessShim() ); +} + +bool checkWebMidiAvailability() +{ + ensureShim(); + + return MAIN_THREAD_EM_ASM_INT( { + if ( typeof window._rtmidi_internals_waiting === "undefined" ) { + console.log ( "Attempted to use Web MIDI API without trying to open it." ); + return false; + } + if ( window._rtmidi_internals_waiting ) { + console.log ( "Attempted to use Web MIDI API while it is being queried." ); + return false; + } + if ( _rtmidi_internals_midi_access == null ) { + console.log ( "Attempted to use Web MIDI API while it already turned out to be unavailable." ); + return false; + } + return true; + } ); +} + +WebMidiAccessShim::WebMidiAccessShim() +{ + MAIN_THREAD_ASYNC_EM_ASM( { + if( typeof window._rtmidi_internals_midi_access !== "undefined" ) + return; + if( typeof window._rtmidi_internals_waiting !== "undefined" ) { + console.log( "MIDI Access was requested while another request is in progress." ); + return; + } + + // define functions + window._rtmidi_internals_get_port_by_number = function( portNumber, isInput ) { + var midi = window._rtmidi_internals_midi_access; + var devices = isInput ? midi.inputs : midi.outputs; + var i = 0; + for (var device of devices.values()) { + if ( i == portNumber ) + return device; + i++; + } + console.log( "MIDI " + (isInput ? "input" : "output") + " device of portNumber " + portNumber + " is not found."); + return null; + }; + + window._rtmidi_internals_waiting = true; + window.navigator.requestMIDIAccess( {"sysex": true} ).then( (midiAccess) => { + window._rtmidi_internals_midi_access = midiAccess; + window._rtmidi_internals_latest_message_timestamp = 0.0; + window._rtmidi_internals_waiting = false; + if( midiAccess == null ) { + console.log ( "Could not get access to MIDI API" ); + } + } ); + } ); +} + +WebMidiAccessShim::~WebMidiAccessShim() +{ +} + +std::string WebMidiAccessShim::getPortName( unsigned int portNumber, bool isInput ) +{ + if( !checkWebMidiAvailability() ) + return ""; + char *ret = (char*) MAIN_THREAD_EM_ASM_INT( { + var port = window._rtmidi_internals_get_port_by_number($0, $1); + if( port == null) + return null; + var length = lengthBytesUTF8(port.name) + 1; + var ret = _malloc(length); + stringToUTF8(port.name, ret, length); + return ret; + }, portNumber, isInput); + if (ret == nullptr) + return ""; + std::string s = ret; + free(ret); + return s; +} + +//*********************************************************************// +// API: WEB MIDI +// Class Definitions: MidiInWeb +//*********************************************************************// + +MidiInWeb::MidiInWeb( const std::string &clientName, unsigned int queueSizeLimit ) + : MidiInApi( queueSizeLimit ) +{ + initialize( clientName ); +} + +MidiInWeb::~MidiInWeb( void ) +{ + closePort(); +} + +extern "C" void EMSCRIPTEN_KEEPALIVE rtmidi_onMidiMessageProc( MidiInApi::RtMidiInData* data, uint8_t* inputBytes, int32_t length, double domHighResTimeStamp ) +{ + auto &message = data->message; + message.bytes.resize(message.bytes.size() + length); + memcpy(message.bytes.data(), inputBytes, length); + // FIXME: handle timestamp + if ( data->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; + callback( message.timeStamp, &message.bytes, data->userData ); + } +} + +void MidiInWeb::openPort( unsigned int portNumber, const std::string &portName ) +{ + if( !checkWebMidiAvailability() ) + return; + if (open_port_number >= 0) + return; + + MAIN_THREAD_EM_ASM( { + // In Web MIDI API world, there is no step to open a port, but we have to register the input callback instead. + var input = window._rtmidi_internals_get_port_by_number($0, true); + input.onmidimessage = function(e) { + // In RtMidi world, timestamps are delta time from previous message, while in Web MIDI world + // timestamps are relative to window creation time (i.e. kind of absolute time with window "epoch" time). + var rtmidiTimestamp = window._rtmidi_internals_latest_message_timestamp == 0.0 ? 0.0 : e.timeStamp - window._rtmidi_internals_latest_message_timestamp; + window._rtmidi_internals_latest_message_timestamp = e.timeStamp; + Module.ccall( 'rtmidi_onMidiMessageProc', 'void', ['number', 'array', 'number', 'number'], [$1, e.data, e.data.length, rtmidiTimestamp] ); + }; + }, portNumber, &inputData_ ); + open_port_number = portNumber; +} + +void MidiInWeb::openVirtualPort( const std::string &portName ) +{ + + errorString_ = "MidiInWeb::openVirtualPort: this function is not implemented for the Web MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiInWeb::closePort( void ) +{ + if( open_port_number < 0 ) + return; + + MAIN_THREAD_EM_ASM( { + var input = _rtmidi_internals_get_port_by_number($0, true); + if( input == null ) { + console.log( "Port #" + $0 + " could not be found."); + return; + } + // unregister event handler + input.onmidimessage = null; + }, open_port_number ); + open_port_number = -1; +} + +void MidiInWeb::setClientName( const std::string &clientName ) +{ + client_name = clientName; +} + +void MidiInWeb::setPortName( const std::string &portName ) +{ + + errorString_ = "MidiInWeb::setPortName: this function is not implemented for the Web MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +unsigned int MidiInWeb::getPortCount( void ) +{ + if( !checkWebMidiAvailability() ) + return 0; + return MAIN_THREAD_EM_ASM_INT( { return _rtmidi_internals_midi_access.inputs.size; } ); +} + +std::string MidiInWeb::getPortName( unsigned int portNumber ) +{ + if( !checkWebMidiAvailability() ) + return ""; + return shim->getPortName( portNumber, true ); +} + +void MidiInWeb::initialize( const std::string& clientName ) +{ + ensureShim(); + setClientName( clientName ); +} + +//*********************************************************************// +// API: WEB MIDI +// Class Definitions: MidiOutWeb +//*********************************************************************// + +MidiOutWeb::MidiOutWeb( const std::string &clientName ) +{ + initialize( clientName ); +} + +MidiOutWeb::~MidiOutWeb( void ) +{ + closePort(); +} + +void MidiOutWeb::openPort( unsigned int portNumber, const std::string &portName ) +{ + if( !checkWebMidiAvailability() ) + return; + if (open_port_number >= 0) + return; + // In Web MIDI API world, there is no step to open a port. + + open_port_number = portNumber; +} + +void MidiOutWeb::openVirtualPort( const std::string &portName ) +{ + + errorString_ = "MidiOutWeb::openVirtualPort: this function is not implemented for the Web MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +void MidiOutWeb::closePort( void ) +{ + // there is really nothing to do for output at JS side. + open_port_number = -1; +} + +void MidiOutWeb::setClientName( const std::string &clientName ) +{ + client_name = clientName; +} + +void MidiOutWeb::setPortName( const std::string &portName ) +{ + + errorString_ = "MidiOutWeb::setPortName: this function is not implemented for the Web MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); + +} + +unsigned int MidiOutWeb::getPortCount( void ) +{ + if( !checkWebMidiAvailability() ) + return 0; + return MAIN_THREAD_EM_ASM_INT( { return _rtmidi_internals_midi_access.outputs.size; } ); +} + +std::string MidiOutWeb::getPortName( unsigned int portNumber ) +{ + if( !checkWebMidiAvailability() ) + return ""; + return shim->getPortName( portNumber, false ); +} + +void MidiOutWeb::sendMessage( const unsigned char *message, size_t size ) +{ + if( open_port_number < 0 ) + return; + + MAIN_THREAD_EM_ASM( { + var output = _rtmidi_internals_get_port_by_number( $0, false ); + if( output == null ) { + console.log( "Port #" + $0 + " could not be found."); + return; + } + var buf = new ArrayBuffer ($2); + var msg = new Uint8Array( buf ); + msg.set( new Uint8Array( Module.HEAPU8.buffer.slice( $1, $1 + $2 ) ) ); + output.send( msg ); + }, open_port_number, message, size ); +} + +void MidiOutWeb::initialize( const std::string& clientName ) +{ + if ( shim.get() != nullptr ) + return; + shim.reset( new WebMidiAccessShim() ); + setClientName( clientName ); +} + +#endif // __WEB_MIDI_API__ diff --git a/distrho/src/jackbridge/rtmidi/RtMidi.h b/distrho/src/jackbridge/rtmidi/RtMidi.h new file mode 100644 index 00000000..a6f5b794 --- /dev/null +++ b/distrho/src/jackbridge/rtmidi/RtMidi.h @@ -0,0 +1,658 @@ +/**********************************************************************/ +/*! \class RtMidi + \brief An abstract base class for realtime MIDI input/output. + + This class implements some common functionality for the realtime + MIDI input/output subclasses RtMidiIn and RtMidiOut. + + RtMidi GitHub site: https://github.com/thestk/rtmidi + RtMidi WWW site: http://www.music.mcgill.ca/~gary/rtmidi/ + + RtMidi: realtime MIDI i/o C++ classes + Copyright (c) 2003-2021 Gary P. Scavone + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/**********************************************************************/ + +/*! + \file RtMidi.h + */ + +#ifndef RTMIDI_H +#define RTMIDI_H + +#if defined _WIN32 || defined __CYGWIN__ + #if defined(RTMIDI_EXPORT) + #define RTMIDI_DLL_PUBLIC __declspec(dllexport) + #else + #define RTMIDI_DLL_PUBLIC + #endif +#else + #if __GNUC__ >= 4 + #define RTMIDI_DLL_PUBLIC __attribute__( (visibility( "default" )) ) + #else + #define RTMIDI_DLL_PUBLIC + #endif +#endif + +#define RTMIDI_VERSION "5.0.0" + +#include +#include +#include +#include + + +/************************************************************************/ +/*! \class RtMidiError + \brief Exception handling class for RtMidi. + + The RtMidiError class is quite simple but it does allow errors to be + "caught" by RtMidiError::Type. See the RtMidi documentation to know + which methods can throw an RtMidiError. +*/ +/************************************************************************/ + +class RTMIDI_DLL_PUBLIC RtMidiError : public std::exception +{ + public: + //! Defined RtMidiError types. + enum Type { + WARNING, /*!< A non-critical error. */ + DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ + UNSPECIFIED, /*!< The default, unspecified error type. */ + NO_DEVICES_FOUND, /*!< No devices found on system. */ + INVALID_DEVICE, /*!< An invalid device ID was specified. */ + MEMORY_ERROR, /*!< An error occured during memory allocation. */ + INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ + INVALID_USE, /*!< The function was called incorrectly. */ + DRIVER_ERROR, /*!< A system driver error occured. */ + SYSTEM_ERROR, /*!< A system error occured. */ + THREAD_ERROR /*!< A thread error occured. */ + }; + + //! The constructor. + RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() + : message_(message), type_(type) {} + + //! The destructor. + virtual ~RtMidiError( void ) throw() {} + + //! Prints thrown error message to stderr. + virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } + + //! Returns the thrown error message type. + virtual const Type& getType( void ) const throw() { return type_; } + + //! Returns the thrown error message string. + virtual const std::string& getMessage( void ) const throw() { return message_; } + + //! Returns the thrown error message as a c-style string. + virtual const char* what( void ) const throw() { return message_.c_str(); } + + protected: + std::string message_; + Type type_; +}; + +//! RtMidi error callback function prototype. +/*! + \param type Type of error. + \param errorText Error description. + + Note that class behaviour is undefined after a critical error (not + a warning) is reported. + */ +typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData ); + +class MidiApi; + +class RTMIDI_DLL_PUBLIC RtMidi +{ + public: + + RtMidi(RtMidi&& other) noexcept; + //! MIDI API specifier arguments. + enum Api { + UNSPECIFIED, /*!< Search for a working compiled API. */ + MACOSX_CORE, /*!< Macintosh OS-X CoreMIDI API. */ + LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ + UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */ + WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ + RTMIDI_DUMMY, /*!< A compilable but non-functional API. */ + WEB_MIDI_API, /*!< W3C Web MIDI API. */ + NUM_APIS /*!< Number of values in this enum. */ + }; + + //! A static function to determine the current RtMidi version. + static std::string getVersion( void ) throw(); + + //! A static function to determine the available compiled MIDI APIs. + /*! + The values returned in the std::vector can be compared against + the enumerated list values. Note that there can be more than one + API compiled for certain operating systems. + */ + static void getCompiledApi( std::vector &apis ) throw(); + + //! Return the name of a specified compiled MIDI API. + /*! + This obtains a short lower-case name used for identification purposes. + This value is guaranteed to remain identical across library versions. + If the API is unknown, this function will return the empty string. + */ + static std::string getApiName( RtMidi::Api api ); + + //! Return the display name of a specified compiled MIDI API. + /*! + This obtains a long name used for display purposes. + If the API is unknown, this function will return the empty string. + */ + static std::string getApiDisplayName( RtMidi::Api api ); + + //! Return the compiled MIDI API having the given name. + /*! + A case insensitive comparison will check the specified name + against the list of compiled APIs, and return the one which + matches. On failure, the function returns UNSPECIFIED. + */ + static RtMidi::Api getCompiledApiByName( const std::string &name ); + + //! Pure virtual openPort() function. + virtual void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi" ) ) = 0; + + //! Pure virtual openVirtualPort() function. + virtual void openVirtualPort( const std::string &portName = std::string( "RtMidi" ) ) = 0; + + //! Pure virtual getPortCount() function. + virtual unsigned int getPortCount() = 0; + + //! Pure virtual getPortName() function. + virtual std::string getPortName( unsigned int portNumber = 0 ) = 0; + + //! Pure virtual closePort() function. + virtual void closePort( void ) = 0; + + void setClientName( const std::string &clientName ); + void setPortName( const std::string &portName ); + + //! Returns true if a port is open and false if not. + /*! + Note that this only applies to connections made with the openPort() + function, not to virtual ports. + */ + virtual bool isPortOpen( void ) const = 0; + + //! Set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0; + + protected: + RtMidi(); + virtual ~RtMidi(); + MidiApi *rtapi_; + + /* Make the class non-copyable */ + RtMidi(RtMidi& other) = delete; + RtMidi& operator=(RtMidi& other) = delete; +}; + +/**********************************************************************/ +/*! \class RtMidiIn + \brief A realtime MIDI input class. + + This class provides a common, platform-independent API for + realtime MIDI input. It allows access to a single MIDI input + port. Incoming MIDI messages are either saved to a queue for + retrieval using the getMessage() function or immediately passed to + a user-specified callback function. Create multiple instances of + this class to connect to more than one MIDI device at the same + time. With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also + possible to open a virtual input port to which other MIDI software + clients can connect. +*/ +/**********************************************************************/ + +// **************************************************************** // +// +// RtMidiIn and RtMidiOut class declarations. +// +// RtMidiIn / RtMidiOut are "controllers" used to select an available +// MIDI input or output interface. They present common APIs for the +// user to call but all functionality is implemented by the classes +// MidiInApi, MidiOutApi and their subclasses. RtMidiIn and RtMidiOut +// each create an instance of a MidiInApi or MidiOutApi subclass based +// on the user's API choice. If no choice is made, they attempt to +// make a "logical" API selection. +// +// **************************************************************** // + +class RTMIDI_DLL_PUBLIC RtMidiIn : public RtMidi +{ + public: + //! User callback function type definition. + typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData ); + + //! Default constructor that allows an optional api, client name and queue size. + /*! + An exception will be thrown if a MIDI system initialization + error occurs. The queue size defines the maximum number of + messages that can be held in the MIDI queue (when not using a + callback function). If the queue size limit is reached, + incoming messages will be ignored. + + If no API argument is specified and multiple API support has been + compiled, the default order of use is ALSA, JACK (Linux) and CORE, + JACK (OS-X). + + \param api An optional API id can be specified. + \param clientName An optional client name can be specified. This + will be used to group the ports that are created + by the application. + \param queueSizeLimit An optional size of the MIDI input queue can be specified. + */ + RtMidiIn( RtMidi::Api api=UNSPECIFIED, + const std::string& clientName = "RtMidi Input Client", + unsigned int queueSizeLimit = 100 ); + + RtMidiIn(RtMidiIn&& other) noexcept : RtMidi(std::move(other)) { } + + //! If a MIDI connection is still open, it will be closed by the destructor. + ~RtMidiIn ( void ) throw(); + + //! Returns the MIDI API specifier for the current instance of RtMidiIn. + RtMidi::Api getCurrentApi( void ) throw(); + + //! Open a MIDI input connection given by enumeration number. + /*! + \param portNumber An optional port number greater than 0 can be specified. + Otherwise, the default or first port found is opened. + \param portName An optional name for the application port that is used to connect to portId can be specified. + */ + void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Input" ) ); + + //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only). + /*! + This function creates a virtual MIDI input port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X, any JACK, + and Linux ALSA APIs (the function returns an error for the other APIs). + + \param portName An optional name for the application port that is + used to connect to portId can be specified. + */ + void openVirtualPort( const std::string &portName = std::string( "RtMidi Input" ) ); + + //! Set a callback function to be invoked for incoming MIDI messages. + /*! + The callback function will be called whenever an incoming MIDI + message is received. While not absolutely necessary, it is best + to set the callback function before opening a MIDI port to avoid + leaving some messages in the queue. + + \param callback A callback function must be given. + \param userData Optionally, a pointer to additional data can be + passed to the callback function whenever it is called. + */ + void setCallback( RtMidiCallback callback, void *userData = 0 ); + + //! Cancel use of the current callback function (if one exists). + /*! + Subsequent incoming MIDI messages will be written to the queue + and can be retrieved with the \e getMessage function. + */ + void cancelCallback(); + + //! Close an open MIDI connection (if one exists). + void closePort( void ); + + //! Returns true if a port is open and false if not. + /*! + Note that this only applies to connections made with the openPort() + function, not to virtual ports. + */ + virtual bool isPortOpen() const; + + //! Return the number of available MIDI input ports. + /*! + \return This function returns the number of MIDI ports of the selected API. + */ + unsigned int getPortCount(); + + //! Return a string identifier for the specified MIDI input port number. + /*! + \return The name of the port with the given Id is returned. + \retval An empty string is returned if an invalid port specifier + is provided. User code should assume a UTF-8 encoding. + */ + std::string getPortName( unsigned int portNumber = 0 ); + + //! Specify whether certain MIDI message types should be queued or ignored during input. + /*! + By default, MIDI timing and active sensing messages are ignored + during message input because of their relative high data rates. + MIDI sysex messages are ignored by default as well. Variable + values of "true" imply that the respective message type will be + ignored. + */ + void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); + + //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. + /*! + This function returns immediately whether a new message is + available or not. A valid message is indicated by a non-zero + vector size. An exception is thrown if an error occurs during + message retrieval or an input connection was not previously + established. + */ + double getMessage( std::vector *message ); + + //! Set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); + + //! Set maximum expected incoming message size. + /*! + For APIs that require manual buffer management, it can be useful to set the buffer + size and buffer count when expecting to receive large SysEx messages. Note that + currently this function has no effect when called after openPort(). The default + buffer size is 1024 with a count of 4 buffers, which should be sufficient for most + cases; as mentioned, this does not affect all API backends, since most either support + dynamically scalable buffers or take care of buffer handling themselves. It is + principally intended for users of the Windows MM backend who must support receiving + especially large messages. + */ + virtual void setBufferSize( unsigned int size, unsigned int count ); + + protected: + void openMidiApi( RtMidi::Api api, const std::string &clientName, unsigned int queueSizeLimit ); +}; + +/**********************************************************************/ +/*! \class RtMidiOut + \brief A realtime MIDI output class. + + This class provides a common, platform-independent API for MIDI + output. It allows one to probe available MIDI output ports, to + connect to one such port, and to send MIDI bytes immediately over + the connection. Create multiple instances of this class to + connect to more than one MIDI device at the same time. With the + OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a + virtual port to which other MIDI software clients can connect. +*/ +/**********************************************************************/ + +class RTMIDI_DLL_PUBLIC RtMidiOut : public RtMidi +{ + public: + //! Default constructor that allows an optional client name. + /*! + An exception will be thrown if a MIDI system initialization error occurs. + + If no API argument is specified and multiple API support has been + compiled, the default order of use is ALSA, JACK (Linux) and CORE, + JACK (OS-X). + */ + RtMidiOut( RtMidi::Api api=UNSPECIFIED, + const std::string& clientName = "RtMidi Output Client" ); + + RtMidiOut(RtMidiOut&& other) noexcept : RtMidi(std::move(other)) { } + + //! The destructor closes any open MIDI connections. + ~RtMidiOut( void ) throw(); + + //! Returns the MIDI API specifier for the current instance of RtMidiOut. + RtMidi::Api getCurrentApi( void ) throw(); + + //! Open a MIDI output connection. + /*! + An optional port number greater than 0 can be specified. + Otherwise, the default or first port found is opened. An + exception is thrown if an error occurs while attempting to make + the port connection. + */ + void openPort( unsigned int portNumber = 0, const std::string &portName = std::string( "RtMidi Output" ) ); + + //! Close an open MIDI connection (if one exists). + void closePort( void ); + + //! Returns true if a port is open and false if not. + /*! + Note that this only applies to connections made with the openPort() + function, not to virtual ports. + */ + virtual bool isPortOpen() const; + + //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only). + /*! + This function creates a virtual MIDI output port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X, Linux ALSA + and JACK APIs (the function does nothing with the other APIs). + An exception is thrown if an error occurs while attempting to + create the virtual port. + */ + void openVirtualPort( const std::string &portName = std::string( "RtMidi Output" ) ); + + //! Return the number of available MIDI output ports. + unsigned int getPortCount( void ); + + //! Return a string identifier for the specified MIDI port type and number. + /*! + \return The name of the port with the given Id is returned. + \retval An empty string is returned if an invalid port specifier + is provided. User code should assume a UTF-8 encoding. + */ + std::string getPortName( unsigned int portNumber = 0 ); + + //! Immediately send a single message out an open MIDI output port. + /*! + An exception is thrown if an error occurs during output or an + output connection was not previously established. + */ + void sendMessage( const std::vector *message ); + + //! Immediately send a single message out an open MIDI output port. + /*! + An exception is thrown if an error occurs during output or an + output connection was not previously established. + + \param message A pointer to the MIDI message as raw bytes + \param size Length of the MIDI message in bytes + */ + void sendMessage( const unsigned char *message, size_t size ); + + //! Set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); + + protected: + void openMidiApi( RtMidi::Api api, const std::string &clientName ); +}; + + +// **************************************************************** // +// +// MidiInApi / MidiOutApi class declarations. +// +// Subclasses of MidiInApi and MidiOutApi contain all API- and +// OS-specific code necessary to fully implement the RtMidi API. +// +// Note that MidiInApi and MidiOutApi are abstract base classes and +// cannot be explicitly instantiated. RtMidiIn and RtMidiOut will +// create instances of a MidiInApi or MidiOutApi subclass. +// +// **************************************************************** // + +class RTMIDI_DLL_PUBLIC MidiApi +{ + public: + + MidiApi(); + virtual ~MidiApi(); + virtual RtMidi::Api getCurrentApi( void ) = 0; + virtual void openPort( unsigned int portNumber, const std::string &portName ) = 0; + virtual void openVirtualPort( const std::string &portName ) = 0; + virtual void closePort( void ) = 0; + virtual void setClientName( const std::string &clientName ) = 0; + virtual void setPortName( const std::string &portName ) = 0; + + virtual unsigned int getPortCount( void ) = 0; + virtual std::string getPortName( unsigned int portNumber ) = 0; + + inline bool isPortOpen() const { return connected_; } + void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ); + + //! A basic error reporting function for RtMidi classes. + void error( RtMidiError::Type type, std::string errorString ); + +protected: + virtual void initialize( const std::string& clientName ) = 0; + + void *apiData_; + bool connected_; + std::string errorString_; + RtMidiErrorCallback errorCallback_; + bool firstErrorOccurred_; + void *errorCallbackUserData_; + +}; + +class RTMIDI_DLL_PUBLIC MidiInApi : public MidiApi +{ + public: + + MidiInApi( unsigned int queueSizeLimit ); + virtual ~MidiInApi( void ); + void setCallback( RtMidiIn::RtMidiCallback callback, void *userData ); + void cancelCallback( void ); + virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ); + double getMessage( std::vector *message ); + virtual void setBufferSize( unsigned int size, unsigned int count ); + + // A MIDI structure used internally by the class to store incoming + // messages. Each message represents one and only one MIDI message. + struct MidiMessage { + std::vector bytes; + + //! Time in seconds elapsed since the previous message + double timeStamp; + + // Default constructor. + MidiMessage() + : bytes(0), timeStamp(0.0) {} + }; + + struct MidiQueue { + unsigned int front; + unsigned int back; + unsigned int ringSize; + MidiMessage *ring; + + // Default constructor. + MidiQueue() + : front(0), back(0), ringSize(0), ring(0) {} + bool push( const MidiMessage& ); + bool pop( std::vector*, double* ); + unsigned int size( unsigned int *back=0, unsigned int *front=0 ); + }; + + // The RtMidiInData structure is used to pass private class data to + // the MIDI input handling function or thread. + struct RtMidiInData { + MidiQueue queue; + MidiMessage message; + unsigned char ignoreFlags; + bool doInput; + bool firstMessage; + void *apiData; + bool usingCallback; + RtMidiIn::RtMidiCallback userCallback; + void *userData; + bool continueSysex; + unsigned int bufferSize; + unsigned int bufferCount; + + // Default constructor. + RtMidiInData() + : ignoreFlags(7), doInput(false), firstMessage(true), apiData(0), usingCallback(false), + userCallback(0), userData(0), continueSysex(false), bufferSize(1024), bufferCount(4) {} + }; + + protected: + RtMidiInData inputData_; +}; + +class RTMIDI_DLL_PUBLIC MidiOutApi : public MidiApi +{ + public: + + MidiOutApi( void ); + virtual ~MidiOutApi( void ); + virtual void sendMessage( const unsigned char *message, size_t size ) = 0; +}; + +// **************************************************************** // +// +// Inline RtMidiIn and RtMidiOut definitions. +// +// **************************************************************** // + +inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } +inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } +inline void RtMidiIn :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } +inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } +inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); } +inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { static_cast(rtapi_)->setCallback( callback, userData ); } +inline void RtMidiIn :: cancelCallback( void ) { static_cast(rtapi_)->cancelCallback(); } +inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } +inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } +inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { static_cast(rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } +inline double RtMidiIn :: getMessage( std::vector *message ) { return static_cast(rtapi_)->getMessage( message ); } +inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } +inline void RtMidiIn :: setBufferSize( unsigned int size, unsigned int count ) { static_cast(rtapi_)->setBufferSize(size, count); } + +inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } +inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string &portName ) { rtapi_->openPort( portNumber, portName ); } +inline void RtMidiOut :: openVirtualPort( const std::string &portName ) { rtapi_->openVirtualPort( portName ); } +inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } +inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } +inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } +inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } +inline void RtMidiOut :: sendMessage( const std::vector *message ) { static_cast(rtapi_)->sendMessage( &message->at(0), message->size() ); } +inline void RtMidiOut :: sendMessage( const unsigned char *message, size_t size ) { static_cast(rtapi_)->sendMessage( message, size ); } +inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } + +#endif From 24d413912920d0536a452e3091f4e1445826652b Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 21:49:39 +0100 Subject: [PATCH 482/504] Fix build Signed-off-by: falkTX --- distrho/src/jackbridge/RtAudioBridge.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index 5c72f549..5b01c8b8 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -53,9 +53,7 @@ using DISTRHO_NAMESPACE::String; struct RtAudioBridge : NativeBridge { // pointer to RtAudio instance ScopedPointer handle; -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 bool captureEnabled = false; -#endif #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT std::vector midiIns; #endif @@ -233,7 +231,7 @@ struct RtAudioBridge : NativeBridge { } DISTRHO_SAFE_EXCEPTION("midiIn.openPort()"); } #endif - #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if defined(RTMIDI_API_TYPE) && DISTRHO_PLUGIN_WANT_MIDI_OUTPUT for (uint i=0; i* message, void* userData) + static void RtMidiCallback(double /*timeStamp*/, std::vector* message, void* userData) { const size_t len = message->size(); DISTRHO_SAFE_ASSERT_RETURN(len > 0 && len <= kMaxMIDIInputMessageSize,); From d41b971d77dceadcb76a7736908d0b6a36ff1469 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 22 Jul 2022 22:15:23 +0100 Subject: [PATCH 483/504] Set audio port group hints for most example plugins, fixing vst3 Signed-off-by: falkTX --- examples/CairoUI/CairoExamplePlugin.cpp | 13 +++++++++++++ examples/CairoUI/Makefile | 3 ++- .../EmbedExternalExamplePlugin.cpp | 13 +++++++++++++ examples/FileHandling/FileHandlingPlugin.cpp | 13 +++++++++++++ examples/FileHandling/Makefile | 3 ++- examples/Info/InfoExamplePlugin.cpp | 13 +++++++++++++ examples/Latency/LatencyExamplePlugin.cpp | 13 +++++++++++++ examples/Metronome/ExamplePluginMetronome.cpp | 13 +++++++++++++ examples/Parameters/ExamplePluginParameters.cpp | 13 +++++++++++++ examples/SendNote/SendNoteExamplePlugin.cpp | 17 +++++++++++++---- examples/States/ExamplePluginStates.cpp | 13 +++++++++++++ 11 files changed, 121 insertions(+), 6 deletions(-) diff --git a/examples/CairoUI/CairoExamplePlugin.cpp b/examples/CairoUI/CairoExamplePlugin.cpp index 38891516..f22f1552 100644 --- a/examples/CairoUI/CairoExamplePlugin.cpp +++ b/examples/CairoUI/CairoExamplePlugin.cpp @@ -54,6 +54,19 @@ public: return d_cconst('d', 'C', 'a', 'i'); } + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupMono; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + void initParameter(uint32_t index, Parameter& parameter) { // unused diff --git a/examples/CairoUI/Makefile b/examples/CairoUI/Makefile index 2c2c2489..309654a4 100644 --- a/examples/CairoUI/Makefile +++ b/examples/CairoUI/Makefile @@ -40,7 +40,8 @@ endif # HAVE_LIBLO endif # MACOS_OR_WINDOWS TARGETS += lv2_sep -TARGETS += vst +TARGETS += vst2 +TARGETS += vst3 endif # HAVE_CAIRO diff --git a/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp b/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp index 0eb65941..e16b318d 100644 --- a/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp +++ b/examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp @@ -99,6 +99,19 @@ protected: /* -------------------------------------------------------------------------------------------------------- * Init */ + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupStereo; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + /** Initialize the parameter @a index. This function will be called once, shortly after the plugin is created. diff --git a/examples/FileHandling/FileHandlingPlugin.cpp b/examples/FileHandling/FileHandlingPlugin.cpp index 691a4557..1a00ddfc 100644 --- a/examples/FileHandling/FileHandlingPlugin.cpp +++ b/examples/FileHandling/FileHandlingPlugin.cpp @@ -98,6 +98,19 @@ protected: /* -------------------------------------------------------------------------------------------------------- * Init */ + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupMono; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + /** Initialize the parameter @a index. This function will be called once, shortly after the plugin is created. diff --git a/examples/FileHandling/Makefile b/examples/FileHandling/Makefile index 23cd6399..1ebd4027 100644 --- a/examples/FileHandling/Makefile +++ b/examples/FileHandling/Makefile @@ -34,7 +34,8 @@ else TARGETS += lv2_dsp endif -TARGETS += vst +TARGETS += vst2 +TARGETS += vst3 all: $(TARGETS) diff --git a/examples/Info/InfoExamplePlugin.cpp b/examples/Info/InfoExamplePlugin.cpp index 6e339c25..36264017 100644 --- a/examples/Info/InfoExamplePlugin.cpp +++ b/examples/Info/InfoExamplePlugin.cpp @@ -103,6 +103,19 @@ protected: /* -------------------------------------------------------------------------------------------------------- * Init */ + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupStereo; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + /** Initialize the parameter @a index. This function will be called once, shortly after the plugin is created. diff --git a/examples/Latency/LatencyExamplePlugin.cpp b/examples/Latency/LatencyExamplePlugin.cpp index 2d264a34..cc40bd50 100644 --- a/examples/Latency/LatencyExamplePlugin.cpp +++ b/examples/Latency/LatencyExamplePlugin.cpp @@ -108,6 +108,19 @@ protected: /* -------------------------------------------------------------------------------------------------------- * Init */ + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupMono; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + /** Initialize the parameter @a index. This function will be called once, shortly after the plugin is created. diff --git a/examples/Metronome/ExamplePluginMetronome.cpp b/examples/Metronome/ExamplePluginMetronome.cpp index 537e994b..83c484dc 100644 --- a/examples/Metronome/ExamplePluginMetronome.cpp +++ b/examples/Metronome/ExamplePluginMetronome.cpp @@ -150,6 +150,19 @@ protected: /* -------------------------------------------------------------------------------------------------------- * Init */ + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupMono; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + /** Initialize the parameter @a index. This function will be called once, shortly after the plugin is created. diff --git a/examples/Parameters/ExamplePluginParameters.cpp b/examples/Parameters/ExamplePluginParameters.cpp index eb466b49..4b705af2 100644 --- a/examples/Parameters/ExamplePluginParameters.cpp +++ b/examples/Parameters/ExamplePluginParameters.cpp @@ -110,6 +110,19 @@ The plugin will be treated as an effect, but it will not change the host audio." kPortGroupBottom }; + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupStereo; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + /** Initialize the parameter @a index. This function will be called once, shortly after the plugin is created. diff --git a/examples/SendNote/SendNoteExamplePlugin.cpp b/examples/SendNote/SendNoteExamplePlugin.cpp index df4899e9..937526c0 100644 --- a/examples/SendNote/SendNoteExamplePlugin.cpp +++ b/examples/SendNote/SendNoteExamplePlugin.cpp @@ -100,11 +100,20 @@ protected: } /* -------------------------------------------------------------------------------------------------------- - * Init and Internal data, unused in this plugin */ + * Init */ - void initParameter(uint32_t, Parameter&) override {} - float getParameterValue(uint32_t) const override { return 0.0f;} - void setParameterValue(uint32_t, float) override {} + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupMono; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } /* -------------------------------------------------------------------------------------------------------- * Audio/MIDI Processing */ diff --git a/examples/States/ExamplePluginStates.cpp b/examples/States/ExamplePluginStates.cpp index 9808952c..119935cf 100644 --- a/examples/States/ExamplePluginStates.cpp +++ b/examples/States/ExamplePluginStates.cpp @@ -104,6 +104,19 @@ The plugin will be treated as an effect, but it will not change the host audio." /* -------------------------------------------------------------------------------------------------------- * Init */ + /** + Initialize the audio port @a index.@n + This function will be called once, shortly after the plugin is created. + */ + void initAudioPort(bool input, uint32_t index, AudioPort& port) override + { + // treat meter audio ports as stereo + port.groupId = kPortGroupStereo; + + // everything else is as default + Plugin::initAudioPort(input, index, port); + } + /** Set the name of the program @a index. This function will be called once, shortly after the plugin is created. From f412918c5f4022f074663abf86877a67f9d9a421 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Jul 2022 04:16:46 +0100 Subject: [PATCH 484/504] A few more tweaks for native-audio standalone, allow forcing Signed-off-by: falkTX --- Makefile.base.mk | 2 ++ Makefile.plugins.mk | 7 ++++-- distrho/src/DistrhoPluginJACK.cpp | 2 +- distrho/src/jackbridge/JackBridge.cpp | 27 ++++++++---------------- distrho/src/jackbridge/RtAudioBridge.hpp | 19 ++++++++++------- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 696cdb69..e5c755db 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -346,7 +346,9 @@ endif endif # backwards compat, always available/enabled +ifneq ($(FORCE_NATIVE_AUDIO_FALLBACK),true) HAVE_JACK = true +endif # --------------------------------------------------------------------------------------------------------------------- # Set Generic DGL stuff diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index d1c0c955..9f4d1e77 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -88,13 +88,16 @@ JACK_LIBS += -lole32 -lwinmm JACK_LIBS += -ldsound # WASAPI # JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid -else ifeq ($(HAVE_PULSEAUDIO),true) +else +ifeq ($(HAVE_PULSEAUDIO),true) JACK_FLAGS += $(PULSEAUDIO_FLAGS) JACK_LIBS += $(PULSEAUDIO_LIBS) -else ifeq ($(HAVE_ALSA),true) +endif +ifeq ($(HAVE_ALSA),true) JACK_FLAGS += $(ALSA_FLAGS) JACK_LIBS += $(ALSA_LIBS) endif +endif ifeq ($(HAVE_RTAUDIO),true) ifneq ($(HAIKU),true) diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index 44b932f0..35701827 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -31,7 +31,7 @@ # include "../extra/Thread.hpp" #endif -#if defined(STATIC_BUILD) && !defined(DISTRHO_OS_WASM) +#if defined(HAVE_JACK) && defined(STATIC_BUILD) && !defined(DISTRHO_OS_WASM) # define JACKBRIDGE_DIRECT #endif diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index a73d7e91..9e6b82fd 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -441,8 +441,7 @@ struct JackBridge { , set_thread_creator_ptr(nullptr) #endif { - #ifdef DISTRHO_OS_WASM - // never use jack in wasm + #ifndef HAVE_JACK return; #endif @@ -2284,13 +2283,11 @@ bool isUsingNativeAudio() noexcept bool supportsAudioInput() { -#if defined(JACKBRIDGE_DUMMY) - return false; -#elif !defined(JACKBRIDGE_DIRECT) +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) if (usingNativeBridge) return nativeBridge->supportsAudioInput(); #endif - return true; + return false; } bool supportsBufferSizeChanges() @@ -2304,35 +2301,29 @@ bool supportsBufferSizeChanges() bool supportsMIDI() { -#if defined(JACKBRIDGE_DUMMY) - return false; -#elif !defined(JACKBRIDGE_DIRECT) +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) if (usingNativeBridge) return nativeBridge->supportsMIDI(); #endif - return true; + return false; } bool isAudioInputEnabled() { -#if defined(JACKBRIDGE_DUMMY) - return false; -#elif !defined(JACKBRIDGE_DIRECT) +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) if (usingNativeBridge) return nativeBridge->isAudioInputEnabled(); #endif - return true; + return false; } bool isMIDIEnabled() { -#if defined(JACKBRIDGE_DUMMY) - return false; -#elif !defined(JACKBRIDGE_DIRECT) +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) if (usingNativeBridge) return nativeBridge->isMIDIEnabled(); #endif - return true; + return false; } uint getBufferSize() diff --git a/distrho/src/jackbridge/RtAudioBridge.hpp b/distrho/src/jackbridge/RtAudioBridge.hpp index 5b01c8b8..2d37133a 100644 --- a/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/distrho/src/jackbridge/RtAudioBridge.hpp @@ -31,14 +31,17 @@ # define __WINDOWS_DS__ # define RTAUDIO_API_TYPE WINDOWS_DS # define RTMIDI_API_TYPE WINDOWS_MM -#elif defined(HAVE_PULSEAUDIO) -# define __LINUX_PULSE__ -# define RTAUDIO_API_TYPE LINUX_PULSE -# define RTMIDI_API_TYPE LINUX_ALSA -#elif defined(HAVE_ALSA) -# define __LINUX_ALSA__ -# define RTAUDIO_API_TYPE LINUX_ALSA -# define RTMIDI_API_TYPE LINUX_ALSA +#else +# if defined(HAVE_PULSEAUDIO) +# define __LINUX_PULSE__ +# define RTAUDIO_API_TYPE LINUX_PULSE +# elif defined(HAVE_ALSA) +# define __LINUX_ALSA__ + # define RTAUDIO_API_TYPE LINUX_ALSA +# endif +# ifdef HAVE_ALSA +# define RTMIDI_API_TYPE LINUX_ALSA +# endif #endif #ifdef RTAUDIO_API_TYPE From ef1bb41477e635bbccb4dc44115e9cb19af2dc0f Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 27 Jul 2022 05:05:35 +0100 Subject: [PATCH 485/504] Switch to using macos-11 for github CI Signed-off-by: falkTX --- .github/workflows/cmake.yml | 2 +- .github/workflows/example-plugins.yml | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 5932f468..6adfba8c 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -56,7 +56,7 @@ jobs: path: ${{runner.workspace}}/build/bin/ cmake_macos: - runs-on: macos-10.15 + runs-on: macos-11 steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/example-plugins.yml b/.github/workflows/example-plugins.yml index ac270764..ee00f28a 100644 --- a/.github/workflows/example-plugins.yml +++ b/.github/workflows/example-plugins.yml @@ -141,15 +141,11 @@ jobs: bin/* macos-universal: - runs-on: macos-10.15 + runs-on: macos-11 steps: - uses: actions/checkout@v2 with: submodules: recursive - - name: Fix up Xcode - run: | - sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/* - sudo xcode-select -s "/Applications/Xcode_12.3.app" - name: Build macOS universal env: CFLAGS: -arch x86_64 -arch arm64 -DMAC_OS_X_VERSION_MAX_ALLOWED=MAC_OS_X_VERSION_10_12 -mmacosx-version-min=10.12 -mtune=generic -msse -msse2 @@ -157,7 +153,7 @@ jobs: LDFLAGS: -arch x86_64 -arch arm64 -mmacosx-version-min=10.12 run: | make features - make NOOPT=true -j $(sysctl -n hw.logicalcpu) + make HAVE_CAIRO=false NOOPT=true -j $(sysctl -n hw.logicalcpu) ./utils/package-osx-bundles.sh - name: Set sha8 id: slug From ad482c91b54ed7622084bf1a58ccc52c03579226 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Jul 2022 17:55:51 +0200 Subject: [PATCH 486/504] Make sure wasm LV2 function symbols are exported Signed-off-by: falkTX --- distrho/src/lv2/lv2.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distrho/src/lv2/lv2.h b/distrho/src/lv2/lv2.h index eaca514a..cbf4f02f 100644 --- a/distrho/src/lv2/lv2.h +++ b/distrho/src/lv2/lv2.h @@ -355,7 +355,9 @@ typedef struct _LV2_Descriptor { Put this (LV2_SYMBOL_EXPORT) before any functions that are to be loaded by the host as a symbol from the dynamic library. */ -#ifdef _WIN32 +#if defined(__EMSCRIPTEN__) +# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __attribute__((used)) +#elif defined(_WIN32) # define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __declspec(dllexport) #else # define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __attribute__((visibility("default"))) From 9d37dbf240e770195624e4fc4e06d2b49cf4661a Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 28 Jul 2022 23:58:08 +0200 Subject: [PATCH 487/504] Allow to fake/force a small buffer size for wasm Signed-off-by: falkTX --- distrho/src/jackbridge/WebBridge.hpp | 36 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp index 1353e518..3f1f7566 100644 --- a/distrho/src/jackbridge/WebBridge.hpp +++ b/distrho/src/jackbridge/WebBridge.hpp @@ -111,7 +111,10 @@ struct WebBridge : NativeBridge { return false; } - bufferSize = 2048; + bufferSize = EM_ASM_INT({ + var WAB = Module['WebAudioBridge']; + return WAB['minimizeBufferSize'] ? 256 : 2048; + }); sampleRate = EM_ASM_INT_V({ var WAB = Module['WebAudioBridge']; return WAB.audioContext.sampleRate; @@ -125,23 +128,28 @@ struct WebBridge : NativeBridge { var bufferSize = $2; var WAB = Module['WebAudioBridge']; + var realBufferSize = WAB['minimizeBufferSize'] ? 2048 : bufferSize; + var divider = realBufferSize / bufferSize; + // main processor - WAB.processor = WAB.audioContext['createScriptProcessor'](bufferSize, numInputs, numOutputs); + WAB.processor = WAB.audioContext['createScriptProcessor'](realBufferSize, numInputs, numOutputs); WAB.processor['onaudioprocess'] = function (e) { // var timestamp = performance.now(); - for (var i = 0; i < numInputs; ++i) { - var buffer = e['inputBuffer']['getChannelData'](i); - for (var j = 0; j < bufferSize; ++j) { - // setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float'); - HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j]; + for (var k = 0; k < divider; ++k) { + for (var i = 0; i < numInputs; ++i) { + var buffer = e['inputBuffer']['getChannelData'](i); + for (var j = 0; j < bufferSize; ++j) { + // setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float'); + HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[bufferSize * k + j]; + } } - } - dynCall('vi', $4, [$5]); - for (var i = 0; i < numOutputs; ++i) { - var buffer = e['outputBuffer']['getChannelData'](i); - var offset = bufferSize * (numInputs + i); - for (var j = 0; j < bufferSize; ++j) { - buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; + dynCall('vi', $4, [$5]); + for (var i = 0; i < numOutputs; ++i) { + var buffer = e['outputBuffer']['getChannelData'](i); + var offset = bufferSize * (numInputs + i); + for (var j = 0; j < bufferSize; ++j) { + buffer[bufferSize * k + j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; + } } } }; From 11e76be7057c8b189a593ca328aa871fe61fc699 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 31 Jul 2022 20:28:44 +0200 Subject: [PATCH 488/504] Easier event debug, fix wasm high-dpi and touch events Signed-off-by: falkTX --- dgl/src/WindowPrivateData.cpp | 4 ++-- dgl/src/pugl-upstream | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 60b81957..6b32e0ce 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -31,7 +31,7 @@ START_NAMESPACE_DGL -#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) +#ifdef DGL_DEBUG_EVENTS # define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); # define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); # define DGL_DBGF std::fflush(stderr); @@ -614,7 +614,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh void Window::PrivateData::onPuglExpose() { - DGL_DBG("PUGL: onPuglExpose\n"); + // DGL_DBG("PUGL: onPuglExpose\n"); puglOnDisplayPrepare(view); diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 856c759e..b54d2393 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 856c759e1e360b0a766f61bb2b2cfffe15790368 +Subproject commit b54d23933dd05aecdda9ba1f9e01781e30b12123 From 1cc5641ba765dcb22da32c66faf3a09f808bfb27 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 1 Aug 2022 20:09:58 +0100 Subject: [PATCH 489/504] Avoid libdl symbols for !HAVE_JACK and STATIC_BUILD Signed-off-by: falkTX --- distrho/src/jackbridge/JackBridge.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp index 9e6b82fd..1538f67a 100644 --- a/distrho/src/jackbridge/JackBridge.cpp +++ b/distrho/src/jackbridge/JackBridge.cpp @@ -34,7 +34,12 @@ #endif #include -#include "../../extra/LibraryUtils.hpp" + +#ifdef HAVE_JACK +# include "../../extra/LibraryUtils.hpp" +#else +typedef void* lib_t; +#endif // in case JACK fails, we fallback to native bridges simulating JACK API #include "NativeBridge.hpp" @@ -339,9 +344,9 @@ struct JackBridge { jacksym_remove_all_properties remove_all_properties_ptr; jacksym_set_property_change_callback set_property_change_callback_ptr; -#ifdef __WINE__ + #ifdef __WINE__ jacksym_set_thread_creator set_thread_creator_ptr; -#endif + #endif JackBridge() : lib(nullptr), @@ -437,14 +442,11 @@ struct JackBridge { remove_properties_ptr(nullptr), remove_all_properties_ptr(nullptr), set_property_change_callback_ptr(nullptr) -#ifdef __WINE__ + #ifdef __WINE__ , set_thread_creator_ptr(nullptr) -#endif + #endif { - #ifndef HAVE_JACK - return; - #endif - + #ifdef HAVE_JACK #if defined(DISTRHO_OS_MAC) const char* const filename = "libjack.dylib"; #elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64) @@ -585,14 +587,16 @@ struct JackBridge { LIB_SYMBOL(remove_all_properties) LIB_SYMBOL(set_property_change_callback) -#ifdef __WINE__ + #ifdef __WINE__ LIB_SYMBOL(set_thread_creator) -#endif + #endif + #endif #undef JOIN #undef LIB_SYMBOL } + #ifdef HAVE_JACK ~JackBridge() noexcept { USE_NAMESPACE_DISTRHO @@ -603,6 +607,7 @@ struct JackBridge { lib = nullptr; } } + #endif DISTRHO_DECLARE_NON_COPYABLE(JackBridge); }; From 2d5dd370c6c9f7528e29ebee8c686da9a909773d Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 2 Aug 2022 13:45:44 +0100 Subject: [PATCH 490/504] Create macOS app bundles for standalones with custom UI --- Makefile.plugins.mk | 217 +++++++++++++++------------ utils/plugin.app/Contents/Info.plist | 20 +++ utils/plugin.vst/Contents/Info.plist | 2 +- 3 files changed, 139 insertions(+), 100 deletions(-) create mode 100644 utils/plugin.app/Contents/Info.plist diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 9f4d1e77..44009bb4 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -122,103 +122,6 @@ ifeq ($(MACOS),true) OBJS_UI += $(BUILD_DIR)/DistrhoUI_macOS_$(NAME).mm.o endif -# --------------------------------------------------------------------------------------------------------------------- -# Set VST2 filename, either single binary or inside a bundle - -ifeq ($(MACOS),true) -VST2_CONTENTS = $(NAME).vst/Contents -VST2_FILENAME = $(VST2_CONTENTS)/MacOS/$(NAME) -else ifeq ($(USE_VST2_BUNDLE),true) -VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT) -else -VST2_FILENAME = $(NAME)-vst$(LIB_EXT) -endif - -# --------------------------------------------------------------------------------------------------------------------- -# Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html - -ifeq ($(LINUX),true) -VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so -else ifeq ($(MACOS),true) -VST3_CONTENTS = $(NAME).vst3/Contents -VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) -else ifeq ($(WASM),true) -VST3_FILENAME = $(NAME).vst3/Contents/wasm/$(NAME).vst3 -else ifeq ($(WINDOWS),true) -ifeq ($(CPU_I386),true) -VST3_FILENAME = $(NAME).vst3/Contents/x86-win/$(NAME).vst3 -else ifeq ($(CPU_X86_64),true) -VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3 -endif -endif - -# --------------------------------------------------------------------------------------------------------------------- -# Set plugin binary file targets - -jack = $(TARGET_DIR)/$(NAME)$(APP_EXT) -ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) -dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) -dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT) -lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) -lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) -lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) -vst2 = $(TARGET_DIR)/$(VST2_FILENAME) -ifneq ($(VST3_FILENAME),) -vst3 = $(TARGET_DIR)/$(VST3_FILENAME) -endif -shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) -static = $(TARGET_DIR)/$(NAME).a - -ifeq ($(MACOS),true) -vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist -vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/PkgInfo -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 -endif - -# --------------------------------------------------------------------------------------------------------------------- -# Set plugin symbols to export - -ifeq ($(MACOS),true) -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 -SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp -else ifeq ($(WASM),true) -SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" -SYMBOLS_DSSI = -sEXPORTED_FUNCTIONS="['ladspa_descriptor','dssi_descriptor']" -SYMBOLS_LV2DSP = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl']" -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_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" -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 -SYMBOLS_SHARED = $(DPF_PATH)/utils/symbols/shared.def -else ifneq ($(DEBUG),true) -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 -SYMBOLS_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version -endif - # --------------------------------------------------------------------------------------------------------------------- # Handle UI stuff, disable UI support automatically @@ -317,6 +220,114 @@ endif # TODO split dsp and ui object build flags BASE_FLAGS += $(DGL_FLAGS) +# --------------------------------------------------------------------------------------------------------------------- +# Set VST2 filename, either single binary or inside a bundle + +ifeq ($(MACOS),true) +VST2_CONTENTS = $(NAME).vst/Contents +VST2_FILENAME = $(VST2_CONTENTS)/MacOS/$(NAME) +else ifeq ($(USE_VST2_BUNDLE),true) +VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT) +else +VST2_FILENAME = $(NAME)-vst$(LIB_EXT) +endif + +# --------------------------------------------------------------------------------------------------------------------- +# Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html + +ifeq ($(LINUX),true) +VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so +else ifeq ($(MACOS),true) +VST3_CONTENTS = $(NAME).vst3/Contents +VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) +else ifeq ($(WASM),true) +VST3_FILENAME = $(NAME).vst3/Contents/wasm/$(NAME).vst3 +else ifeq ($(WINDOWS),true) +ifeq ($(CPU_I386),true) +VST3_FILENAME = $(NAME).vst3/Contents/x86-win/$(NAME).vst3 +else ifeq ($(CPU_X86_64),true) +VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3 +endif +endif + +# --------------------------------------------------------------------------------------------------------------------- +# Set plugin binary file targets + +ifeq ($(MACOS),true) +ifeq ($(HAVE_DGL),true) +MACOS_APP_BUNDLE = true +endif +endif + +ifeq ($(MACOS_APP_BUNDLE),true) +jack = $(TARGET_DIR)/$(NAME).app/Contents/MacOS/$(NAME) +jackfiles = $(TARGET_DIR)/$(NAME).app/Contents/Info.plist +else +jack = $(TARGET_DIR)/$(NAME)$(APP_EXT) +endif +ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) +dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) +dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT) +lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) +lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) +lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) +vst2 = $(TARGET_DIR)/$(VST2_FILENAME) +ifneq ($(VST3_FILENAME),) +vst3 = $(TARGET_DIR)/$(VST3_FILENAME) +endif +shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) +static = $(TARGET_DIR)/$(NAME).a + +ifeq ($(MACOS),true) +vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist +vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/PkgInfo +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 +endif + +# --------------------------------------------------------------------------------------------------------------------- +# Set plugin symbols to export + +ifeq ($(MACOS),true) +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 +SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp +else ifeq ($(WASM),true) +SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']" +SYMBOLS_DSSI = -sEXPORTED_FUNCTIONS="['ladspa_descriptor','dssi_descriptor']" +SYMBOLS_LV2DSP = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl']" +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_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']" +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 +SYMBOLS_SHARED = $(DPF_PATH)/utils/symbols/shared.def +else ifneq ($(DEBUG),true) +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 +SYMBOLS_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version +endif + # --------------------------------------------------------------------------------------------------------------------- # Runtime test build @@ -418,7 +429,7 @@ $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(E # --------------------------------------------------------------------------------------------------------------------- # JACK -jack: $(jack) +jack: $(jack) $(jackfiles) ifeq ($(HAVE_DGL),true) $(jack): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.o $(DGL_LIB) @@ -542,7 +553,15 @@ endif # --------------------------------------------------------------------------------------------------------------------- # macOS files -$(TARGET_DIR)/%/Contents/Info.plist: $(DPF_PATH)/utils/plugin.vst/Contents/Info.plist +$(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/Info.plist + -@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 + -@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 -@mkdir -p $(shell dirname $@) $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ diff --git a/utils/plugin.app/Contents/Info.plist b/utils/plugin.app/Contents/Info.plist new file mode 100644 index 00000000..332ada6b --- /dev/null +++ b/utils/plugin.app/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + @INFO_PLIST_PROJECT_NAME@ + CFBundleIconFile + + CFBundleIdentifier + studio.kx.distrho.@INFO_PLIST_PROJECT_NAME@ + NSHighResolutionCapable + + NSRequiresAquaSystemAppearance + + NSMicrophoneUsageDescription + @INFO_PLIST_PROJECT_NAME@ requires microphone permissions for audio input. + + diff --git a/utils/plugin.vst/Contents/Info.plist b/utils/plugin.vst/Contents/Info.plist index f2f15ff4..6bc73e2a 100644 --- a/utils/plugin.vst/Contents/Info.plist +++ b/utils/plugin.vst/Contents/Info.plist @@ -1,5 +1,5 @@ - + CFBundleDevelopmentRegion From 5f2ed0625c0e3da1916f3b3c422523a66f87137f Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 2 Aug 2022 13:46:14 +0100 Subject: [PATCH 491/504] Hook into macOS CF runloop for standalones --- dgl/src/Application.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dgl/src/Application.cpp b/dgl/src/Application.cpp index 0ec4de82..5fe05dea 100644 --- a/dgl/src/Application.cpp +++ b/dgl/src/Application.cpp @@ -16,8 +16,10 @@ #include "ApplicationPrivateData.hpp" -#ifdef __EMSCRIPTEN__ +#if defined(__EMSCRIPTEN__) # include +#elif defined(DISTRHO_OS_MAC) +# include #endif START_NAMESPACE_DGL @@ -48,8 +50,18 @@ void Application::exec(const uint idleTimeInMs) { DISTRHO_SAFE_ASSERT_RETURN(pData->isStandalone,); -#ifdef __EMSCRIPTEN__ +#if defined(__EMSCRIPTEN__) emscripten_set_main_loop_arg(app_idle, this, 0, true); +#elif defined(DISTRHO_OS_MAC) + const CFTimeInterval idleTimeInSecs = static_cast(idleTimeInMs) / 1000; + + while (! pData->isQuitting) + { + pData->idle(0); + + if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, idleTimeInSecs, true) == kCFRunLoopRunFinished) + break; + } #else while (! pData->isQuitting) pData->idle(idleTimeInMs); From c9ce3383797f398bb091d37525fd00ef7047afc7 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 2 Aug 2022 14:00:27 +0100 Subject: [PATCH 492/504] Fix last commit --- Makefile.plugins.mk | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 44009bb4..4794a08c 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -206,17 +206,6 @@ endif DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm -ifneq ($(HAVE_DGL),true) -dssi_ui = -lv2_ui = -DGL_LIBS = -OBJS_UI = -endif - -ifneq ($(HAVE_LIBLO),true) -dssi_ui = -endif - # TODO split dsp and ui object build flags BASE_FLAGS += $(DGL_FLAGS) @@ -287,6 +276,17 @@ vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/PkgInfo vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Resources/empty.lproj endif +ifneq ($(HAVE_DGL),true) +dssi_ui = +lv2_ui = +DGL_LIBS = +OBJS_UI = +endif + +ifneq ($(HAVE_LIBLO),true) +dssi_ui = +endif + # --------------------------------------------------------------------------------------------------------------------- # Set plugin symbols to export From f91b8862806c6b5ab18113a547db186be5c158e6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 3 Aug 2022 17:47:14 +0100 Subject: [PATCH 493/504] Mention VST2 and VST3 separately in the README Signed-off-by: falkTX --- README.md | 2 +- dgl/src/pugl-upstream | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 18dbc657..95003a9c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ DPF is designed to make development of new plugins an easy and enjoyable task. The framework facilitates exporting various different plugin formats from the same code-base.
-DPF can build for LADSPA, DSSI, LV2 and VST formats.
+DPF can build for LADSPA, DSSI, LV2, VST2 and VST3 formats.
All current plugin format implementations are complete.
A JACK/Standalone mode is also available, allowing you to quickly test plugins.
diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index b54d2393..8933a6f3 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit b54d23933dd05aecdda9ba1f9e01781e30b12123 +Subproject commit 8933a6f3496802b835d3248612fcda84c4f491a3 From 2d175807d69446dd4af1762244da8128bbdf9baf Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 4 Aug 2022 01:01:15 +0100 Subject: [PATCH 494/504] more aggressive wasm linker flags --- Makefile.base.mk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index e5c755db..f38581b6 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -196,7 +196,9 @@ else # Common linker flags LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections -ifneq ($(WASM),true) +ifeq ($(WASM),true) +LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1 +else LINK_OPTS += -Wl,--as-needed ifneq ($(SKIP_STRIPPING),true) LINK_OPTS += -Wl,--strip-all @@ -257,7 +259,7 @@ LINK_FLAGS = $(LINK_OPTS) $(LDFLAGS) ifeq ($(WASM),true) # Special flag for emscripten -LINK_FLAGS += -sLLD_REPORT_UNDEFINED +LINK_FLAGS += -sENVIRONMENT=web -sLLD_REPORT_UNDEFINED else ifneq ($(MACOS),true) # Not available on MacOS LINK_FLAGS += -Wl,--no-undefined From c951d1cac399d3f56661ceeaeb2ec87b718ae694 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 5 Aug 2022 12:46:48 +0100 Subject: [PATCH 495/504] Fix build against old SDL2 Signed-off-by: falkTX --- distrho/src/jackbridge/SDL2Bridge.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/distrho/src/jackbridge/SDL2Bridge.hpp b/distrho/src/jackbridge/SDL2Bridge.hpp index e4fb08b3..e40093c8 100644 --- a/distrho/src/jackbridge/SDL2Bridge.hpp +++ b/distrho/src/jackbridge/SDL2Bridge.hpp @@ -21,6 +21,14 @@ #include +#ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME +# define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME" +#endif + +#ifndef SDL_HINT_AUDIO_DEVICE_STREAM_NAME +# define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME" +#endif + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0 # error SDL without audio does not make sense #endif From 30af4179952d739b43627e50780d460e189a86db Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 6 Aug 2022 02:33:03 +0100 Subject: [PATCH 496/504] More wasm tweaks, clipboard support --- Makefile.base.mk | 7 ++++++- dgl/src/pugl-upstream | 2 +- distrho/src/jackbridge/WebBridge.hpp | 1 - 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index f38581b6..3169518a 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -197,7 +197,10 @@ else # Common linker flags LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections ifeq ($(WASM),true) +LINK_OPTS += -O3 LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1 +LINK_OPTS += -sASYNCIFY +LINK_OPTS += -sASYNCIFY_IMPORTS=puglGetAsyncClipboardData else LINK_OPTS += -Wl,--as-needed ifneq ($(SKIP_STRIPPING),true) @@ -414,7 +417,9 @@ else ifeq ($(MACOS),true) OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations OPENGL_LIBS = -framework OpenGL else ifeq ($(WASM),true) -ifneq ($(USE_GLES2),true) +ifeq ($(USE_GLES2),true) +OPENGL_LIBS = -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 +else ifneq ($(USE_GLES3),true) OPENGL_LIBS = -sLEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0 endif diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 8933a6f3..0bf91c2c 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 8933a6f3496802b835d3248612fcda84c4f491a3 +Subproject commit 0bf91c2c4c14db135386635e160304a4e085e55e diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp index 3f1f7566..3afb9e3a 100644 --- a/distrho/src/jackbridge/WebBridge.hpp +++ b/distrho/src/jackbridge/WebBridge.hpp @@ -160,7 +160,6 @@ struct WebBridge : NativeBridge { // resume/start playback on first click document.addEventListener('click', function(e) { var WAB = Module['WebAudioBridge']; - console.log(WAB.audioContext.state); if (WAB.audioContext.state === 'suspended') WAB.audioContext.resume(); }); From 72c7dc1e2fe40333b437846456e1a3448e6dfb52 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 7 Aug 2022 01:15:46 +0100 Subject: [PATCH 497/504] Fix a compiler warning --- dgl/src/Cairo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index 6cd7f201..1a5b69ec 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2022 Filipe Coelho * Copyright (C) 2019-2021 Jean Pierre Cimalando * * Permission to use, copy, modify, and/or distribute this software for any purpose with @@ -384,8 +384,8 @@ void CairoImage::loadFromMemory(const char* const rdata, const Size& s, co cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride); DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(s.getWidth() == cairo_image_surface_get_width(newsurface),); - DISTRHO_SAFE_ASSERT_RETURN(s.getHeight() == cairo_image_surface_get_height(newsurface),); + DISTRHO_SAFE_ASSERT_RETURN(static_cast(s.getWidth()) == cairo_image_surface_get_width(newsurface),); + DISTRHO_SAFE_ASSERT_RETURN(static_cast(s.getHeight()) == cairo_image_surface_get_height(newsurface),); cairo_surface_destroy(surface); From a27d7a9b67a15443ae5911dbf5c14a299d303017 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 7 Aug 2022 01:16:03 +0100 Subject: [PATCH 498/504] VST3: Properly describe provided classes, fixes Ableton; Cleanup --- distrho/src/DistrhoPluginVST3.cpp | 241 +++++++++++++++--------------- 1 file changed, 123 insertions(+), 118 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index a73f860c..2fdbda8d 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -603,110 +603,10 @@ public: #endif { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - std::vector visitedInputPortGroups; - for (uint32_t i=0; i::iterator end = visitedInputPortGroups.end(); - if (std::find(visitedInputPortGroups.begin(), end, port.groupId) == end) - { - visitedInputPortGroups.push_back(port.groupId); - ++inputBuses.numGroups; - } - continue; - } - - if (port.hints & kAudioPortIsCV) - ++inputBuses.numCV; - else - ++inputBuses.numMainAudio; - - if (port.hints & kAudioPortIsSidechain) - ++inputBuses.numSidechain; - } - - if (inputBuses.numMainAudio != 0) - inputBuses.audio = 1; - if (inputBuses.numSidechain != 0) - inputBuses.sidechain = 1; - - uint32_t cvInputBusId = 0; - for (uint32_t i=0; i(); #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - std::vector visitedOutputPortGroups; - for (uint32_t i=0; i::iterator end = visitedOutputPortGroups.end(); - if (std::find(visitedOutputPortGroups.begin(), end, port.groupId) == end) - { - visitedOutputPortGroups.push_back(port.groupId); - ++outputBuses.numGroups; - } - continue; - } - - if (port.hints & kAudioPortIsCV) - ++outputBuses.numCV; - else - ++outputBuses.numMainAudio; - - if (port.hints & kAudioPortIsSidechain) - ++outputBuses.numSidechain; - } - - if (outputBuses.numMainAudio != 0) - outputBuses.audio = 1; - if (outputBuses.numSidechain != 0) - outputBuses.sidechain = 1; - - uint32_t cvOutputBusId = 0; - for (uint32_t i=0; i(); #endif if (const uint32_t extraParameterCount = fParameterCount + kVst3InternalParameterBaseCount) @@ -1484,7 +1384,10 @@ public: AudioPortWithBusId& port(fPlugin.getAudioPort(true, i)); if (port.busId != busId) + { + d_stdout("port.busId != busId: %d %d", port.busId, busId); continue; + } v3_speaker_arrangement arr; @@ -1532,7 +1435,7 @@ public: return V3_OK; } #endif // DISTRHO_PLUGIN_NUM_INPUTS - d_stdout("invalid bus arrangement %d", busIndex); + d_stdout("invalid input bus arrangement %d", busIndex); return V3_INVALID_ARG; } else @@ -1543,7 +1446,10 @@ public: AudioPortWithBusId& port(fPlugin.getAudioPort(false, i)); if (port.busId != busId) + { + d_stdout("port.busId != busId: %d %d", port.busId, busId); continue; + } v3_speaker_arrangement arr; @@ -1591,7 +1497,7 @@ public: return V3_OK; } #endif // DISTRHO_PLUGIN_NUM_OUTPUTS - d_stdout("invalid bus arrangement %d", busIndex); + d_stdout("invalid output bus arrangement %d", busIndex); return V3_INVALID_ARG; } } @@ -2724,6 +2630,71 @@ private: TimePosition fTimePosition; #endif + // ---------------------------------------------------------------------------------------------------------------- + // bus related helper functions called on constructor + + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + template + void fillInBusInfoDetails() + { + constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + BusInfo& bufInfo(isInput ? inputBuses : outputBuses); + + std::vector visitedPortGroups; + for (uint32_t i=0; i::iterator end = visitedPortGroups.end(); + if (std::find(visitedPortGroups.begin(), end, port.groupId) == end) + { + visitedPortGroups.push_back(port.groupId); + ++bufInfo.numGroups; + } + continue; + } + + if (port.hints & kAudioPortIsCV) + ++bufInfo.numCV; + else + ++bufInfo.numMainAudio; + + if (port.hints & kAudioPortIsSidechain) + ++bufInfo.numSidechain; + } + + if (bufInfo.numMainAudio != 0) + bufInfo.audio = 1; + if (bufInfo.numSidechain != 0) + bufInfo.sidechain = 1; + + uint32_t busIdFromGroup = 0; + uint32_t busIdForCV = 0; + for (uint32_t i=0; i %i %p", idx, info); std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG); info->cardinality = 0x7FFFFFFF; - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); + + if (idx == 0) + { + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + } + else + { + std::memcpy(info->class_id, dpf_tuid_controller, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Component Controller Class", ARRAY_SIZE(info->category)); + } + return V3_OK; } @@ -4671,19 +4656,29 @@ struct dpf_factory : v3_plugin_factory_cpp { { d_stdout("dpf_factory::get_class_info_2 => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG); info->cardinality = 0x7FFFFFFF; -#if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI + #if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI info->class_flags = V3_DISTRIBUTABLE; -#endif - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + #endif DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version)); + + if (idx == 0) + { + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + } + else + { + std::memcpy(info->class_id, dpf_tuid_controller, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Component Controller Class", ARRAY_SIZE(info->category)); + } + return V3_OK; } @@ -4694,19 +4689,29 @@ struct dpf_factory : v3_plugin_factory_cpp { { d_stdout("dpf_factory::get_class_info_utf16 => %i %p", idx, info); std::memset(info, 0, sizeof(*info)); - DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG); info->cardinality = 0x7FFFFFFF; -#if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI + #if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI info->class_flags = V3_DISTRIBUTABLE; -#endif - std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); - DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + #endif DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories)); DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name)); DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy_utf16(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version)); + + if (idx == 0) + { + std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category)); + } + else + { + std::memcpy(info->class_id, dpf_tuid_controller, sizeof(v3_tuid)); + DISTRHO_NAMESPACE::strncpy(info->category, "Component Controller Class", ARRAY_SIZE(info->category)); + } + return V3_OK; } From 7fb4d014e8db06e7171446824255f1cfeb7cb173 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 7 Aug 2022 02:18:22 +0100 Subject: [PATCH 499/504] Unduplicate some VST3 common code --- distrho/src/DistrhoPluginVST3.cpp | 777 +++++++++++++----------------- 1 file changed, 328 insertions(+), 449 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 2fdbda8d..62bc6ed2 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -54,7 +54,6 @@ * - bus arrangements * - optional audio buses * - routing info, do we care? - * - set sidechain bus name from port group * == CV * - cv scaling to -1/+1 * - test in at least 1 host @@ -257,13 +256,18 @@ v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instan * VST3 DSP class. * * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things. - * The DSP is created during the "initialize" component event, and destroyed during "terminate". + * This class is created during the "initialize" component event, and destroyed during "terminate". * * The low-level VST3 stuff comes after. */ class PluginVst3 { - /* buses: we provide 1 for the main audio (if there is any) plus 1 for each sidechain or cv port. + /* Buses: count possible buses we can provide to the host, in case they are not yet defined by the developer. + * These values are only used if port groups aren't set. + * We count the number of used port groups in `numGroups`. + * + * When port groups are not in use, we fill in appropriately. + * 1 bus is provided for the main audio (if there is any) plus 1 for each sidechain or cv port. * Main audio comes first, if available. * Then sidechain, also if available. * And finally each CV port individually. @@ -288,10 +292,10 @@ class PluginVst3 } inputBuses, outputBuses; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - /* handy class for storing and sorting events and MIDI CC parameters - * only stores events for which a MIDI conversion is possible. + /* Handy class for storing and sorting VST3 events and MIDI CC parameters. + * It will only store events for which a MIDI conversion is possible. */ - struct InputEventsList { + struct InputEventList { enum Type { NoteOn, NoteOff, @@ -313,17 +317,17 @@ class PluginVst3 }; } eventListStorage[kMaxMidiEvents]; - struct InputEventTiming { + struct InputEvent { int32_t sampleOffset; const InputEventStorage* storage; - InputEventTiming* next; + InputEvent* next; } eventList[kMaxMidiEvents]; uint16_t numUsed; int32_t firstSampleOffset; int32_t lastSampleOffset; - InputEventTiming* firstEvent; - InputEventTiming* lastEvent; + InputEvent* firstEvent; + InputEvent* lastEvent; void init() { @@ -336,7 +340,7 @@ class PluginVst3 { uint32_t count = 0; - for (const InputEventTiming* event = firstEvent; event != nullptr; event = event->next) + for (const InputEvent* event = firstEvent; event != nullptr; event = event->next) { MidiEvent& midiEvent(midiEvents[count++]); midiEvent.frame = event->sampleOffset; @@ -491,7 +495,7 @@ class PluginVst3 eventStorage.type = UI_MIDI; std::memcpy(eventStorage.midi, midiData, sizeof(uint8_t)*3); - InputEventTiming* const event = &eventList[numUsed]; + InputEvent* const event = &eventList[numUsed]; event->sampleOffset = 0; event->storage = &eventStorage; @@ -514,7 +518,7 @@ class PluginVst3 private: bool placeSorted(const int32_t sampleOffset) noexcept { - InputEventTiming* const event = &eventList[numUsed]; + InputEvent* const event = &eventList[numUsed]; // initialize if (numUsed == 0) @@ -542,7 +546,7 @@ class PluginVst3 else { // keep reference out of the loop so we can check validity afterwards - InputEventTiming* event2 = firstEvent; + InputEvent* event2 = firstEvent; // iterate all events for (; event2 != nullptr; event2 = event2->next) @@ -575,39 +579,39 @@ public: PluginVst3(v3_host_application** const host) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), fComponentHandler(nullptr), -#if DISTRHO_PLUGIN_HAS_UI -# if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DISTRHO_PLUGIN_HAS_UI + #if DPF_VST3_USES_SEPARATE_CONTROLLER fConnectionFromCompToCtrl(nullptr), -# endif + #endif fConnectionFromCtrlToView(nullptr), fHostApplication(host), -#endif + #endif fParameterCount(fPlugin.getParameterCount()), fVst3ParameterCount(fParameterCount + kVst3InternalParameterCount), fCachedParameterValues(nullptr), fDummyAudioBuffer(nullptr), fParameterValuesChangedDuringProcessing(nullptr) -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI , fParameterValueChangesForUI(nullptr) , fConnectedToUI(false) -#endif -#if DISTRHO_PLUGIN_WANT_LATENCY + #endif + #if DISTRHO_PLUGIN_WANT_LATENCY , fLastKnownLatency(fPlugin.getLatency()) -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fHostEventOutputHandle(nullptr) -#endif -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS , fCurrentProgram(0) , fProgramCountMinusOne(fPlugin.getProgramCount()-1) -#endif + #endif { -#if DISTRHO_PLUGIN_NUM_INPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 fillInBusInfoDetails(); -#endif -#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 fillInBusInfoDetails(); -#endif + #endif if (const uint32_t extraParameterCount = fParameterCount + kVst3InternalParameterBaseCount) { @@ -732,14 +736,14 @@ public: return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV + outputBuses.numGroups; break; case V3_EVENT: -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (busDirection == V3_INPUT) return 1; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT if (busDirection == V3_OUTPUT) return 1; -#endif + #endif break; } @@ -755,228 +759,31 @@ public: DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG); -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 || DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - uint32_t busId = static_cast(busIndex); -#endif + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 || DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + const uint32_t busId = static_cast(busIndex); + #endif if (mediaType == V3_AUDIO) { #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - int32_t numChannels; - uint32_t flags; - v3_bus_types busType; - v3_str_128 busName = {}; - if (busDirection == V3_INPUT) { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - if (busId < inputBuses.numGroups) - { - numChannels = fPlugin.getAudioPortCountWithGroupId(true, busId); - busType = V3_AUX; - flags = 0; - - for (uint32_t i=0; i(busId, info); #else - d_stdout("invalid bus %d", busId); + d_stdout("invalid input bus %d", busId); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_INPUTS } else { #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - if (busId < outputBuses.numGroups) - { - numChannels = fPlugin.getAudioPortCountWithGroupId(false, busId); - busType = V3_AUX; - flags = 0; - - for (uint32_t i=0; i(busId, info); #else - d_stdout("invalid bus %d", busId); + d_stdout("invalid output bus %d", busId); return V3_INVALID_ARG; #endif // DISTRHO_PLUGIN_NUM_OUTPUTS } - - std::memset(info, 0, sizeof(v3_bus_info)); - info->media_type = V3_AUDIO; - info->direction = busDirection; - info->channel_count = numChannels; - std::memcpy(info->bus_name, busName, sizeof(busName)); - info->bus_type = busType; - info->flags = flags; - return V3_OK; #else d_stdout("invalid bus, line %d", __LINE__); return V3_INVALID_ARG; @@ -1373,130 +1180,24 @@ public: DISTRHO_SAFE_ASSERT_RETURN(speaker != nullptr, V3_INVALID_ARG); #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - uint32_t busId = static_cast(busIndex); + const uint32_t busId = static_cast(busIndex); #endif if (busDirection == V3_INPUT) { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - for (uint32_t i=0; i(busId, speaker)) return V3_OK; - } - #endif // DISTRHO_PLUGIN_NUM_INPUTS + #endif d_stdout("invalid input bus arrangement %d", busIndex); return V3_INVALID_ARG; } else { #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - for (uint32_t i=0; i(busId, speaker)) return V3_OK; - } - #endif // DISTRHO_PLUGIN_NUM_OUTPUTS + #endif d_stdout("invalid output bus arrangement %d", busIndex); return V3_INVALID_ARG; } @@ -2589,13 +2290,13 @@ private: // VST3 stuff v3_component_handler** fComponentHandler; -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI #if DPF_VST3_USES_SEPARATE_CONTROLLER v3_connection_point** fConnectionFromCompToCtrl; #endif v3_connection_point** fConnectionFromCtrlToView; v3_host_application** const fHostApplication; -#endif + #endif // Temporary data const uint32_t fParameterCount; @@ -2603,35 +2304,35 @@ private: float* fCachedParameterValues; // basic offset + real float* fDummyAudioBuffer; bool* fParameterValuesChangedDuringProcessing; // basic offset + real -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI bool* fParameterValueChangesForUI; // basic offset + real bool fConnectedToUI; -#endif -#if DISTRHO_PLUGIN_WANT_LATENCY + #endif + #if DISTRHO_PLUGIN_WANT_LATENCY uint32_t fLastKnownLatency; -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT MidiEvent fMidiEvents[kMaxMidiEvents]; -# if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI SmallStackRingBuffer fNotesRingBuffer; -# endif -#endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + #endif + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT v3_event_list** fHostEventOutputHandle; -#endif -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS uint32_t fCurrentProgram; const uint32_t fProgramCountMinusOne; -#endif -#if DISTRHO_PLUGIN_WANT_STATE + #endif + #if DISTRHO_PLUGIN_WANT_STATE StringMap fStateMap; -#endif -#if DISTRHO_PLUGIN_WANT_TIMEPOS + #endif + #if DISTRHO_PLUGIN_WANT_TIMEPOS TimePosition fTimePosition; -#endif + #endif // ---------------------------------------------------------------------------------------------------------------- - // bus related helper functions called on constructor + // helper functions for dealing with buses #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 template @@ -2693,6 +2394,185 @@ private: } } } + + template + v3_result getAudioBusInfo(uint32_t busId, v3_bus_info* const info) const + { + constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + const BusInfo& bufInfo(isInput ? inputBuses : outputBuses); + + int32_t numChannels; + uint32_t flags; + v3_bus_types busType; + v3_str_128 busName = {}; + + if (busId < bufInfo.numGroups) + { + numChannels = fPlugin.getAudioPortCountWithGroupId(isInput, busId); + busType = V3_AUX; + flags = 0; + + for (uint32_t i=0; imedia_type = V3_AUDIO; + info->direction = isInput ? V3_INPUT : V3_OUTPUT; + info->channel_count = numChannels; + std::memcpy(info->bus_name, busName, sizeof(busName)); + info->bus_type = busType; + info->flags = flags; + return V3_OK; + } + + template + bool getAudioBusArrangement(uint32_t busId, v3_speaker_arrangement* const speaker) const + { + constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + const BusInfo& bufInfo(isInput ? inputBuses : outputBuses); + + for (uint32_t i=0; irequestParameterValueChange(index, value); } -#endif + #endif -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT bool writeMidi(const MidiEvent& midiEvent) { DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fHostEventOutputHandle != nullptr, false); @@ -2947,8 +2827,7 @@ private: { return ((PluginVst3*)ptr)->writeMidi(midiEvent); } -#endif - + #endif }; // -------------------------------------------------------------------------------------------------------------------- @@ -3254,41 +3133,41 @@ struct dpf_midi_mapping : v3_midi_mapping_cpp { struct dpf_edit_controller : v3_edit_controller_cpp { std::atomic_int refcounter; -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI ScopedPointer connectionCtrl2View; -#endif -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #endif + #if DPF_VST3_USES_SEPARATE_CONTROLLER ScopedPointer connectionComp2Ctrl; ScopedPointer vst3; -#else + #else ScopedPointer& vst3; bool initialized; -#endif + #endif // cached values v3_component_handler** handler; v3_host_application** const hostApplicationFromFactory; -#if !DPF_VST3_USES_SEPARATE_CONTROLLER + #if !DPF_VST3_USES_SEPARATE_CONTROLLER v3_host_application** const hostApplicationFromComponent; v3_host_application** hostApplicationFromComponentInitialize; -#endif + #endif v3_host_application** hostApplicationFromInitialize; -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller(v3_host_application** const hostApp) : refcounter(1), vst3(nullptr), -#else + #else dpf_edit_controller(ScopedPointer& v, v3_host_application** const hostApp, v3_host_application** const hostComp) : refcounter(1), vst3(v), initialized(false), -#endif + #endif handler(nullptr), hostApplicationFromFactory(hostApp), -#if !DPF_VST3_USES_SEPARATE_CONTROLLER + #if !DPF_VST3_USES_SEPARATE_CONTROLLER hostApplicationFromComponent(hostComp), hostApplicationFromComponentInitialize(nullptr), -#endif + #endif hostApplicationFromInitialize(nullptr) { d_stdout("dpf_edit_controller() with hostApplication %p", hostApplicationFromFactory); @@ -3296,10 +3175,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // make sure host application is valid through out this controller lifetime if (hostApplicationFromFactory != nullptr) v3_cpp_obj_ref(hostApplicationFromFactory); -#if !DPF_VST3_USES_SEPARATE_CONTROLLER + #if !DPF_VST3_USES_SEPARATE_CONTROLLER if (hostApplicationFromComponent != nullptr) v3_cpp_obj_ref(hostApplicationFromComponent); -#endif + #endif // v3_funknown, everything custom query_interface = query_interface_edit_controller; @@ -3329,18 +3208,18 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ~dpf_edit_controller() { d_stdout("~dpf_edit_controller()"); -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI connectionCtrl2View = nullptr; -#endif -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #endif + #if DPF_VST3_USES_SEPARATE_CONTROLLER connectionComp2Ctrl = nullptr; vst3 = nullptr; -#endif + #endif -#if !DPF_VST3_USES_SEPARATE_CONTROLLER + #if !DPF_VST3_USES_SEPARATE_CONTROLLER if (hostApplicationFromComponent != nullptr) v3_cpp_obj_unref(hostApplicationFromComponent); -#endif + #endif if (hostApplicationFromFactory != nullptr) v3_cpp_obj_unref(hostApplicationFromFactory); } @@ -3364,22 +3243,22 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (v3_tuid_match(iid, v3_midi_mapping_iid)) { -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT d_stdout("query_interface_edit_controller => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_midi_mapping midi_mapping; static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; *iface = &midi_mapping_ptr; return V3_OK; -#else + #else d_stdout("query_interface_edit_controller => %p %s %p | reject unused", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; -#endif + #endif } if (v3_tuid_match(iid, v3_connection_point_iid)) { -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER d_stdout("query_interface_edit_controller => %p %s %p | OK convert %p", self, tuid2str(iid), iface, controller->connectionComp2Ctrl.get()); @@ -3389,11 +3268,11 @@ struct dpf_edit_controller : v3_edit_controller_cpp { ++controller->connectionComp2Ctrl->refcounter; *iface = &controller->connectionComp2Ctrl; return V3_OK; -#else + #else d_stdout("query_interface_edit_controller => %p %s %p | reject unwanted", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; -#endif + #endif } d_stdout("query_interface_edit_controller => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); @@ -3420,7 +3299,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { return refcount; } -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER /** * Some hosts will have unclean instances of a few of the controller child classes at this point. * We check for those here, going through the whole possible chain to see if it is safe to delete. @@ -3445,9 +3324,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { delete controller; delete controllerptr; -#else + #else d_stdout("dpf_edit_controller::unref => %p | refcount is zero, deletion will be done by component later", self); -#endif + #endif return 0; } @@ -3459,11 +3338,11 @@ struct dpf_edit_controller : v3_edit_controller_cpp { dpf_edit_controller* const controller = *static_cast(self); // check if already initialized -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 == nullptr, V3_INVALID_ARG); -#else + #else DISTRHO_SAFE_ASSERT_RETURN(! controller->initialized, V3_INVALID_ARG); -#endif + #endif // query for host application v3_host_application** hostApplication = nullptr; @@ -3475,7 +3354,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // save it for later so we can unref it controller->hostApplicationFromInitialize = hostApplication; -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER // provide the factory application to the plugin if this new one is missing if (hostApplication == nullptr) hostApplication = controller->hostApplicationFromFactory; @@ -3497,10 +3376,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { if (point->other != nullptr) controller->vst3->comp2ctrl_connect(point->other); } -#else + #else // mark as initialized controller->initialized = true; -#endif + #endif return V3_OK; } @@ -3510,19 +3389,19 @@ struct dpf_edit_controller : v3_edit_controller_cpp { d_stdout("dpf_edit_controller::terminate => %p", self); dpf_edit_controller* const controller = *static_cast(self); -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER // check if already terminated DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_INVALID_ARG); // delete actual plugin controller->vst3 = nullptr; -#else + #else // check if already terminated DISTRHO_SAFE_ASSERT_RETURN(controller->initialized, V3_INVALID_ARG); // mark as uninitialzed controller->initialized = false; -#endif + #endif // unref host application received during initialize if (controller->hostApplicationFromInitialize != nullptr) @@ -3541,30 +3420,30 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream); -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); return vst3->setState(stream); -#else + #else return V3_OK; // unused (void)self; (void)stream; -#endif + #endif } static v3_result V3_API set_state(void* const self, v3_bstream** const stream) { d_stdout("dpf_edit_controller::set_state => %p %p", self, stream); -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED); -#endif + #endif return V3_NOT_IMPLEMENTED; @@ -3577,10 +3456,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { { d_stdout("dpf_edit_controller::get_state => %p %p", self, stream); -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER dpf_edit_controller* const controller = *static_cast(self); DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED); -#endif + #endif return V3_NOT_IMPLEMENTED; @@ -3698,7 +3577,7 @@ struct dpf_edit_controller : v3_edit_controller_cpp { d_stdout("create_view has contexts %p %p", controller->hostApplicationFromFactory, controller->hostApplicationFromInitialize); -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI // plugin must be initialized PluginVst3* const vst3 = controller->vst3; DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr); @@ -3711,12 +3590,12 @@ struct dpf_edit_controller : v3_edit_controller_cpp { // we require a host application for message creation v3_host_application** const host = controller->hostApplicationFromInitialize != nullptr ? controller->hostApplicationFromInitialize -#if !DPF_VST3_USES_SEPARATE_CONTROLLER + #if !DPF_VST3_USES_SEPARATE_CONTROLLER : controller->hostApplicationFromComponent != nullptr ? controller->hostApplicationFromComponent : controller->hostApplicationFromComponentInitialize != nullptr ? controller->hostApplicationFromComponentInitialize -#endif + #endif : controller->hostApplicationFromFactory; DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr); @@ -3742,9 +3621,9 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } return view; -#else + #else return nullptr; -#endif + #endif } }; @@ -3787,16 +3666,16 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp { static uint32_t V3_API get_process_context_requirements(void*) { -#if DISTRHO_PLUGIN_WANT_TIMEPOS + #if DISTRHO_PLUGIN_WANT_TIMEPOS return 0x0 | V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID | V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID | V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID | V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID | V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING -#else + #else return 0x0; -#endif + #endif } DISTRHO_PREVENT_HEAP_ALLOCATION @@ -3867,9 +3746,9 @@ struct dpf_audio_processor : v3_audio_processor_cpp { v3_speaker_arrangement* const inputs, const int32_t num_inputs, v3_speaker_arrangement* const outputs, const int32_t num_outputs) { - // NOTE this is called a bunch of times - // d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", - // self, inputs, num_inputs, outputs, num_outputs); + // NOTE this is called a bunch of times in JUCE hosts + d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i", + self, inputs, num_inputs, outputs, num_outputs); dpf_audio_processor* const processor = *static_cast(self); PluginVst3* const vst3 = processor->vst3; @@ -3965,11 +3844,11 @@ struct dpf_audio_processor : v3_audio_processor_cpp { struct dpf_component : v3_component_cpp { std::atomic_int refcounter; ScopedPointer processor; -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER ScopedPointer connectionComp2Ctrl; -#else + #else ScopedPointer controller; -#endif + #endif ScopedPointer vst3; v3_host_application** const hostApplicationFromFactory; v3_host_application** hostApplicationFromInitialize; @@ -4010,11 +3889,11 @@ struct dpf_component : v3_component_cpp { { d_stdout("~dpf_component()"); processor = nullptr; -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER connectionComp2Ctrl = nullptr; -#else + #else controller = nullptr; -#endif + #endif vst3 = nullptr; if (hostApplicationFromFactory != nullptr) @@ -4040,17 +3919,17 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_midi_mapping_iid)) { -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT d_stdout("query_interface_component => %p %s %p | OK convert static", self, tuid2str(iid), iface); static dpf_midi_mapping midi_mapping; static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping; *iface = &midi_mapping_ptr; return V3_OK; -#else + #else d_stdout("query_interface_component => %p %s %p | reject unused", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; -#endif + #endif } if (v3_tuid_match(iid, v3_audio_processor_iid)) @@ -4068,7 +3947,7 @@ struct dpf_component : v3_component_cpp { if (v3_tuid_match(iid, v3_connection_point_iid)) { -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER d_stdout("query_interface_component => %p %s %p | OK convert %p", self, tuid2str(iid), iface, component->connectionComp2Ctrl.get()); @@ -4078,16 +3957,16 @@ struct dpf_component : v3_component_cpp { ++component->connectionComp2Ctrl->refcounter; *iface = &component->connectionComp2Ctrl; return V3_OK; -#else + #else d_stdout("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; -#endif + #endif } if (v3_tuid_match(iid, v3_edit_controller_iid)) { -#if !DPF_VST3_USES_SEPARATE_CONTROLLER + #if !DPF_VST3_USES_SEPARATE_CONTROLLER d_stdout("query_interface_component => %p %s %p | OK convert %p", self, tuid2str(iid), iface, component->controller.get()); @@ -4099,11 +3978,11 @@ struct dpf_component : v3_component_cpp { ++component->controller->refcounter; *iface = &component->controller; return V3_OK; -#else + #else d_stdout("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface); *iface = nullptr; return V3_NO_INTERFACE; -#endif + #endif } d_stdout("query_interface_component => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); @@ -4147,7 +4026,7 @@ struct dpf_component : v3_component_cpp { } } -#if DPF_VST3_USES_SEPARATE_CONTROLLER + #if DPF_VST3_USES_SEPARATE_CONTROLLER if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl) { if (const int refcount = point->refcounter) @@ -4156,7 +4035,7 @@ struct dpf_component : v3_component_cpp { d_stderr("DPF warning: asked to delete component while connection point still active (refcount %d)", refcount); } } -#else + #else if (dpf_edit_controller* const controller = component->controller) { if (const int refcount = controller->refcounter) @@ -4165,7 +4044,7 @@ struct dpf_component : v3_component_cpp { d_stderr("DPF warning: asked to delete component while edit controller still active (refcount %d)", refcount); } } -#endif + #endif if (unclean) return handleUncleanComponent(componentptr); From 8368f132201f57487987b9408aabeee2591a3fe9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 7 Aug 2022 03:56:32 +0100 Subject: [PATCH 500/504] Start dealing with VST3 buses --- distrho/src/DistrhoPluginVST3.cpp | 250 +++++++++++++++++++++++------- 1 file changed, 195 insertions(+), 55 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 62bc6ed2..a4b02bbf 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -264,11 +264,10 @@ class PluginVst3 { /* Buses: count possible buses we can provide to the host, in case they are not yet defined by the developer. * These values are only used if port groups aren't set. - * We count the number of used port groups in `numGroups`. * * When port groups are not in use, we fill in appropriately. - * 1 bus is provided for the main audio (if there is any) plus 1 for each sidechain or cv port. - * Main audio comes first, if available. + * 1 bus is provided for the main audio (if there is any) plus 1 for sidechain and 1 for each cv port. + * Main audio is used as first bus, if available. * Then sidechain, also if available. * And finally each CV port individually. * @@ -277,18 +276,20 @@ class PluginVst3 struct BusInfo { uint8_t audio; // either 0 or 1 uint8_t sidechain; // either 0 or 1 - uint32_t numMainAudio; - uint32_t numSidechain; - uint32_t numCV; - uint32_t numGroups; + uint32_t groups; + uint32_t audioPorts; + uint32_t sidechainPorts; + uint32_t groupPorts; + uint32_t cvPorts; BusInfo() : audio(0), sidechain(0), - numMainAudio(0), - numSidechain(0), - numCV(0), - numGroups(0) {} + groups(0), + audioPorts(0), + sidechainPorts(0), + groupPorts(0), + cvPorts(0) {} } inputBuses, outputBuses; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -608,9 +609,11 @@ public: { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 fillInBusInfoDetails(); + std::memset(fEnabledInputs, 0, sizeof(fEnabledInputs)); #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 fillInBusInfoDetails(); + std::memset(fEnabledOutputs, 0, sizeof(fEnabledOutputs)); #endif if (const uint32_t extraParameterCount = fParameterCount + kVst3InternalParameterBaseCount) @@ -731,9 +734,9 @@ public: { case V3_AUDIO: if (busDirection == V3_INPUT) - return inputBuses.audio + inputBuses.sidechain + inputBuses.numCV + inputBuses.numGroups; + return inputBuses.audio + inputBuses.sidechain + inputBuses.groups + inputBuses.cvPorts; if (busDirection == V3_OUTPUT) - return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV + outputBuses.numGroups; + return outputBuses.audio + outputBuses.sidechain + outputBuses.groups + outputBuses.cvPorts; break; case V3_EVENT: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -1166,11 +1169,30 @@ public: // ---------------------------------------------------------------------------------------------------------------- // v3_audio_processor interface calls - v3_result setBusArrangements(v3_speaker_arrangement* /*inputs*/, const int32_t /*numInputs*/, - v3_speaker_arrangement* /*outputs*/, const int32_t /*numOutputs*/) + v3_result setBusArrangements(v3_speaker_arrangement* const inputs, const int32_t numInputs, + v3_speaker_arrangement* const outputs, const int32_t numOutputs) { - // TODO - return V3_NOT_IMPLEMENTED; + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(numInputs >= 0, V3_INVALID_ARG); + if (!setAudioBusArrangement(inputs, static_cast(numInputs))) + return V3_INTERNAL_ERR; + #else + DISTRHO_SAFE_ASSERT_RETURN(numInputs == 0, V3_INVALID_ARG); + // unused + (void)inputs; + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + DISTRHO_SAFE_ASSERT_RETURN(numOutputs >= 0, V3_INVALID_ARG); + if (!setAudioBusArrangement(outputs, static_cast(numOutputs))) + return V3_INTERNAL_ERR; + #else + DISTRHO_SAFE_ASSERT_RETURN(numOutputs == 0, V3_INVALID_ARG); + // unused + (void)outputs; + #endif + + return V3_OK; } v3_result getBusArrangement(const int32_t busDirection, const int32_t busIndex, v3_speaker_arrangement* const speaker) const noexcept @@ -1268,7 +1290,7 @@ public: if (! fPlugin.isActive()) fPlugin.activate(); -#if DISTRHO_PLUGIN_WANT_TIMEPOS + #if DISTRHO_PLUGIN_WANT_TIMEPOS if (v3_process_context* const ctx = data->ctx) { fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING; @@ -1323,7 +1345,7 @@ public: fPlugin.setTimePosition(fTimePosition); } -#endif + #endif if (data->nframes <= 0) { @@ -2304,6 +2326,12 @@ private: float* fCachedParameterValues; // basic offset + real float* fDummyAudioBuffer; bool* fParameterValuesChangedDuringProcessing; // basic offset + real + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + bool fEnabledInputs[DISTRHO_PLUGIN_NUM_INPUTS]; + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + bool fEnabledOutputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + #endif #if DISTRHO_PLUGIN_HAS_UI bool* fParameterValueChangesForUI; // basic offset + real bool fConnectedToUI; @@ -2339,12 +2367,23 @@ private: void fillInBusInfoDetails() { constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; - BusInfo& bufInfo(isInput ? inputBuses : outputBuses); + BusInfo& busInfo(isInput ? inputBuses : outputBuses); + bool* const enabledPorts = isInput + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + ? fEnabledInputs + #else + ? nullptr + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + : fEnabledOutputs; + #else + : nullptr; + #endif std::vector visitedPortGroups; for (uint32_t i=0; imedia_type = V3_AUDIO; info->direction = isInput ? V3_INPUT : V3_OUTPUT; @@ -2513,18 +2565,36 @@ private: bool getAudioBusArrangement(uint32_t busId, v3_speaker_arrangement* const speaker) const { constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; - const BusInfo& bufInfo(isInput ? inputBuses : outputBuses); + const BusInfo& busInfo(isInput ? inputBuses : outputBuses); + const bool* const enabledPorts = isInput + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + ? fEnabledInputs + #else + ? nullptr + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + : fEnabledOutputs; + #else + : nullptr; + #endif for (uint32_t i=0; i + bool setAudioBusArrangement(v3_speaker_arrangement* const speakers, const uint32_t numBuses) + { + constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + BusInfo& busInfo(isInput ? inputBuses : outputBuses); + bool* const enabledPorts = isInput + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + ? fEnabledInputs + #else + ? nullptr + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + : fEnabledOutputs; + #else + : nullptr; + #endif + + for (uint32_t busId=0; busId Date: Sun, 7 Aug 2022 05:20:26 +0100 Subject: [PATCH 501/504] Correct last commit, deal with a few vst3 edge cases --- Makefile.base.mk | 4 +++ distrho/src/DistrhoPluginVST3.cpp | 43 +++++++++++++++++++------------ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index 3169518a..a54ac40c 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -352,8 +352,12 @@ endif # backwards compat, always available/enabled ifneq ($(FORCE_NATIVE_AUDIO_FALLBACK),true) +ifeq ($(STATIC_BUILD),true) +HAVE_JACK = $(shell $(PKG_CONFIG) --exists jack && echo true) +else HAVE_JACK = true endif +endif # --------------------------------------------------------------------------------------------------------------------- # Set Generic DGL stuff diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index a4b02bbf..78c35cd9 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -2537,13 +2537,8 @@ private: if (port.busId == busId) { - const PortGroupWithId& group(fPlugin.getPortGroupById(port.groupId)); - - if (group.name.isNotEmpty()) - strncpy_utf16(busName, group.name, 128); - else - strncpy_utf16(busName, port.name, 128); - + const String& groupName(busInfo.groups ? fPlugin.getPortGroupById(port.groupId).name : port.name); + strncpy_utf16(busName, groupName, 128); break; } } @@ -2674,23 +2669,33 @@ private: if (port.busId != busId) { - d_stdout("setAudioBusArrangement port.busId != busId: %d %d", port.busId, busId); + // d_stdout("setAudioBusArrangement port.busId != busId: %d %d", port.busId, busId); continue; } - // special case for turning mono into "stereo" - /* if (arr == (V3_SPEAKER_L|V3_SPEAKER_R)) { + // some hosts try to make CV ports stereo, that doesn't make any sense! + if (port.hints & kAudioPortIsCV) + return false; + + // force stereo mode + if (busId == 0 && busInfo.audioPorts != 0) + { + busInfo.audioPorts = 2; + enabledPorts[i] = i < 2; + } + + /* + // special case for turning mono into "stereo" if (port.groupId == kPortGroupMono) port.groupId = kPortGroupStereo; else if (busId == 0 && busInfo.audioPorts == 1) busInfo.audioPorts = 2; + */ } - */ enabledPorts[i] = arr != 0; - break; } } @@ -4801,11 +4806,17 @@ bool ENTRYFNNAME(ENTRYFNNAMEARGS) String tmpPath(getBinaryFilename()); tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); - DISTRHO_SAFE_ASSERT_RETURN(tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents"), true); - tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); - bundlePath = tmpPath; - d_nextBundlePath = bundlePath.buffer(); + if (tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents")) + { + tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); + bundlePath = tmpPath; + d_nextBundlePath = bundlePath.buffer(); + } + else + { + bundlePath = "error"; + } } // init dummy plugin and set uniqueId From f1925ebcc22a412ac2ae5c5b5d2c50851052654d Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 7 Aug 2022 20:07:17 +0100 Subject: [PATCH 502/504] Make wasm real clipboard use optional, disabled by default --- Makefile.base.mk | 7 +++++-- dgl/src/pugl-upstream | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile.base.mk b/Makefile.base.mk index a54ac40c..87ae06bd 100644 --- a/Makefile.base.mk +++ b/Makefile.base.mk @@ -199,8 +199,6 @@ LINK_OPTS = -fdata-sections -ffunction-sections -Wl,-O1,--gc-sections ifeq ($(WASM),true) LINK_OPTS += -O3 LINK_OPTS += -sAGGRESSIVE_VARIABLE_ELIMINATION=1 -LINK_OPTS += -sASYNCIFY -LINK_OPTS += -sASYNCIFY_IMPORTS=puglGetAsyncClipboardData else LINK_OPTS += -Wl,--as-needed ifneq ($(SKIP_STRIPPING),true) @@ -272,6 +270,11 @@ ifeq ($(MACOS_OLD),true) BUILD_CXX_FLAGS = $(BASE_FLAGS) $(CXXFLAGS) -DHAVE_CPP11_SUPPORT=0 endif +ifeq ($(WASM_CLIPBOARD),true) +BUILD_CXX_FLAGS += -DPUGL_WASM_ASYNC_CLIPBOARD +LINK_FLAGS += -sASYNCIFY -sASYNCIFY_IMPORTS=puglGetAsyncClipboardData +endif + ifeq ($(WASM_EXCEPTIONS),true) BUILD_CXX_FLAGS += -fexceptions LINK_FLAGS += -fexceptions diff --git a/dgl/src/pugl-upstream b/dgl/src/pugl-upstream index 0bf91c2c..09afe84f 160000 --- a/dgl/src/pugl-upstream +++ b/dgl/src/pugl-upstream @@ -1 +1 @@ -Subproject commit 0bf91c2c4c14db135386635e160304a4e085e55e +Subproject commit 09afe84fcaa67adba7a168b8490046dab6ecf67d From 03700cbd13e56647d1ba45e4eca4cf3f18c64520 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Aug 2022 21:25:06 +0100 Subject: [PATCH 503/504] Finalize details on VST3 bus/speaker handling --- distrho/src/DistrhoPluginVST3.cpp | 238 ++++++++++++++++++------------ distrho/src/travesty/component.h | 14 +- 2 files changed, 156 insertions(+), 96 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 78c35cd9..fe9645d3 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -51,8 +51,6 @@ * - MIDI program changes * - MIDI sysex * == BUSES - * - bus arrangements - * - optional audio buses * - routing info, do we care? * == CV * - cv scaling to -1/+1 @@ -265,8 +263,11 @@ class PluginVst3 /* Buses: count possible buses we can provide to the host, in case they are not yet defined by the developer. * These values are only used if port groups aren't set. * - * When port groups are not in use, we fill in appropriately. - * 1 bus is provided for the main audio (if there is any) plus 1 for sidechain and 1 for each cv port. + * When port groups are not in use: + * - 1 bus is provided for the main audio (if there is any) + * - 1 for sidechain + * - 1 for each cv port + * So basically: * Main audio is used as first bus, if available. * Then sidechain, also if available. * And finally each CV port individually. @@ -608,12 +609,12 @@ public: #endif { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - fillInBusInfoDetails(); std::memset(fEnabledInputs, 0, sizeof(fEnabledInputs)); + fillInBusInfoDetails(); #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - fillInBusInfoDetails(); std::memset(fEnabledOutputs, 0, sizeof(fEnabledOutputs)); + fillInBusInfoDetails(); #endif if (const uint32_t extraParameterCount = fParameterCount + kVst3InternalParameterBaseCount) @@ -706,9 +707,9 @@ public: } fCachedParameterValues[kVst3InternalParameterBaseCount + index] = value; -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI fParameterValueChangesForUI[kVst3InternalParameterBaseCount + index] = true; -#endif + #endif fPlugin.setParameterValue(index, value); } @@ -825,16 +826,56 @@ public: v3_result getRoutingInfo(v3_routing_info*, v3_routing_info*) { - // TODO + /* + output->media_type = V3_AUDIO; + output->bus_idx = 0; + output->channel = -1; + d_stdout("getRoutingInfo %s %d %d", + v3_media_type_str(input->media_type), input->bus_idx, input->channel); + */ return V3_NOT_IMPLEMENTED; } - v3_result activateBus(const int32_t /* mediaType */, - const int32_t /* busDirection */, - const int32_t /* busIndex */, - const bool /* state */) + v3_result activateBus(const int32_t mediaType, + const int32_t busDirection, + const int32_t busIndex, + const bool state) noexcept { - // TODO, returning ok to make bitwig happy + DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG); + DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG); + + if (mediaType == V3_AUDIO) + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + const uint32_t busId = static_cast(busIndex); + + if (busDirection == V3_INPUT) + { + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + for (uint32_t i=0; i 0 + for (uint32_t i=0; i 0 if (data->inputs != nullptr) { - for (; i < data->inputs->num_channels; ++i) + for (int32_t j = 0; j < data->inputs->num_channels; ++j) { + while (!fEnabledInputs[i] && i < DISTRHO_PLUGIN_NUM_INPUTS) + inputs[i++] = fDummyAudioBuffer; + DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_INPUTS, i); - inputs[i] = data->inputs->channel_buffers_32[i]; + inputs[i++] = data->inputs->channel_buffers_32[j]; } } + #endif for (; i < std::max(1, DISTRHO_PLUGIN_NUM_INPUTS); ++i) inputs[i] = fDummyAudioBuffer; } { int32_t i = 0; + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 if (data->outputs != nullptr) { - for (; i < data->outputs->num_channels; ++i) + for (int32_t j = 0; j < data->outputs->num_channels; ++j) { + while (!fEnabledOutputs[i] && i < DISTRHO_PLUGIN_NUM_OUTPUTS) + outputs[i++] = fDummyAudioBuffer; + DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_OUTPUTS, i); - outputs[i] = data->outputs->channel_buffers_32[i]; + outputs[i++] = data->outputs->channel_buffers_32[j]; } } + #endif for (; i < std::max(1, DISTRHO_PLUGIN_NUM_OUTPUTS); ++i) outputs[i] = fDummyAudioBuffer; } @@ -2387,8 +2438,8 @@ private: if (port.groupId != kPortGroupNone) { - const std::vector::iterator end = visitedPortGroups.end(); - if (std::find(visitedPortGroups.begin(), end, port.groupId) == end) + const std::vector::const_iterator end = visitedPortGroups.cend(); + if (std::find(visitedPortGroups.cbegin(), end, port.groupId) == end) { visitedPortGroups.push_back(port.groupId); ++busInfo.groups; @@ -2410,15 +2461,17 @@ private: if (busInfo.sidechainPorts != 0) busInfo.sidechain = 1; - uint32_t busIdFromGroup = 0; uint32_t busIdForCV = 0; + const std::vector::const_iterator vpgStart = visitedPortGroups.cbegin(); + const std::vector::const_iterator vpgEnd = visitedPortGroups.cend(); + for (uint32_t i=0; imedia_type = V3_AUDIO; info->direction = isInput ? V3_INPUT : V3_OUTPUT; @@ -2556,6 +2614,50 @@ private: return V3_OK; } + template + v3_speaker_arrangement getSpeakerArrangementForAudioPort(const BusInfo& busInfo, const uint32_t portGroupId, uint32_t busId) const noexcept + { + switch (portGroupId) + { + case kPortGroupMono: + return V3_SPEAKER_M; + case kPortGroupStereo: + return V3_SPEAKER_L | V3_SPEAKER_R; + } + + v3_speaker_arrangement arr = 0x0; + + if (busId < busInfo.groups) + { + const uint32_t numPortsInBus = fPlugin.getAudioPortCountWithGroupId(isInput, busId); + for (uint32_t j=0; j bool getAudioBusArrangement(uint32_t busId, v3_speaker_arrangement* const speaker) const { @@ -2586,54 +2688,12 @@ private: if (!enabledPorts[i]) { *speaker = 0; - // d_stdout("getAudioBusArrangement %d %lx", busId, 0); + // d_stdout("getAudioBusArrangement %d %d not enabled", i, busId); return true; } - v3_speaker_arrangement arr; - - switch (port.groupId) - { - case kPortGroupMono: - arr = V3_SPEAKER_M; - break; - case kPortGroupStereo: - arr = V3_SPEAKER_L | V3_SPEAKER_R; - break; - default: - if (busId < busInfo.groups) - { - const uint32_t numPortsInBus = fPlugin.getAudioPortCountWithGroupId(isInput, busId); - arr = 0x0; - for (uint32_t j=0; j(busInfo, port.groupId, busId); + // d_stdout("getAudioBusArrangement %d enabled by value %lx", busId, *speaker); return true; } @@ -2657,6 +2717,8 @@ private: : nullptr; #endif + bool ok = true; + for (uint32_t busId=0; busId(busInfo, port.groupId, busId); - /* - // special case for turning mono into "stereo" - if (port.groupId == kPortGroupMono) - port.groupId = kPortGroupStereo; - else if (busId == 0 && busInfo.audioPorts == 1) - busInfo.audioPorts = 2; - */ + // fail if host tries to map it to anything else + if (earr != arr && arr != 0) + { + ok = false; + continue; } enabledPorts[i] = arr != 0; @@ -2716,7 +2766,7 @@ private: } } - return true; + return ok; } #endif @@ -4594,7 +4644,7 @@ struct dpf_factory : v3_plugin_factory_cpp { d_stdout("dpf_factory::get_factory_info => %p", info); std::memset(info, 0, sizeof(*info)); - info->flags = 0x10; + info->flags = 0x10; // unicode DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->url, getPluginInfo().getHomePage(), ARRAY_SIZE(info->url)); // DISTRHO_NAMESPACE::strncpy(info->email, "", ARRAY_SIZE(info->email)); // TODO diff --git a/distrho/src/travesty/component.h b/distrho/src/travesty/component.h index 652da36e..6c7b59e2 100644 --- a/distrho/src/travesty/component.h +++ b/distrho/src/travesty/component.h @@ -73,6 +73,12 @@ enum v3_bus_flags { V3_IS_CONTROL_VOLTAGE = 1 << 1 }; +enum v3_io_mode { + V3_SIMPLE = 0, + V3_ADVANCED, + V3_OFFLINE_PROCESSING +}; + struct v3_bus_info { int32_t media_type; int32_t direction; @@ -82,12 +88,16 @@ struct v3_bus_info { uint32_t flags; }; +struct v3_routing_info { + int32_t media_type; + int32_t bus_idx; + int32_t channel; +}; + /** * component */ -struct v3_routing_info; - struct v3_component { #ifndef __cplusplus struct v3_plugin_base; From 3df2b31e681b43f5baeca7ed37f161d0ad9845de Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 8 Aug 2022 21:31:11 +0100 Subject: [PATCH 504/504] Fix a warning --- distrho/src/DistrhoPluginVST3.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index fe9645d3..43694638 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -846,7 +846,7 @@ public: if (mediaType == V3_AUDIO) { - #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 const uint32_t busId = static_cast(busIndex); if (busDirection == V3_INPUT) @@ -877,6 +877,11 @@ public: } return V3_OK; + + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0 + // unused + (void)state; + #endif } v3_result setActive(const bool active) @@ -2438,8 +2443,8 @@ private: if (port.groupId != kPortGroupNone) { - const std::vector::const_iterator end = visitedPortGroups.cend(); - if (std::find(visitedPortGroups.cbegin(), end, port.groupId) == end) + const std::vector::iterator end = visitedPortGroups.end(); + if (std::find(visitedPortGroups.begin(), end, port.groupId) == end) { visitedPortGroups.push_back(port.groupId); ++busInfo.groups; @@ -2462,8 +2467,8 @@ private: busInfo.sidechain = 1; uint32_t busIdForCV = 0; - const std::vector::const_iterator vpgStart = visitedPortGroups.cbegin(); - const std::vector::const_iterator vpgEnd = visitedPortGroups.cend(); + const std::vector::iterator vpgStart = visitedPortGroups.begin(); + const std::vector::iterator vpgEnd = visitedPortGroups.end(); for (uint32_t i=0; i