Browse Source

Update everything

tags/v1.5
falkTX 3 years ago
parent
commit
5130b6201d
64 changed files with 14488 additions and 1041 deletions
  1. +189
    -0
      .github/workflows/build.yml
  2. +0
    -1
      dpf/LICENSE
  3. +27
    -3
      dpf/Makefile.base.mk
  4. +32
    -16
      dpf/Makefile.plugins.mk
  5. +9
    -0
      dpf/cmake/DPF-plugin.cmake
  6. +11
    -1
      dpf/dgl/Application.hpp
  7. +5
    -0
      dpf/dgl/Color.hpp
  8. +154
    -0
      dpf/dgl/EventHandlers.hpp
  9. +11
    -22
      dpf/dgl/ImageBaseWidgets.hpp
  10. +1
    -1
      dpf/dgl/Makefile
  11. +16
    -0
      dpf/dgl/SubWidget.hpp
  12. +9
    -9
      dpf/dgl/Widget.hpp
  13. +40
    -16
      dpf/dgl/Window.hpp
  14. +5
    -0
      dpf/dgl/src/Application.cpp
  15. +3
    -3
      dpf/dgl/src/ApplicationPrivateData.cpp
  16. +3
    -3
      dpf/dgl/src/ApplicationPrivateData.hpp
  17. +1
    -3
      dpf/dgl/src/Cairo.cpp
  18. +7
    -0
      dpf/dgl/src/Color.cpp
  19. +0
    -188
      dpf/dgl/src/Common.hpp
  20. +632
    -0
      dpf/dgl/src/EventHandlers.cpp
  21. +163
    -311
      dpf/dgl/src/ImageBaseWidgets.cpp
  22. +4
    -1
      dpf/dgl/src/NanoVG.cpp
  23. +3
    -3
      dpf/dgl/src/OpenGL.cpp
  24. +20
    -5
      dpf/dgl/src/SubWidget.cpp
  25. +1
    -0
      dpf/dgl/src/SubWidgetPrivateData.cpp
  26. +1
    -0
      dpf/dgl/src/SubWidgetPrivateData.hpp
  27. +6
    -6
      dpf/dgl/src/WidgetPrivateData.cpp
  28. +14
    -5
      dpf/dgl/src/Window.cpp
  29. +11
    -13
      dpf/dgl/src/WindowPrivateData.cpp
  30. +3
    -3
      dpf/dgl/src/WindowPrivateData.hpp
  31. +11
    -10
      dpf/dgl/src/pugl-upstream/src/win.c
  32. +9
    -1
      dpf/dgl/src/pugl.cpp
  33. +4
    -0
      dpf/dgl/src/pugl.hpp
  34. +3
    -3
      dpf/distrho/DistrhoPlugin.hpp
  35. +5
    -1
      dpf/distrho/DistrhoUI.hpp
  36. +3
    -0
      dpf/distrho/extra/LibraryUtils.hpp
  37. +3
    -0
      dpf/distrho/extra/Mutex.hpp
  38. +3
    -0
      dpf/distrho/extra/Sleep.hpp
  39. +3
    -2
      dpf/distrho/src/DistrhoPluginInternal.hpp
  40. +16
    -0
      dpf/distrho/src/DistrhoPluginJACK.cpp
  41. +2
    -2
      dpf/distrho/src/DistrhoPluginLV2.cpp
  42. +60
    -8
      dpf/distrho/src/DistrhoPluginLV2export.cpp
  43. +36
    -4
      dpf/distrho/src/DistrhoPluginVST2.cpp
  44. +2
    -2
      dpf/distrho/src/DistrhoPluginVST3.cpp
  45. +1
    -1
      dpf/distrho/src/DistrhoUI.cpp
  46. +16
    -11
      dpf/distrho/src/DistrhoUIInternal.hpp
  47. +17
    -3
      dpf/distrho/src/DistrhoUILV2.cpp
  48. +150
    -112
      dpf/distrho/src/DistrhoUIPrivateData.hpp
  49. +294
    -153
      dpf/distrho/src/jackbridge/JackBridge.cpp
  50. +6
    -2
      dpf/distrho/src/jackbridge/JackBridge.hpp
  51. +216
    -0
      dpf/distrho/src/jackbridge/RtAudioBridge.hpp
  52. +27
    -0
      dpf/distrho/src/jackbridge/rtaudio/LICENSE
  53. +10911
    -0
      dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp
  54. +1185
    -0
      dpf/distrho/src/jackbridge/rtaudio/RtAudio.h
  55. +42
    -40
      dpf/tests/Demo.cpp
  56. +0
    -13
      dpf/utils/README-DPF-Windows.txt
  57. +1
    -1
      dpf/utils/generate-ttl.sh
  58. +0
    -54
      dpf/utils/pack-win-dlls.sh
  59. +51
    -0
      dpf/utils/package-osx-bundles.sh
  60. +18
    -0
      dpf/utils/plugin.pkg/package.xml.in
  61. +3
    -0
      dpf/utils/plugin.pkg/welcome.txt.in
  62. +1
    -1
      dpf/utils/plugin.vst/Contents/Info.plist
  63. +1
    -2
      plugins/Kars/LICENSE
  64. +7
    -2
      plugins/MVerb/DistrhoUIMVerb.cpp

+ 189
- 0
.github/workflows/build.yml View File

@@ -0,0 +1,189 @@
name: build

on:
push:
branches:
- '*'
pull_request:
branches:
- '*'
env:
DEBIAN_FRONTEND: noninteractive

jobs:
linux-arm64:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up dependencies
run: |
sudo dpkg --add-architecture arm64 && \
sudo sed -i "s/deb http/deb [arch=amd64] http/" /etc/apt/sources.list && \
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports bionic main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/ports-arm64.list && \
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 qemu-user-static
- name: Build linux arm64 cross-compiled
env:
CC: aarch64-linux-gnu-gcc
CXX: aarch64-linux-gnu-g++
LDFLAGS: -static-libgcc -static-libstdc++
run: |
make -j $(nproc)
- name: Set sha8
id: slug
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)"
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-linux-arm64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
bin/*

linux-armhf:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up dependencies
run: |
sudo dpkg --add-architecture armhf && \
sudo sed -i "s/deb http/deb [arch=amd64] http/" /etc/apt/sources.list && \
echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports bionic main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/ports-armhf.list && \
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 qemu-user-static
- name: Build linux armhf cross-compiled
env:
CC: arm-linux-gnueabihf-gcc
CXX: arm-linux-gnueabihf-g++
LDFLAGS: -static-libgcc -static-libstdc++
run: |
make -j $(nproc)
- name: Set sha8
id: slug
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)"
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-linux-armhf-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
bin/*

linux-x64:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up dependencies
run: |
sudo apt-get install -yq libasound2-dev libgl1-mesa-dev liblo-dev libpulse-dev
- name: Build linux x64
env:
LDFLAGS: -static-libgcc -static-libstdc++
run: |
make -j $(nproc)
- name: Set sha8
id: slug
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)"
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-linux-x64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
bin/*

macos-universal:
runs-on: macos-10.15
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
CXXFLAGS: -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
LDFLAGS: -arch x86_64 -arch arm64 -mmacosx-version-min=10.12
run: |
make NOOPT=true -j $(sysctl -n hw.logicalcpu) && \
./dpf/utils/package-osx-bundles.sh
- name: Set sha8
id: slug
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)"
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-macOS-universal-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
*-macOS.pkg
bin/*
!bin/*-ladspa.dylib
!bin/*-dssi.dylib
!bin/lv2
!bin/vst2

win32:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up dependencies
run: |
sudo dpkg --add-architecture i386 && \
sudo apt-get update -qq && \
sudo apt-get install -yq binutils-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64 wine-stable:i386
- name: Build win32 cross-compiled
env:
CC: i686-w64-mingw32-gcc
CXX: i686-w64-mingw32-g++
EXE_WRAPPER: wine
PKG_CONFIG: "false"
WINEDEBUG: "-all"
run: |
make -j $(nproc)
- name: Set sha8
id: slug
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)"
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-win32-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
bin/*
!bin/*-ladspa.dll
!bin/*-dssi.dll

win64:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up dependencies
run: |
sudo apt-get install -yq binutils-mingw-w64-x86-64 g++-mingw-w64-x86-64 mingw-w64 wine-stable
- name: Build win64 cross-compiled
env:
CC: x86_64-w64-mingw32-gcc
CXX: x86_64-w64-mingw32-g++
EXE_WRAPPER: wine
PKG_CONFIG: "false"
WINEDEBUG: "-all"
run: |
make -j $(nproc)
- name: Set sha8
id: slug
run: echo "::set-output name=sha8::$(echo ${{ github.sha }} | cut -c1-8)"
- uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}-win64-${{ github.event.pull_request.number || steps.slug.outputs.sha8 }}
path: |
bin/*
!bin/*-ladspa.dll
!bin/*-dssi.dll

+ 0
- 1
dpf/LICENSE View File

@@ -1,4 +1,3 @@
DISTRHO Plugin Framework (DPF)
Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>

Permission to use, copy, modify, and/or distribute this software for any purpose with


+ 27
- 3
dpf/Makefile.base.mk View File

@@ -198,7 +198,7 @@ endif

ifeq ($(WINDOWS),true)
# Always build statically on windows
LINK_FLAGS += -static
LINK_FLAGS += -static -static-libgcc -static-libstdc++
endif

# ---------------------------------------------------------------------------------------------------------------------
@@ -247,6 +247,20 @@ endif

HAVE_LIBLO = $(shell $(PKG_CONFIG) --exists liblo && echo true)

ifeq ($(MACOS),true)
HAVE_RTAUDIO = true
else ifeq ($(WINDOWS),true)
HAVE_RTAUDIO = true
else ifneq ($(HAIKU),true)
HAVE_ALSA = $(shell $(PKG_CONFIG) --exists alsa && echo true)
HAVE_PULSEAUDIO = $(shell $(PKG_CONFIG) --exists libpulse-simple && echo true)
ifeq ($(HAVE_ALSA),true)
HAVE_RTAUDIO = true
else ifeq ($(HAVE_PULSEAUDIO),true)
HAVE_RTAUDIO = true
endif
endif

# backwards compat
HAVE_JACK = true

@@ -357,9 +371,19 @@ endif
# ---------------------------------------------------------------------------------------------------------------------
# Set optional libraries specific stuff

ifeq ($(HAVE_ALSA),true)
ALSA_FLAGS = $(shell $(PKG_CONFIG) --cflags alsa)
ALSA_LIBS = $(shell $(PKG_CONFIG) --libs alsa)
endif

ifeq ($(HAVE_LIBLO),true)
LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo)
LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo)
LIBLO_FLAGS = $(shell $(PKG_CONFIG) --cflags liblo)
LIBLO_LIBS = $(shell $(PKG_CONFIG) --libs liblo)
endif

ifeq ($(HAVE_PULSEAUDIO),true)
PULSEAUDIO_FLAGS = $(shell $(PKG_CONFIG) --cflags libpulse-simple)
PULSEAUDIO_LIBS = $(shell $(PKG_CONFIG) --libs libpulse-simple)
endif

# ---------------------------------------------------------------------------------------------------------------------


+ 32
- 16
dpf/Makefile.plugins.mk View File

@@ -7,14 +7,12 @@
# NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file!


ifeq ($(DPF_CUSTOM_PATH),)
ifeq ($(DPF_PATH),)
ifeq (,$(wildcard ../../Makefile.base.mk))
DPF_PATH=../../dpf
else
DPF_PATH=../..
endif
else
DPF_PATH = $(DPF_CUSTOM_PATH)
endif

include $(DPF_PATH)/Makefile.base.mk
@@ -22,31 +20,49 @@ include $(DPF_PATH)/Makefile.base.mk
# ---------------------------------------------------------------------------------------------------------------------
# Basic setup

ifeq ($(DPF_CUSTOM_PATH),)
ifeq ($(DPF_TARGET_DIR),)
TARGET_DIR = ../../bin
BUILD_DIR = ../../build/$(NAME)
else
ifeq ($(DPF_CUSTOM_TARGET_DIR),)
$(error DPF_CUSTOM_TARGET_DIR is not set)
else
TARGET_DIR = $(DPF_CUSTOM_TARGET_DIR)
TARGET_DIR = $(DPF_TARGET_DIR)
endif
ifeq ($(DPF_CUSTOM_BUILD_DIR),)
$(error DPF_CUSTOM_BUILD_DIR is not set)
ifeq ($(DPF_BUILD_DIR),)
BUILD_DIR = ../../build/$(NAME)
else
BUILD_DIR = $(DPF_CUSTOM_BUILD_DIR)
endif
BUILD_DIR = $(DPF_BUILD_DIR)
endif

BUILD_C_FLAGS += -I.
BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl

ifeq ($(HAVE_ALSA),true)
BASE_FLAGS += -DHAVE_ALSA
endif

ifeq ($(HAVE_LIBLO),true)
BASE_FLAGS += -DHAVE_LIBLO
endif

ifneq ($(HAIKU_OR_MACOS_OR_WINDOWS),true)
JACK_LIBS = -ldl
ifeq ($(HAVE_PULSEAUDIO),true)
BASE_FLAGS += -DHAVE_PULSEAUDIO
endif

ifeq ($(MACOS),true)
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)
endif
ifeq ($(HAVE_PULSEAUDIO),true)
JACK_FLAGS += $(PULSEAUDIO_FLAGS)
JACK_LIBS += $(PULSEAUDIO_LIBS)
endif
ifeq ($(HAVE_RTAUDIO),true)
JACK_LIBS += -lpthread
endif # !HAIKU
endif

# backwards compat
@@ -257,7 +273,7 @@ $(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm
$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoPluginMain.cpp (JACK)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK -c -o $@
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK $(JACK_FLAGS) -c -o $@

$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp
-@mkdir -p $(BUILD_DIR)


+ 9
- 0
dpf/cmake/DPF-plugin.cmake View File

@@ -187,6 +187,13 @@ function(dpf__build_jack NAME DGL_LIBRARY)
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")
find_library(APPLE_COREFOUNDATION_FRAMEWORK "CoreFoundation")
target_link_libraries("${NAME}-jack" PRIVATE "${APPLE_COREAUDIO_FRAMEWORK}" "${APPLE_COREFOUNDATION_FRAMEWORK}")
endif()
endfunction()

# dpf__build_ladspa
@@ -326,6 +333,7 @@ function(dpf__add_dgl_cairo)
"${DPF_ROOT_DIR}/dgl/src/Application.cpp"
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Color.cpp"
"${DPF_ROOT_DIR}/dgl/src/EventHandlers.cpp"
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp"
@@ -386,6 +394,7 @@ function(dpf__add_dgl_opengl)
"${DPF_ROOT_DIR}/dgl/src/Application.cpp"
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Color.cpp"
"${DPF_ROOT_DIR}/dgl/src/EventHandlers.cpp"
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp"


+ 11
- 1
dpf/dgl/Application.hpp View File

@@ -30,6 +30,8 @@ START_NAMESPACE_DGL
There's no single/global application instance in DGL, and multiple windows can share the same app instance.

In standalone mode an application will automatically quit its event-loop when all its windows are closed.

Unless stated otherwise, functions within this class are not thread-safe.
*/
class Application
{
@@ -61,6 +63,7 @@ public:
/**
Quit the application.
This stops the event-loop and closes all Windows.
This function is thread-safe.
@note This function is meant for standalones only, *never* call this from plugins.
*/
void quit();
@@ -68,14 +71,21 @@ public:
/**
Check if the application is about to quit.
Returning true means there's no event-loop running at the moment (or it's just about to stop).
This function is thread-safe.
*/
bool isQuiting() const noexcept;

/**
Check if the application is standalone, otherwise running as a module or plugin.
This function is thread-safe.
*/
bool isStandalone() const noexcept;

/**
Add a callback function to be triggered on every idle cycle.
You can add more than one, and remove them at anytime with removeIdleCallback().
Idle callbacks trigger right after OS event handling and Window idle events (within the same cycle).
There are no guarantees in terms of timing.
There are no guarantees in terms of timing, use Window::addIdleCallback for time-relative callbacks.
*/
void addIdleCallback(IdleCallback* callback);



+ 5
- 0
dpf/dgl/Color.hpp View File

@@ -65,6 +65,11 @@ struct Color {
*/
Color(const Color& color1, const Color& color2, float u) noexcept;

/**
Create a new color based on this one but with a different alpha value.
*/
Color withAlpha(float alpha) noexcept;

/**
Create a color specified by hue, saturation and lightness.
Values must in [0..1] range.


+ 154
- 0
dpf/dgl/EventHandlers.hpp View File

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

#ifndef DGL_EVENT_HANDLERS_HPP_INCLUDED
#define DGL_EVENT_HANDLERS_HPP_INCLUDED

#include "Widget.hpp"

START_NAMESPACE_DGL

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

class ButtonEventHandler
{
public:
enum State {
kButtonStateDefault = 0x0,
kButtonStateHover = 0x1,
kButtonStateActive = 0x2,
kButtonStateActiveHover = kButtonStateActive|kButtonStateHover
};

class Callback
{
public:
virtual ~Callback() {}
virtual void buttonClicked(SubWidget* widget, int button) = 0;
};

explicit ButtonEventHandler(SubWidget* self);
~ButtonEventHandler();

bool isActive() noexcept;
void setActive(bool active, bool sendCallback) noexcept;

bool isChecked() const noexcept;
void setChecked(bool checked, bool sendCallback) noexcept;

bool isCheckable() const noexcept;
void setCheckable(bool checkable) noexcept;

Point<double> getLastClickPosition() const noexcept;
Point<double> getLastMotionPosition() const noexcept;

void setCallback(Callback* callback) noexcept;

bool mouseEvent(const Widget::MouseEvent& ev);
bool motionEvent(const Widget::MotionEvent& ev);

protected:
State getState() const noexcept;
void clearState() noexcept;

virtual void stateChanged(State state, State oldState);

void setInternalCallback(Callback* callback) noexcept;
void triggerUserCallback(SubWidget* widget, int button);

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ButtonEventHandler)
};

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

class KnobEventHandler
{
public:
enum Orientation {
Horizontal,
Vertical
};

// NOTE hover not implemented yet
enum State {
kKnobStateDefault = 0x0,
kKnobStateHover = 0x1,
kKnobStateDragging = 0x2,
kKnobStateDraggingHover = kKnobStateDragging|kKnobStateHover
};

class Callback
{
public:
virtual ~Callback() {}
virtual void knobDragStarted(SubWidget* widget) = 0;
virtual void knobDragFinished(SubWidget* widget) = 0;
virtual void knobValueChanged(SubWidget* widget, float value) = 0;
};

explicit KnobEventHandler(SubWidget* self);
explicit KnobEventHandler(SubWidget* self, const KnobEventHandler& other);
KnobEventHandler& operator=(const KnobEventHandler& other);
~KnobEventHandler();

// returns raw value, is assumed to be scaled if using log
float getValue() const noexcept;

// NOTE: value is assumed to be scaled if using log
virtual bool setValue(float value, bool sendCallback = false) noexcept;

// returns 0-1 ranged value, already with log scale as needed
float getNormalizedValue() const noexcept;

// NOTE: value is assumed to be scaled if using log
void setDefault(float def) noexcept;

// NOTE: value is assumed to be scaled if using log
void setRange(float min, float max) noexcept;

void setStep(float step) noexcept;

void setUsingLogScale(bool yesNo) noexcept;

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

void setCallback(Callback* callback) noexcept;

bool mouseEvent(const Widget::MouseEvent& ev);
bool motionEvent(const Widget::MotionEvent& ev);
bool scrollEvent(const Widget::ScrollEvent& ev);

protected:
State getState() const noexcept;

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(KnobEventHandler)
};

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

END_NAMESPACE_DGL

#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED


+ 11
- 22
dpf/dgl/ImageBaseWidgets.hpp View File

@@ -17,6 +17,7 @@
#ifndef DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED
#define DGL_IMAGE_BASE_WIDGETS_HPP_INCLUDED

#include "EventHandlers.hpp"
#include "StandaloneWindow.hpp"
#include "SubWidget.hpp"

@@ -47,7 +48,8 @@ private:
// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
class ImageBaseButton : public SubWidget
class ImageBaseButton : public SubWidget,
public ButtonEventHandler
{
public:
class Callback
@@ -80,14 +82,10 @@ private:
// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
class ImageBaseKnob : public SubWidget
class ImageBaseKnob : public SubWidget,
public KnobEventHandler
{
public:
enum Orientation {
Horizontal,
Vertical
};

class Callback
{
public:
@@ -102,25 +100,16 @@ public:
ImageBaseKnob& operator=(const ImageBaseKnob& imageKnob);
~ImageBaseKnob() override;

float getValue() const noexcept;

void setDefault(float def) noexcept;
void setRange(float min, float max) noexcept;
void setStep(float step) noexcept;
void setValue(float value, bool sendCallback = false) noexcept;
void setUsingLogScale(bool yesNo) noexcept;

void setCallback(Callback* callback) noexcept;
void setOrientation(Orientation orientation) noexcept;
void setRotationAngle(int angle);

void setImageLayerCount(uint count) noexcept;
void setRotationAngle(int angle);
bool setValue(float value, bool sendCallback = false) noexcept override;

protected:
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;
void onDisplay() override;
bool onMouse(const MouseEvent&) override;
bool onMotion(const MotionEvent&) override;
bool onScroll(const ScrollEvent&) override;

private:
struct PrivateData;


+ 1
- 1
dpf/dgl/Makefile View File

@@ -27,6 +27,7 @@ OBJS_common = \
../build/dgl/Application.cpp.o \
../build/dgl/ApplicationPrivateData.cpp.o \
../build/dgl/Color.cpp.o \
../build/dgl/EventHandlers.cpp.o \
../build/dgl/Geometry.cpp.o \
../build/dgl/ImageBase.cpp.o \
../build/dgl/ImageBaseWidgets.cpp.o \
@@ -39,7 +40,6 @@ OBJS_common = \
../build/dgl/WidgetPrivateData.cpp.o \
../build/dgl/Window.cpp.o \
../build/dgl/WindowPrivateData.cpp.o
# ../build/dgl/WindowFileBrowser.cpp.o

# ---------------------------------------------------------------------------------------------------------------------



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

@@ -111,6 +111,22 @@ public:
*/
void setAbsolutePos(const Point<int>& pos) noexcept;

/**
Get the margin currently in use for widget coordinates.
By default this value is (0,0).
*/
Point<int> getMargin() const noexcept;

/**
Set a margin to be used for widget coordinates using @a x and @a y values.
*/
void setMargin(int x, int y) noexcept;

/**
Set a margin to be used for widget coordinates.
*/
void setMargin(const Point<int>& offset) noexcept;

/**
Get parent Widget, as passed in the constructor.
*/


+ 9
- 9
dpf/dgl/Widget.hpp View File

@@ -66,7 +66,7 @@ public:
uint flags;
uint time;

/** Constuctor */
/** Constructor */
BaseEvent() noexcept : mod(0x0), flags(0x0), time(0) {}
/** Destuctor */
virtual ~BaseEvent() noexcept {}
@@ -96,7 +96,7 @@ public:
uint key;
uint keycode;

/** Constuctor */
/** Constructor */
KeyboardEvent() noexcept
: BaseEvent(),
press(false),
@@ -118,7 +118,7 @@ public:
bool press;
Key key;

/** Constuctor */
/** Constructor */
SpecialEvent() noexcept
: BaseEvent(),
press(false),
@@ -145,7 +145,7 @@ public:
uint character;
char string[8];

/** Constuctor */
/** Constructor */
CharacterInputEvent() noexcept
: BaseEvent(),
keycode(0),
@@ -168,7 +168,7 @@ public:
Point<double> pos;
Point<double> absolutePos;

/** Constuctor */
/** Constructor */
MouseEvent() noexcept
: BaseEvent(),
button(0),
@@ -188,7 +188,7 @@ public:
Point<double> pos;
Point<double> absolutePos;

/** Constuctor */
/** Constructor */
MotionEvent() noexcept
: BaseEvent(),
pos(0.0, 0.0),
@@ -216,7 +216,7 @@ public:
Point<double> delta;
ScrollDirection direction;

/** Constuctor */
/** Constructor */
ScrollEvent() noexcept
: BaseEvent(),
pos(0.0, 0.0),
@@ -235,7 +235,7 @@ public:
Size<uint> size;
Size<uint> oldSize;

/** Constuctor */
/** Constructor */
ResizeEvent() noexcept
: size(0, 0),
oldSize(0, 0) {}
@@ -251,7 +251,7 @@ public:
Point<int> pos;
Point<int> oldPos;

/** Constuctor */
/** Constructor */
PositionChangedEvent() noexcept
: pos(0, 0),
oldPos(0, 0) {}


+ 40
- 16
dpf/dgl/Window.hpp View File

@@ -87,14 +87,14 @@ public:
/** Whether to show list of places (bookmarks) */
ButtonState showPlaces;

/** Constuctor for default values */
/** Constructor for default values */
Buttons()
: listAllFiles(kButtonVisibleChecked),
showHidden(kButtonVisibleUnchecked),
showPlaces(kButtonVisibleUnchecked) {}
} buttons;

/** Constuctor for default values */
/** Constructor for default values */
FileBrowserOptions()
: startDir(nullptr),
title(nullptr),
@@ -104,6 +104,43 @@ public:
};
#endif // DGL_FILE_BROWSER_DISABLED

/**
Window graphics context as a scoped struct.
This class gives graphics context drawing time to a window's widgets.
Typically used for allowing OpenGL drawing operations during a window + widget constructor.

Unless you are subclassing the Window or StandaloneWindow classes, you do not need to care.
In such cases you will need to use this struct as a way to get a valid OpenGL context.
For example in a standalone application:
```
int main()
{
Application app;
Window win(app);
ScopedPointer<MyCustomTopLevelWidget> widget;
{
const ScopedGraphicsContext sgc(win);
widget = new MyCustomTopLevelWidget(win);
}
app.exec();
return 0;
}
```

This struct is necessary because we cannot automatically make the window leave the OpenGL context in custom code.
We must always cleanly enter and leave the OpenGL context.
In order to avoid messing up the global host context, this class is used around widget creation.
*/
class ScopedGraphicsContext
{
Window& window;
public:
explicit ScopedGraphicsContext(Window& window);
~ScopedGraphicsContext();
DISTRHO_DECLARE_NON_COPYABLE(ScopedGraphicsContext)
DISTRHO_PREVENT_HEAP_ALLOCATION
};

/**
Constructor for a regular, standalone window.
*/
@@ -362,9 +399,6 @@ public:
DISTRHO_DEPRECATED_BY("runAsModal(bool)")
inline void exec(bool blockWait = false) { runAsModal(blockWait); }

// TESTING, DO NOT USE
void leaveContext();

protected:
/**
A function called when the window is attempted to be closed.
@@ -414,6 +448,7 @@ private:
struct PrivateData;
PrivateData* const pData;
friend class Application;
friend class PluginWindow;
friend class TopLevelWidget;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window);
@@ -423,15 +458,4 @@ private:

END_NAMESPACE_DGL

/* TODO
* add eventcrossing/enter-leave event
*/
#if 0
protected:
bool handlePluginKeyboard(const bool press, const uint key);
bool handlePluginSpecial(const bool press, const Key key);
#endif

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

#endif // DGL_WINDOW_HPP_INCLUDED

+ 5
- 0
dpf/dgl/src/Application.cpp View File

@@ -53,6 +53,11 @@ bool Application::isQuiting() const noexcept
return pData->isQuitting || pData->isQuittingInNextCycle;
}

bool Application::isStandalone() const noexcept
{
return pData->isStandalone;
}

void Application::addIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)


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

@@ -25,7 +25,7 @@ START_NAMESPACE_DGL

typedef std::list<DGL_NAMESPACE::Window*>::reverse_iterator WindowListReverseIterator;

static ThreadHandle getCurrentThreadHandle() noexcept
static d_ThreadHandle getCurrentThreadHandle() noexcept
{
#ifdef DISTRHO_OS_WINDOWS
return GetCurrentThread();
@@ -34,7 +34,7 @@ static ThreadHandle getCurrentThreadHandle() noexcept
#endif
}

static bool isThisMainThread(const ThreadHandle mainThreadHandle) noexcept
static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept
{
#ifdef DISTRHO_OS_WINDOWS
return GetCurrentThread() == mainThreadHandle; // IsGUIThread ?
@@ -127,7 +127,7 @@ void Application::PrivateData::quit()
{
DISTRHO_SAFE_ASSERT_RETURN(isStandalone,);

if (! isThisMainThread(mainThreadHandle))
if (! isThisTheMainThread(mainThreadHandle))
{
if (! isQuittingInNextCycle)
{


+ 3
- 3
dpf/dgl/src/ApplicationPrivateData.hpp View File

@@ -24,10 +24,10 @@
#ifdef DISTRHO_OS_WINDOWS
# include <winsock2.h>
# include <windows.h>
typedef HANDLE ThreadHandle;
typedef HANDLE d_ThreadHandle;
#else
# include <pthread.h>
typedef pthread_t ThreadHandle;
typedef pthread_t d_ThreadHandle;
#endif

typedef struct PuglWorldImpl PuglWorld;
@@ -59,7 +59,7 @@ struct Application::PrivateData {
uint visibleWindows;

/** Handle that identifies the main thread. Used to check if calls belong to current thread or not. */
ThreadHandle mainThreadHandle;
d_ThreadHandle mainThreadHandle;

/** List of windows for this application. Only used during `close`. */
std::list<DGL_NAMESPACE::Window*> windows;


+ 1
- 3
dpf/dgl/src/Cairo.cpp View File

@@ -24,7 +24,6 @@
#include "../Color.hpp"
#include "../ImageBaseWidgets.hpp"

#include "Common.hpp"
#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
@@ -657,8 +656,7 @@ void ImageBaseKnob<CairoImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
const double normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum)
/ (pData->maximum - pData->minimum);
const double normValue = getNormalizedValue();

cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface;



+ 7
- 0
dpf/dgl/src/Color.cpp View File

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

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

Color Color::fromHSL(float hue, float saturation, float lightness, float alpha)
{
float m1, m2;


+ 0
- 188
dpf/dgl/src/Common.hpp View File

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

#ifndef DGL_COMMON_HPP_INCLUDED
#define DGL_COMMON_HPP_INCLUDED

#include "../ImageBaseWidgets.hpp"

START_NAMESPACE_DGL

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

template <class ImageType>
struct ButtonImpl {
enum State {
kStateNormal = 0,
kStateHover,
kStateDown
};

int button;
int state;
ImageBaseButton<ImageType>* const self;

typename ImageBaseButton<ImageType>::Callback* callback_img;

explicit ButtonImpl(ImageBaseButton<ImageType>* const s) noexcept
: button(-1),
state(kStateNormal),
self(s),
callback_img(nullptr) {}

bool onMouse(const Widget::MouseEvent& ev)
{
// button was released, handle it now
if (button != -1 && ! ev.press)
{
DISTRHO_SAFE_ASSERT(state == kStateDown);

// release button
const int button2 = button;
button = -1;

// cursor was moved outside the button bounds, ignore click
if (! self->contains(ev.pos))
{
state = kStateNormal;
self->repaint();
return true;
}

// still on bounds, register click
state = kStateHover;
self->repaint();

if (callback_img != nullptr)
callback_img->imageButtonClicked(self, button2);

return true;
}

// button was pressed, wait for release
if (ev.press && self->contains(ev.pos))
{
button = static_cast<int>(ev.button);
state = kStateDown;
self->repaint();
return true;
}

return false;
}

bool onMotion(const Widget::MotionEvent& ev)
{
// keep pressed
if (button != -1)
return true;

if (self->contains(ev.pos))
{
// check if entering hover
if (state == kStateNormal)
{
state = kStateHover;
self->repaint();
return true;
}
}
else
{
// check if exiting hover
if (state == kStateHover)
{
state = kStateNormal;
self->repaint();
return true;
}
}

return false;
}

DISTRHO_PREVENT_HEAP_ALLOCATION
DISTRHO_DECLARE_NON_COPYABLE(ButtonImpl)
};

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

template <class ImageType>
struct ImageBaseKnob<ImageType>::PrivateData {
ImageType image;
float minimum;
float maximum;
float step;
float value;
float valueDef;
float valueTmp;
bool usingDefault;
bool usingLog;
Orientation orientation;

int rotationAngle;
bool dragging;
double lastX;
double lastY;

Callback* callback;

bool alwaysRepaint;
bool isImgVertical;
uint imgLayerWidth;
uint imgLayerHeight;
uint imgLayerCount;
bool isReady;

union {
uint glTextureId;
void* cairoSurface;
};

explicit PrivateData(const ImageType& img, const Orientation o);
explicit PrivateData(PrivateData* const other);
void assignFrom(PrivateData* const other);

~PrivateData()
{
cleanup();
}

void init();
void cleanup();

inline float logscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return a * std::exp(b*v);
}

inline float invlogscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return std::log(v/a)/b;
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

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

END_NAMESPACE_DGL

#endif // DGL_APP_PRIVATE_DATA_HPP_INCLUDED

+ 632
- 0
dpf/dgl/src/EventHandlers.cpp View File

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

#include "../EventHandlers.hpp"
#include "../SubWidget.hpp"

START_NAMESPACE_DGL

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

struct ButtonEventHandler::PrivateData {
ButtonEventHandler* const self;
SubWidget* const widget;
ButtonEventHandler::Callback* internalCallback;
ButtonEventHandler::Callback* userCallback;

int button;
int state;
bool checkable;
bool checked;

Point<double> lastClickPos;
Point<double> lastMotionPos;

PrivateData(ButtonEventHandler* const s, SubWidget* const w)
: self(s),
widget(w),
internalCallback(nullptr),
userCallback(nullptr),
button(-1),
state(kButtonStateDefault),
checkable(false),
checked(false),
lastClickPos(0, 0),
lastMotionPos(0, 0) {}

bool mouseEvent(const Widget::MouseEvent& ev)
{
lastClickPos = ev.pos;

// button was released, handle it now
if (button != -1 && ! ev.press)
{
DISTRHO_SAFE_ASSERT(state & kButtonStateActive);

// release button
const int button2 = button;
button = -1;

const int state2 = state;
state &= ~kButtonStateActive;

self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();

// cursor was moved outside the button bounds, ignore click
if (! widget->contains(ev.pos))
return true;

// still on bounds, register click
if (checkable)
checked = !checked;

if (internalCallback != nullptr)
internalCallback->buttonClicked(widget, button2);
else if (userCallback != nullptr)
userCallback->buttonClicked(widget, button2);

return true;
}

// button was pressed, wait for release
if (ev.press && widget->contains(ev.pos))
{
const int state2 = state;
button = static_cast<int>(ev.button);
state |= kButtonStateActive;
self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();
return true;
}

return false;
}

bool motionEvent(const Widget::MotionEvent& ev)
{
// keep pressed
if (button != -1)
{
lastMotionPos = ev.pos;
return true;
}

bool ret = false;

if (widget->contains(ev.pos))
{
// check if entering hover
if ((state & kButtonStateHover) == 0x0)
{
const int state2 = state;
state |= kButtonStateHover;
ret = widget->contains(lastMotionPos);
self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();
}
}
else
{
// check if exiting hover
if (state & kButtonStateHover)
{
const int state2 = state;
state &= ~kButtonStateHover;
ret = widget->contains(lastMotionPos);
self->stateChanged(static_cast<State>(state), static_cast<State>(state2));
widget->repaint();
}
}

lastMotionPos = ev.pos;
return ret;
}

void setActive(const bool active2, const bool sendCallback) noexcept
{
const bool active = state & kButtonStateActive;
if (active == active2)
return;

state |= kButtonStateActive;
widget->repaint();

if (sendCallback)
{
if (internalCallback != nullptr)
internalCallback->buttonClicked(widget, -1);
else if (userCallback != nullptr)
userCallback->buttonClicked(widget, -1);
}
}

void setChecked(const bool checked2, const bool sendCallback) noexcept
{
if (checked == checked2)
return;

checked = checked2;
widget->repaint();

if (sendCallback)
{
if (internalCallback != nullptr)
internalCallback->buttonClicked(widget, -1);
else if (userCallback != nullptr)
userCallback->buttonClicked(widget, -1);
}
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

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

ButtonEventHandler::ButtonEventHandler(SubWidget* const self)
: pData(new PrivateData(this, self)) {}

ButtonEventHandler::~ButtonEventHandler()
{
delete pData;
}

bool ButtonEventHandler::isActive() noexcept
{
return pData->state & kButtonStateActive;
}

void ButtonEventHandler::setActive(const bool active, const bool sendCallback) noexcept
{
pData->setActive(active, sendCallback);
}

bool ButtonEventHandler::isChecked() const noexcept
{
return pData->checked;
}

void ButtonEventHandler::setChecked(const bool checked, const bool sendCallback) noexcept
{
pData->setChecked(checked, sendCallback);
}

bool ButtonEventHandler::isCheckable() const noexcept
{
return pData->checkable;
}

void ButtonEventHandler::setCheckable(const bool checkable) noexcept
{
if (pData->checkable == checkable)
return;

pData->checkable = checkable;
}

Point<double> ButtonEventHandler::getLastClickPosition() const noexcept
{
return pData->lastClickPos;
}

Point<double> ButtonEventHandler::getLastMotionPosition() const noexcept
{
return pData->lastMotionPos;
}

void ButtonEventHandler::setCallback(Callback* const callback) noexcept
{
pData->userCallback = callback;
}

bool ButtonEventHandler::mouseEvent(const Widget::MouseEvent& ev)
{
return pData->mouseEvent(ev);
}

bool ButtonEventHandler::motionEvent(const Widget::MotionEvent& ev)
{
return pData->motionEvent(ev);
}

ButtonEventHandler::State ButtonEventHandler::getState() const noexcept
{
return static_cast<State>(pData->state);
}

void ButtonEventHandler::clearState() noexcept
{
pData->state = kButtonStateDefault;
}

void ButtonEventHandler::stateChanged(State, State)
{
}

void ButtonEventHandler::setInternalCallback(Callback* const callback) noexcept
{
pData->internalCallback = callback;
}

void ButtonEventHandler::triggerUserCallback(SubWidget* const widget, const int button)
{
if (pData->userCallback != nullptr)
pData->userCallback->buttonClicked(widget, button);
}

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

struct KnobEventHandler::PrivateData {
KnobEventHandler* const self;
SubWidget* const widget;
KnobEventHandler::Callback* callback;

float minimum;
float maximum;
float step;
float value;
float valueDef;
float valueTmp;
bool usingDefault;
bool usingLog;
Orientation orientation;
int state;

double lastX;
double lastY;

PrivateData(KnobEventHandler* const s, SubWidget* const w)
: self(s),
widget(w),
callback(nullptr),
minimum(0.0f),
maximum(1.0f),
step(0.0f),
value(0.5f),
valueDef(value),
valueTmp(value),
usingDefault(false),
usingLog(false),
orientation(Vertical),
state(kKnobStateDefault),
lastX(0.0),
lastY(0.0) {}

PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other)
: self(s),
widget(w),
callback(other->callback),
minimum(other->minimum),
maximum(other->maximum),
step(other->step),
value(other->value),
valueDef(other->valueDef),
valueTmp(value),
usingDefault(other->usingDefault),
usingLog(other->usingLog),
orientation(other->orientation),
state(kKnobStateDefault),
lastX(0.0),
lastY(0.0) {}

void assignFrom(PrivateData* const other)
{
callback = other->callback;
minimum = other->minimum;
maximum = other->maximum;
step = other->step;
value = other->value;
valueDef = other->valueDef;
valueTmp = value;
usingDefault = other->usingDefault;
usingLog = other->usingLog;
orientation = other->orientation;
state = kKnobStateDefault;
lastX = 0.0;
lastY = 0.0;
}

inline float logscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return a * std::exp(b*v);
}

inline float invlogscale(const float v) const
{
const float b = std::log(maximum/minimum)/(maximum-minimum);
const float a = maximum/std::exp(maximum*b);
return std::log(v/a)/b;
}

bool mouseEvent(const Widget::MouseEvent& ev)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! widget->contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && usingDefault)
{
setValue(valueDef, true);
valueTmp = value;
return true;
}

state |= kKnobStateDragging;
lastX = ev.pos.getX();
lastY = ev.pos.getY();
widget->repaint();

if (callback != nullptr)
callback->knobDragStarted(widget);

return true;
}
else if (state & kKnobStateDragging)
{
state &= ~kKnobStateDragging;
widget->repaint();

if (callback != nullptr)
callback->knobDragFinished(widget);

return true;
}

return false;
}

bool motionEvent(const Widget::MotionEvent& ev)
{
if ((state & kKnobStateDragging) == 0x0)
return false;

bool doVal = false;
float d, value2 = 0.0f;

if (orientation == Horizontal)
{
if (const double movX = ev.pos.getX() - lastX)
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movX));
doVal = true;
}
}
else if (orientation == Vertical)
{
if (const double movY = lastY - ev.pos.getY())
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movY));
doVal = true;
}
}

if (! doVal)
return false;

if (usingLog)
value2 = logscale(value2);

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

if (d_isNotZero(step))
{
const float rest = std::fmod(value2, step);
value2 -= rest + (rest > step/2.0f ? step : 0.0f);
}
}

setValue(value2, true);

lastX = ev.pos.getX();
lastY = ev.pos.getY();

return true;
}

bool scrollEvent(const Widget::ScrollEvent& ev)
{
if (! widget->contains(ev.pos))
return false;

const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f;
const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp)
+ ((maximum - minimum) / d * 10.f * dir);

if (usingLog)
value2 = logscale(value2);

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

if (d_isNotZero(step))
{
const float rest = std::fmod(value2, step);
value2 = value2 - rest + (rest > step/2.0f ? step : 0.0f);
}
}

setValue(value2, true);
return true;
}

float getNormalizedValue() const noexcept
{
const float diff = maximum - minimum;
return ((usingLog ? invlogscale(value) : value) - minimum) / diff;
}

void setRange(const float min, const float max) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(max > min,);

if (value < min)
{
valueTmp = value = min;
widget->repaint();
}
else if (value > max)
{
valueTmp = value = max;
widget->repaint();
}

minimum = min;
maximum = max;
}

bool setValue(const float value2, const bool sendCallback)
{
if (d_isEqual(value, value2))
return false;

valueTmp = value = value2;
widget->repaint();

if (sendCallback && callback != nullptr)
{
try {
callback->knobValueChanged(widget, value);
} DISTRHO_SAFE_EXCEPTION("KnobEventHandler::setValue");
}

return true;
}
};

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

KnobEventHandler::KnobEventHandler(SubWidget* const self)
: pData(new PrivateData(this, self)) {}

KnobEventHandler::KnobEventHandler(SubWidget* const self, const KnobEventHandler& other)
: pData(new PrivateData(this, self, other.pData)) {}

KnobEventHandler& KnobEventHandler::operator=(const KnobEventHandler& other)
{
pData->assignFrom(other.pData);
return *this;
}

KnobEventHandler::~KnobEventHandler()
{
delete pData;
}

float KnobEventHandler::getValue() const noexcept
{
return pData->value;
}

bool KnobEventHandler::setValue(const float value, const bool sendCallback) noexcept
{
return pData->setValue(value, sendCallback);
}

float KnobEventHandler::getNormalizedValue() const noexcept
{
return pData->getNormalizedValue();
}

void KnobEventHandler::setDefault(const float def) noexcept
{
pData->valueDef = def;
pData->usingDefault = true;
}

void KnobEventHandler::setRange(const float min, const float max) noexcept
{
pData->setRange(min, max);
}

void KnobEventHandler::setStep(const float step) noexcept
{
pData->step = step;
}

void KnobEventHandler::setUsingLogScale(const bool yesNo) noexcept
{
pData->usingLog = yesNo;
}

KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept
{
return pData->orientation;
}

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

pData->orientation = orientation;
}

void KnobEventHandler::setCallback(Callback* const callback) noexcept
{
pData->callback = callback;
}

bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev)
{
return pData->mouseEvent(ev);
}

bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev)
{
return pData->motionEvent(ev);
}

bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev)
{
return pData->scrollEvent(ev);
}

KnobEventHandler::State KnobEventHandler::getState() const noexcept
{
return static_cast<State>(pData->state);
}

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

END_NAMESPACE_DGL

+ 163
- 311
dpf/dgl/src/ImageBaseWidgets.cpp View File

@@ -16,7 +16,6 @@

#include "../ImageBaseWidgets.hpp"
#include "../Color.hpp"
#include "Common.hpp"

START_NAMESPACE_DGL

@@ -89,18 +88,25 @@ bool ImageBaseAboutWindow<ImageType>::onMouse(const MouseEvent& ev)
// --------------------------------------------------------------------------------------------------------------------

template <class ImageType>
struct ImageBaseButton<ImageType>::PrivateData {
ButtonImpl<ImageType> impl;
struct ImageBaseButton<ImageType>::PrivateData : public ButtonEventHandler::Callback {
ImageBaseButton<ImageType>::Callback* callback;
ImageType imageNormal;
ImageType imageHover;
ImageType imageDown;

PrivateData(ImageBaseButton<ImageType>* const s, const ImageType& normal, const ImageType& hover, const ImageType& down)
: impl(s),
PrivateData(const ImageType& normal, const ImageType& hover, const ImageType& down)
: callback(nullptr),
imageNormal(normal),
imageHover(hover),
imageDown(down) {}

void buttonClicked(SubWidget* widget, int button) override
{
if (callback != nullptr)
if (ImageBaseButton* const imageButton = dynamic_cast<ImageBaseButton*>(widget))
callback->imageButtonClicked(imageButton, button);
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

@@ -109,28 +115,34 @@ struct ImageBaseButton<ImageType>::PrivateData {
template <class ImageType>
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& image)
: SubWidget(parentWidget),
pData(new PrivateData(this, image, image, image))
ButtonEventHandler(this),
pData(new PrivateData(image, image, image))
{
ButtonEventHandler::setCallback(pData);
setSize(image.getSize());
}

template <class ImageType>
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageDown)
: SubWidget(parentWidget),
pData(new PrivateData(this, imageNormal, imageNormal, imageDown))
ButtonEventHandler(this),
pData(new PrivateData(imageNormal, imageNormal, imageDown))
{
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageDown.getSize());

ButtonEventHandler::setCallback(pData);
setSize(imageNormal.getSize());
}

template <class ImageType>
ImageBaseButton<ImageType>::ImageBaseButton(Widget* const parentWidget, const ImageType& imageNormal, const ImageType& imageHover, const ImageType& imageDown)
: SubWidget(parentWidget),
pData(new PrivateData(this, imageNormal, imageHover, imageDown))
ButtonEventHandler(this),
pData(new PrivateData(imageNormal, imageHover, imageDown))
{
DISTRHO_SAFE_ASSERT(imageNormal.getSize() == imageHover.getSize() && imageHover.getSize() == imageDown.getSize());

ButtonEventHandler::setCallback(pData);
setSize(imageNormal.getSize());
}

@@ -143,7 +155,7 @@ ImageBaseButton<ImageType>::~ImageBaseButton()
template <class ImageType>
void ImageBaseButton<ImageType>::setCallback(Callback* callback) noexcept
{
pData->impl.callback_img = callback;
pData->callback = callback;
}

template <class ImageType>
@@ -151,137 +163,162 @@ void ImageBaseButton<ImageType>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());

switch (pData->impl.state)
{
case ButtonImpl<ImageType>::kStateDown:
const State state = ButtonEventHandler::getState();
if (state & kButtonStateActive)
pData->imageDown.draw(context);
break;
case ButtonImpl<ImageType>::kStateHover:
else if (state & kButtonStateHover)
pData->imageHover.draw(context);
break;
default:
else
pData->imageNormal.draw(context);
break;
}
}

template <class ImageType>
bool ImageBaseButton<ImageType>::onMouse(const MouseEvent& ev)
{
return pData->impl.onMouse(ev);
if (SubWidget::onMouse(ev))
return true;
return ButtonEventHandler::mouseEvent(ev);
}

template <class ImageType>
bool ImageBaseButton<ImageType>::onMotion(const MotionEvent& ev)
{
return pData->impl.onMotion(ev);
if (SubWidget::onMotion(ev))
return true;
return ButtonEventHandler::motionEvent(ev);
}

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

template <class ImageType>
ImageBaseKnob<ImageType>::PrivateData::PrivateData(const ImageType& img, const Orientation o)
: image(img),
minimum(0.0f),
maximum(1.0f),
step(0.0f),
value(0.5f),
valueDef(value),
valueTmp(value),
usingDefault(false),
usingLog(false),
orientation(o),
rotationAngle(0),
dragging(false),
lastX(0.0),
lastY(0.0),
callback(nullptr),
alwaysRepaint(false),
isImgVertical(img.getHeight() > img.getWidth()),
imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()),
imgLayerHeight(imgLayerWidth),
imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth),
isReady(false)
{
init();
}
struct ImageBaseKnob<ImageType>::PrivateData : public KnobEventHandler::Callback {
ImageBaseKnob<ImageType>::Callback* callback;
ImageType image;

template <class ImageType>
ImageBaseKnob<ImageType>::PrivateData::PrivateData(PrivateData* const other)
: image(other->image),
minimum(other->minimum),
maximum(other->maximum),
step(other->step),
value(other->value),
valueDef(other->valueDef),
valueTmp(value),
usingDefault(other->usingDefault),
usingLog(other->usingLog),
orientation(other->orientation),
rotationAngle(other->rotationAngle),
dragging(false),
lastX(0.0),
lastY(0.0),
callback(other->callback),
alwaysRepaint(other->alwaysRepaint),
isImgVertical(other->isImgVertical),
imgLayerWidth(other->imgLayerWidth),
imgLayerHeight(other->imgLayerHeight),
imgLayerCount(other->imgLayerCount),
isReady(false)
{
init();
}
int rotationAngle;

bool alwaysRepaint;
bool isImgVertical;
uint imgLayerWidth;
uint imgLayerHeight;
uint imgLayerCount;
bool isReady;

union {
uint glTextureId;
void* cairoSurface;
};

explicit PrivateData(const ImageType& img)
: callback(nullptr),
image(img),
rotationAngle(0),
alwaysRepaint(false),
isImgVertical(img.getHeight() > img.getWidth()),
imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()),
imgLayerHeight(imgLayerWidth),
imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth),
isReady(false)
{
init();
}

template <class ImageType>
void ImageBaseKnob<ImageType>::PrivateData::assignFrom(PrivateData* const other)
{
cleanup();
image = other->image;
minimum = other->minimum;
maximum = other->maximum;
step = other->step;
value = other->value;
valueDef = other->valueDef;
valueTmp = value;
usingDefault = other->usingDefault;
usingLog = other->usingLog;
orientation = other->orientation;
rotationAngle = other->rotationAngle;
dragging = false;
lastX = 0.0;
lastY = 0.0;
callback = other->callback;
alwaysRepaint = other->alwaysRepaint;
isImgVertical = other->isImgVertical;
imgLayerWidth = other->imgLayerWidth;
imgLayerHeight = other->imgLayerHeight;
imgLayerCount = other->imgLayerCount;
isReady = false;
init();
}
explicit PrivateData(PrivateData* const other)
: callback(other->callback),
image(other->image),
rotationAngle(other->rotationAngle),
alwaysRepaint(other->alwaysRepaint),
isImgVertical(other->isImgVertical),
imgLayerWidth(other->imgLayerWidth),
imgLayerHeight(other->imgLayerHeight),
imgLayerCount(other->imgLayerCount),
isReady(false)
{
init();
}

void assignFrom(PrivateData* const other)
{
cleanup();
image = other->image;
rotationAngle = other->rotationAngle;
callback = other->callback;
alwaysRepaint = other->alwaysRepaint;
isImgVertical = other->isImgVertical;
imgLayerWidth = other->imgLayerWidth;
imgLayerHeight = other->imgLayerHeight;
imgLayerCount = other->imgLayerCount;
isReady = false;
init();
}

~PrivateData()
{
cleanup();
}

void knobDragStarted(SubWidget* const widget) override
{
if (callback != nullptr)
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
callback->imageKnobDragStarted(imageKnob);
}

void knobDragFinished(SubWidget* const widget) override
{
if (callback != nullptr)
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
callback->imageKnobDragFinished(imageKnob);
}

void knobValueChanged(SubWidget* const widget, const float value) override
{
if (rotationAngle == 0 || alwaysRepaint)
isReady = false;

if (callback != nullptr)
if (ImageBaseKnob* const imageKnob = dynamic_cast<ImageBaseKnob*>(widget))
callback->imageKnobValueChanged(imageKnob, value);
}

// implemented independently per graphics backend
void init();
void cleanup();

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

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

template <class ImageType>
ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget, const ImageType& image, const Orientation orientation) noexcept
ImageBaseKnob<ImageType>::ImageBaseKnob(Widget* const parentWidget,
const ImageType& image,
const Orientation orientation) noexcept
: SubWidget(parentWidget),
pData(new PrivateData(image, orientation))
KnobEventHandler(this),
pData(new PrivateData(image))
{
KnobEventHandler::setCallback(pData);
setOrientation(orientation);
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
}

template <class ImageType>
ImageBaseKnob<ImageType>::ImageBaseKnob(const ImageBaseKnob<ImageType>& imageKnob)
: SubWidget(imageKnob.getParentWidget()),
KnobEventHandler(this, imageKnob),
pData(new PrivateData(imageKnob.pData))
{
KnobEventHandler::setCallback(pData);
setOrientation(imageKnob.getOrientation());
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
}

template <class ImageType>
ImageBaseKnob<ImageType>& ImageBaseKnob<ImageType>::operator=(const ImageBaseKnob<ImageType>& imageKnob)
{
KnobEventHandler::operator=(imageKnob);
pData->assignFrom(imageKnob.pData);
setSize(pData->imgLayerWidth, pData->imgLayerHeight);
return *this;
@@ -293,116 +330,12 @@ ImageBaseKnob<ImageType>::~ImageBaseKnob()
delete pData;
}

template <class ImageType>
float ImageBaseKnob<ImageType>::getValue() const noexcept
{
return pData->value;
}

// NOTE: value is assumed to be scaled if using log
template <class ImageType>
void ImageBaseKnob<ImageType>::setDefault(float value) noexcept
{
pData->valueDef = value;
pData->usingDefault = true;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setRange(float min, float max) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(max > min,);

if (pData->value < min)
{
pData->value = min;
repaint();

if (pData->callback != nullptr)
{
try {
pData->callback->imageKnobValueChanged(this, pData->value);
} DISTRHO_SAFE_EXCEPTION("ImageBaseKnob<ImageType>::setRange < min");
}
}
else if (pData->value > max)
{
pData->value = max;
repaint();

if (pData->callback != nullptr)
{
try {
pData->callback->imageKnobValueChanged(this, pData->value);
} DISTRHO_SAFE_EXCEPTION("ImageBaseKnob<ImageType>::setRange > max");
}
}

pData->minimum = min;
pData->maximum = max;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setStep(float step) noexcept
{
pData->step = step;
}

// NOTE: value is assumed to be scaled if using log
template <class ImageType>
void ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept
{
if (d_isEqual(pData->value, value))
return;

pData->value = value;

if (d_isZero(pData->step))
pData->valueTmp = value;

if (pData->rotationAngle == 0 || pData->alwaysRepaint)
pData->isReady = false;

repaint();

if (sendCallback && pData->callback != nullptr)
{
try {
pData->callback->imageKnobValueChanged(this, pData->value);
} DISTRHO_SAFE_EXCEPTION("ImageBaseKnob<ImageType>::setValue");
}
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setUsingLogScale(bool yesNo) noexcept
{
pData->usingLog = yesNo;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setCallback(Callback* callback) noexcept
{
pData->callback = callback;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setOrientation(Orientation orientation) noexcept
{
if (pData->orientation == orientation)
return;

pData->orientation = orientation;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setRotationAngle(int angle)
{
if (pData->rotationAngle == angle)
return;

pData->rotationAngle = angle;
pData->isReady = false;
}

template <class ImageType>
void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept
{
@@ -419,38 +352,23 @@ void ImageBaseKnob<ImageType>::setImageLayerCount(uint count) noexcept
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
void ImageBaseKnob<ImageType>::setRotationAngle(int angle)
{
if (ev.button != 1)
return false;

if (ev.press)
{
if (! contains(ev.pos))
return false;

if ((ev.mod & kModifierShift) != 0 && pData->usingDefault)
{
setValue(pData->valueDef, true);
pData->valueTmp = pData->value;
return true;
}

pData->dragging = true;
pData->lastX = ev.pos.getX();
pData->lastY = ev.pos.getY();
if (pData->rotationAngle == angle)
return;

if (pData->callback != nullptr)
pData->callback->imageKnobDragStarted(this);
pData->rotationAngle = angle;
pData->isReady = false;
}

return true;
}
else if (pData->dragging)
template <class ImageType>
bool ImageBaseKnob<ImageType>::setValue(float value, bool sendCallback) noexcept
{
if (KnobEventHandler::setValue(value, sendCallback))
{
if (pData->callback != nullptr)
pData->callback->imageKnobDragFinished(this);
if (pData->rotationAngle == 0 || pData->alwaysRepaint)
pData->isReady = false;

pData->dragging = false;
return true;
}

@@ -458,93 +376,27 @@ bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev)
bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
{
if (! pData->dragging)
return false;

bool doVal = false;
float d, value = 0.0f;

if (pData->orientation == ImageBaseKnob<ImageType>::Horizontal)
{
if (const double movX = ev.pos.getX() - pData->lastX)
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movX));
doVal = true;
}
}
else if (pData->orientation == ImageBaseKnob<ImageType>::Vertical)
{
if (const double movY = pData->lastY - ev.pos.getY())
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp) + (float(pData->maximum - pData->minimum) / d * float(movY));
doVal = true;
}
}

if (! doVal)
return false;

if (pData->usingLog)
value = pData->logscale(value);

if (value < pData->minimum)
{
pData->valueTmp = value = pData->minimum;
}
else if (value > pData->maximum)
{
pData->valueTmp = value = pData->maximum;
}
else if (d_isNotZero(pData->step))
{
pData->valueTmp = value;
const float rest = std::fmod(value, pData->step);
value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
}

setValue(value, true);

pData->lastX = ev.pos.getX();
pData->lastY = ev.pos.getY();
if (SubWidget::onMouse(ev))
return true;
return KnobEventHandler::mouseEvent(ev);
}

return true;
template <class ImageType>
bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev)
{
if (SubWidget::onMotion(ev))
return true;
return KnobEventHandler::motionEvent(ev);
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::onScroll(const ScrollEvent& ev)
{
if (! contains(ev.pos))
return false;

const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f;
const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
float value = (pData->usingLog ? pData->invlogscale(pData->valueTmp) : pData->valueTmp)
+ ((pData->maximum - pData->minimum) / d * 10.f * dir);

if (pData->usingLog)
value = pData->logscale(value);

if (value < pData->minimum)
{
pData->valueTmp = value = pData->minimum;
}
else if (value > pData->maximum)
{
pData->valueTmp = value = pData->maximum;
}
else if (d_isNotZero(pData->step))
{
pData->valueTmp = value;
const float rest = std::fmod(value, pData->step);
value = value - rest + (rest > pData->step/2.0f ? pData->step : 0.0f);
}

setValue(value, true);
return true;
if (SubWidget::onScroll(ev))
return true;
return KnobEventHandler::scrollEvent(ev);
}

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


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

@@ -287,7 +287,10 @@ void NanoVG::beginFrame(Widget* const widget)
return;

if (TopLevelWidget* const tlw = widget->getTopLevelWidget())
nvgBeginFrame(fContext, static_cast<int>(tlw->getWidth()), static_cast<int>(tlw->getHeight()), 1.0f);
nvgBeginFrame(fContext,
static_cast<int>(tlw->getWidth()),
static_cast<int>(tlw->getHeight()),
tlw->getScaleFactor());
}

void NanoVG::cancelFrame()


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

@@ -23,7 +23,6 @@
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

#include "Common.hpp"
#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
@@ -317,6 +316,8 @@ static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, con
setupCalled = true;
}

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

@@ -486,8 +487,7 @@ template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum)
/ (pData->maximum - pData->minimum);
const float normValue = getNormalizedValue();

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);


+ 20
- 5
dpf/dgl/src/SubWidget.cpp View File

@@ -32,9 +32,9 @@ SubWidget::~SubWidget()
}

template<typename T>
bool SubWidget::contains(T x, T y) const noexcept
bool SubWidget::contains(const T x, const T y) const noexcept
{
return Rectangle<double>(0, 0, getWidth(), getHeight()).contains(x, y);
return Rectangle<double>(0, 0, getWidth()-pData->margin.getX(), getHeight()-pData->margin.getY()).contains(x, y);
}

template<typename T>
@@ -70,17 +70,17 @@ Rectangle<uint> SubWidget::getConstrainedAbsoluteArea() const noexcept
getSize());
}

void SubWidget::setAbsoluteX(int x) noexcept
void SubWidget::setAbsoluteX(const int x) noexcept
{
setAbsolutePos(Point<int>(x, getAbsoluteY()));
}

void SubWidget::setAbsoluteY(int y) noexcept
void SubWidget::setAbsoluteY(const int y) noexcept
{
setAbsolutePos(Point<int>(getAbsoluteX(), y));
}

void SubWidget::setAbsolutePos(int x, int y) noexcept
void SubWidget::setAbsolutePos(const int x, const int y) noexcept
{
setAbsolutePos(Point<int>(x, y));
}
@@ -100,6 +100,21 @@ void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept
repaint();
}

Point<int> SubWidget::getMargin() const noexcept
{
return pData->margin;
}

void SubWidget::setMargin(const int x, const int y) noexcept
{
pData->margin = Point<int>(x, y);
}

void SubWidget::setMargin(const Point<int>& offset) noexcept
{
pData->margin = offset;
}

Widget* SubWidget::getParentWidget() const noexcept
{
return pData->parentWidget;


+ 1
- 0
dpf/dgl/src/SubWidgetPrivateData.cpp View File

@@ -26,6 +26,7 @@ SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw)
selfw((Widget*)s),
parentWidget(pw),
absolutePos(),
margin(),
needsFullViewportForDrawing(false),
needsViewportScaling(false),
skipDrawing(false),


+ 1
- 0
dpf/dgl/src/SubWidgetPrivateData.hpp View File

@@ -28,6 +28,7 @@ struct SubWidget::PrivateData {
Widget* const selfw;
Widget* const parentWidget;
Point<int> absolutePos;
Point<int> margin;
bool needsFullViewportForDrawing; // needed for widgets drawing out of bounds
bool needsViewportScaling; // needed for NanoVG
bool skipDrawing; // for context reuse in NanoVG based guis


+ 6
- 6
dpf/dgl/src/WidgetPrivateData.cpp View File

@@ -152,8 +152,8 @@ bool Widget::PrivateData::giveMouseEventForSubWidgets(MouseEvent& ev)
if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(),
y - widget->getAbsoluteY() + widget->getMargin().getY());

if (widget->onMouse(ev))
return true;
@@ -191,8 +191,8 @@ bool Widget::PrivateData::giveMotionEventForSubWidgets(MotionEvent& ev)
if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(),
y - widget->getAbsoluteY() + widget->getMargin().getY());

if (widget->onMotion(ev))
return true;
@@ -230,8 +230,8 @@ bool Widget::PrivateData::giveScrollEventForSubWidgets(ScrollEvent& ev)
if (! widget->isVisible())
continue;

ev.pos = Point<double>(x - widget->getAbsoluteX(),
y - widget->getAbsoluteY());
ev.pos = Point<double>(x - widget->getAbsoluteX() + widget->getMargin().getX(),
y - widget->getAbsoluteY() + widget->getMargin().getY());

if (widget->onScroll(ev))
return true;


+ 14
- 5
dpf/dgl/src/Window.cpp View File

@@ -20,6 +20,20 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// ScopedGraphicsContext

Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win)
: window(win)
{
puglBackendEnter(window.pData->view);
}

Window::ScopedGraphicsContext::~ScopedGraphicsContext()
{
puglBackendLeave(window.pData->view);
}

// -----------------------------------------------------------------------
// Window

@@ -265,11 +279,6 @@ void Window::runAsModal(bool blockWait)
pData->runAsModal(blockWait);
}

void Window::leaveContext()
{
pData->leaveContext();
}

void Window::setGeometryConstraints(const uint minimumWidth,
const uint minimumHeight,
const bool keepAspectRatio,


+ 11
- 13
dpf/dgl/src/WindowPrivateData.cpp View File

@@ -79,6 +79,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s)
appData(a.pData),
self(s),
view(puglNewView(appData->world)),
transientParentView(nullptr),
topLevelWidgets(),
isClosed(true),
isVisible(false),
@@ -102,6 +103,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c
appData(a.pData),
self(s),
view(puglNewView(appData->world)),
transientParentView(ppData->view),
topLevelWidgets(),
isClosed(true),
isVisible(false),
@@ -117,9 +119,10 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c
#endif
modal(ppData)
{
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
puglBackendLeave(transientParentView);
puglSetTransientFor(view, puglGetNativeWindow(transientParentView));

puglSetTransientFor(view, puglGetNativeWindow(ppData->view));
initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
}

Window::PrivateData::PrivateData(Application& a, Window* const s,
@@ -129,6 +132,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
appData(a.pData),
self(s),
view(puglNewView(appData->world)),
transientParentView(nullptr),
topLevelWidgets(),
isClosed(parentWindowHandle == 0),
isVisible(parentWindowHandle != 0),
@@ -158,6 +162,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
appData(a.pData),
self(s),
view(puglNewView(appData->world)),
transientParentView(nullptr),
topLevelWidgets(),
isClosed(parentWindowHandle == 0),
isVisible(parentWindowHandle != 0),
@@ -237,14 +242,15 @@ void Window::PrivateData::initPost()
// create view now, as a few methods we allow devs to use require it
puglRealize(view);

// FIXME this is bad, the enter/leave should be well scoped. try to find a better place for it..
puglBackendEnter(view);

if (isEmbed)
{
appData->oneWindowShown();
puglShow(view);
}

// give context back to transient parent window
if (transientParentView != nullptr)
puglBackendEnter(transientParentView);
}

// -----------------------------------------------------------------------
@@ -630,14 +636,6 @@ void Window::PrivateData::runAsModal(const bool blockWait)
}
}

// -----------------------------------------------------------------------
// TESTING

void Window::PrivateData::leaveContext()
{
puglBackendLeave(view);
}

// -----------------------------------------------------------------------
// pugl events



+ 3
- 3
dpf/dgl/src/WindowPrivateData.hpp View File

@@ -44,6 +44,9 @@ struct Window::PrivateData : IdleCallback {
/** Pugl view instance. */
PuglView* const view;

/** Pugl view instance of the transient parent window. */
PuglView* const transientParentView;

/** Reserved space for graphics context. */
mutable uint8_t graphicsContext[sizeof(void*)];

@@ -161,9 +164,6 @@ struct Window::PrivateData : IdleCallback {
void stopModal();
void runAsModal(bool blockWait);

// TESTING
void leaveContext();

// pugl events
void onPuglConfigure(double width, double height);
void onPuglExpose();


+ 11
- 10
dpf/dgl/src/pugl-upstream/src/win.c View File

@@ -118,8 +118,7 @@ puglWinGetWindowExFlags(const PuglView* const view)
}

PuglWorldInternals*
puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
PuglWorldFlags PUGL_UNUSED(flags))
puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags))
{
PuglWorldInternals* impl =
(PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals));
@@ -127,15 +126,17 @@ puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
return NULL;
}

HMODULE user32 = LoadLibrary("user32.dll");
if (user32) {
PFN_SetProcessDPIAware SetProcessDPIAware =
(PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware");
if (SetProcessDPIAware) {
SetProcessDPIAware();
}
if (type == PUGL_PROGRAM) {
HMODULE user32 = LoadLibrary("user32.dll");
if (user32) {
PFN_SetProcessDPIAware SetProcessDPIAware =
(PFN_SetProcessDPIAware)GetProcAddress(user32, "SetProcessDPIAware");
if (SetProcessDPIAware) {
SetProcessDPIAware();
}

FreeLibrary(user32);
FreeLibrary(user32);
}
}

LARGE_INTEGER frequency;


+ 9
- 1
dpf/dgl/src/pugl.cpp View File

@@ -177,10 +177,18 @@ void puglClearMinSize(PuglView* const view)
view->minHeight = 0;
}

// --------------------------------------------------------------------------------------------------------------------
// missing in pugl, directly returns transient parent

PuglNativeView puglGetTransientParent(const PuglView* const view)
{
return view->transientParent;
}

// --------------------------------------------------------------------------------------------------------------------
// missing in pugl, directly returns title char* pointer

const char* puglGetWindowTitle(const PuglView* view)
const char* puglGetWindowTitle(const PuglView* const view)
{
return view->title;
}


+ 4
- 0
dpf/dgl/src/pugl.hpp View File

@@ -54,6 +54,10 @@ puglBackendLeave(PuglView* view);
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);


+ 3
- 3
dpf/distrho/DistrhoPlugin.hpp View File

@@ -51,20 +51,20 @@ static const uint32_t kAudioPortIsSidechain = 0x2;
static const uint32_t kCVPortHasBipolarRange = 0x10;

/**
CV port has negative unipolar range (0 to +1, or 0 to +10 if scaled).
CV port has negative unipolar range (-1 to 0, or -10 to 0 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasNegativeUnipolarRange = 0x20;

/**
CV port has positive unipolar range (-1 to 0, or -10 to 0 if scaled).
CV port has positive unipolar range (0 to +1, or 0 to +10 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasPositiveUnipolarRange = 0x40;

/**
CV port has scaled range to match real values (-5 to +5v bipolar, +/-10 to 0v unipolar).
One range flag is required if this flag is set.
One other range flag is required if this flag is set.

When enabled, this makes the port a mod:CVPort, compatible with the MOD Devices platform.
*/


+ 5
- 1
dpf/distrho/DistrhoUI.hpp View File

@@ -48,6 +48,10 @@ typedef DGL_NAMESPACE::NanoTopLevelWidget UIWidget;
typedef DGL_NAMESPACE::TopLevelWidget UIWidget;
#endif

START_NAMESPACE_DGL
class PluginWindow;
END_NAMESPACE_DGL

START_NAMESPACE_DISTRHO

/* ------------------------------------------------------------------------------------------------------------
@@ -311,7 +315,7 @@ protected:
private:
struct PrivateData;
PrivateData* const uiData;
friend class PluginWindow;
friend class DGL_NAMESPACE::PluginWindow;
friend class UIExporter;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI)


+ 3
- 0
dpf/distrho/extra/LibraryUtils.hpp View File

@@ -20,6 +20,9 @@
#include "../DistrhoUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <winsock2.h>
# include <windows.h>
typedef HMODULE lib_t;


+ 3
- 0
dpf/distrho/extra/Mutex.hpp View File

@@ -20,6 +20,9 @@
#include "../DistrhoUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <winsock2.h>
# include <windows.h>
#endif


+ 3
- 0
dpf/distrho/extra/Sleep.hpp View File

@@ -20,6 +20,9 @@
#include "../DistrhoUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <winsock2.h>
# include <windows.h>
#else


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

@@ -70,6 +70,7 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por
break;
}
}

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

@@ -157,9 +158,9 @@ struct Plugin::PrivateData {
#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# if DISTRHO_PLUGIN_WANT_STATE
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
# endif
#endif
}


+ 16
- 0
dpf/distrho/src/DistrhoPluginJACK.cpp View File

@@ -38,6 +38,10 @@
# define JACK_METADATA_PRETTY_NAME "http://jackaudio.org/metadata/pretty-name"
#endif

#ifndef JACK_METADATA_PORT_GROUP
# define JACK_METADATA_PORT_GROUP "http://jackaudio.org/metadata/port-group"
#endif

#ifndef JACK_METADATA_SIGNAL_TYPE
# define JACK_METADATA_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type"
#endif
@@ -351,6 +355,12 @@ protected:
fTimePosition.bbt.bar = pos.bar;
fTimePosition.bbt.beat = pos.beat;
fTimePosition.bbt.tick = pos.tick;
#ifdef JACK_TICK_DOUBLE
if (pos.valid & JackTickDouble)
fTimePosition.bbt.tick = pos.tick_double;
else
#endif
fTimePosition.bbt.tick = pos.tick;
fTimePosition.bbt.barStartTick = pos.bar_start_tick;

fTimePosition.bbt.beatsPerBar = pos.beats_per_bar;
@@ -588,6 +598,12 @@ private:
jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer");
}

if (port.groupId != kPortGroupNone)
{
const PortGroupWithId& portGroup(fPlugin.getPortGroupById(port.groupId));
jackbridge_set_property(fClient, uuid, JACK_METADATA_PORT_GROUP, portGroup.name, "text/plain");
}

if (port.hints & kAudioPortIsCV)
{
jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "CV", "text/plain");


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

@@ -236,7 +236,7 @@ public:
fTimePosition.bbt.barStartTick = 0;
fTimePosition.bbt.beatsPerBar = 4;
fTimePosition.bbt.beatType = 4;
fTimePosition.bbt.ticksPerBeat = 960.0;
fTimePosition.bbt.ticksPerBeat = 1920.0;
fTimePosition.bbt.beatsPerMinute = 120.0;
#endif
fPlugin.activate();
@@ -359,7 +359,7 @@ public:
if (obj->body.otype != fURIDs.timePosition)
continue;

LV2_Atom* bar = nullptr;
LV2_Atom* bar = nullptr;
LV2_Atom* barBeat = nullptr;
LV2_Atom* beatUnit = nullptr;
LV2_Atom* beatsPerBar = nullptr;


+ 60
- 8
dpf/distrho/src/DistrhoPluginLV2export.cpp View File

@@ -430,9 +430,21 @@ void lv2_generate_ttl(const char* const basename)
if (port.hints & kAudioPortIsSidechain)
pluginString += " lv2:portProperty lv2:isSideChain;\n";

if (port.groupId != kPortGroupNone)
switch (port.groupId)
{
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;
}

// set ranges
if (port.hints & kCVPortHasBipolarRange)
@@ -508,9 +520,21 @@ void lv2_generate_ttl(const char* const basename)
if (port.hints & kAudioPortIsSidechain)
pluginString += " lv2:portProperty lv2:isSideChain;\n";

if (port.groupId != kPortGroupNone)
switch (port.groupId)
{
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;
}

// set ranges
if (port.hints & kCVPortHasBipolarRange)
@@ -731,7 +755,7 @@ void lv2_generate_ttl(const char* const basename)
// unit
const String& unit(plugin.getParameterUnit(i));

if (unit.isNotEmpty() && ! unit.contains(" "))
if (unit.isNotEmpty() && ! unit.contains(' '))
{
String lunit(unit);
lunit.toLower();
@@ -770,7 +794,10 @@ void lv2_generate_ttl(const char* const basename)
pluginString += " a unit:Unit ;\n";
pluginString += " rdfs:label \"" + unit + "\" ;\n";
pluginString += " unit:symbol \"" + unit + "\" ;\n";
pluginString += " unit:render \"%f " + unit + "\" ;\n";
if (plugin.getParameterHints(i) & kParameterIsInteger)
pluginString += " unit:render \"%d " + unit + "\" ;\n";
else
pluginString += " unit:render \"%f " + unit + "\" ;\n";
pluginString += " ] ;\n";
}
}
@@ -779,7 +806,12 @@ void lv2_generate_ttl(const char* const basename)
const String& comment(plugin.getParameterDescription(i));

if (comment.isNotEmpty())
pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n";
{
if (comment.contains('"') || comment.contains('\n'))
pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n";
else
pluginString += " rdfs:comment \"" + comment + "\" ;\n";
}

// hints
const uint32_t hints(plugin.getParameterHints(i));
@@ -805,9 +837,22 @@ void lv2_generate_ttl(const char* const basename)
// group
const uint32_t groupId = plugin.getParameterGroupId(i);

if (groupId != kPortGroupNone)
switch (groupId)
{
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(groupId) + "> ;\n";
break;
}

} // ! designated

if (i+1 == count)
@@ -823,8 +868,8 @@ void lv2_generate_ttl(const char* const basename)

if (comment.isNotEmpty())
{
if (comment.contains('"'))
pluginString += " rdfs:comment \"" + comment + "\" ;\n\n";
if (comment.contains('"') || comment.contains('\n'))
pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n\n";
else
pluginString += " rdfs:comment \"" + comment + "\" ;\n\n";
}
@@ -903,6 +948,13 @@ 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;



+ 36
- 4
dpf/distrho/src/DistrhoPluginVST2.cpp View File

@@ -1063,7 +1063,7 @@ public:
fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0);

// ticksPerBeat is not possible with VST
fTimePosition.bbt.ticksPerBeat = 960.0;
fTimePosition.bbt.ticksPerBeat = 1920.0;

if (vstTimeInfo->flags & kVstTempoValid)
fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo;
@@ -1099,7 +1099,9 @@ public:
fTimePosition.bbt.beatType = 4.0f;
}

fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*fTimePosition.bbt.beatsPerBar*(fTimePosition.bbt.bar-1);
fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
fTimePosition.bbt.beatsPerBar*
(fTimePosition.bbt.bar-1);

fPlugin.setTimePosition(fTimePosition);
}
@@ -1448,7 +1450,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t
memset(properties, 0, sizeof(VstParameterProperties));

// full name
DISTRHO_NAMESPACE::strncpy(properties->label, plugin.getParameterName(index), VestigeMaxLabelLen);
DISTRHO_NAMESPACE::strncpy(properties->label,
plugin.getParameterName(index),
sizeof(properties->label));

// short name
const String& shortName(plugin.getParameterShortName(index));
@@ -1456,7 +1460,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t
if (shortName.isNotEmpty())
DISTRHO_NAMESPACE::strncpy(properties->shortLabel,
plugin.getParameterShortName(index),
VestigeMaxShortLabelLen);
sizeof(properties->shortLabel));

// parameter hints
const uint32_t hints = plugin.getParameterHints(index);
@@ -1482,6 +1486,34 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t
properties->flags |= kVstParameterCanRamp;
}

// parameter group (category in vst)
const uint32_t groupId = plugin.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)
{
const PortGroupWithId& portGroup(plugin.getPortGroupByIndex(i));

if (portGroup.groupId == groupId)
{
properties->category = i + 1;
DISTRHO_NAMESPACE::strncpy(properties->categoryLabel,
portGroup.name.buffer(),
sizeof(properties->categoryLabel));
break;
}
}

if (properties->category != 0)
{
for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i)
if (plugin.getParameterGroupId(i) == groupId)
++properties->numParametersInCategory;
}
}

return 1;
}
}


+ 2
- 2
dpf/distrho/src/DistrhoPluginVST3.cpp View File

@@ -96,7 +96,7 @@ private:

static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
{
return ((PluginVst*)ptr)->requestParameterValueChange(index, value);
return ((PluginVst3*)ptr)->requestParameterValueChange(index, value);
}
#endif

@@ -109,7 +109,7 @@ private:

static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
{
return ((PluginVst*)ptr)->writeMidi(midiEvent);
return ((PluginVst3*)ptr)->writeMidi(midiEvent);
}
#endif



+ 1
- 1
dpf/distrho/src/DistrhoUI.cpp View File

@@ -39,7 +39,7 @@ UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr;
PluginWindow& UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height)
{
UI::PrivateData* const pData = s_nextPrivateData;
pData->window = new PluginWindow(ui, pData, width, height);
pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, pData->scaleFactor);
return pData->window.getObject();
}



+ 16
- 11
dpf/distrho/src/DistrhoUIInternal.hpp View File

@@ -260,7 +260,9 @@ public:
void quit()
{
uiData->window->close();
uiData->app.quit();

if (uiData->app.isStandalone())
uiData->app.quit();
}
#endif

@@ -316,20 +318,23 @@ public:
return ! uiData->app.isQuiting();
}

bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/)
bool handlePluginKeyboard(const bool press, const uint key, const uint16_t mods)
{
#if 0 /* TODO */
return glWindow.handlePluginKeyboard(press, key);
#endif
return false;
// TODO also trigger Character input event
Widget::KeyboardEvent ev;
ev.press = press;
ev.key = key;
ev.mod = mods;
return ui->onKeyboard(ev);
}

bool handlePluginSpecial(const bool /*press*/, const DGL_NAMESPACE::Key /*key*/, const uint16_t /*mods*/)
bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key, const uint16_t mods)
{
#if 0 /* TODO */
return glWindow.handlePluginSpecial(press, key);
#endif
return false;
Widget::SpecialEvent ev;
ev.press = press;
ev.key = key;
ev.mod = mods;
return ui->onSpecial(ev);
}
#endif



+ 17
- 3
dpf/distrho/src/DistrhoUILV2.cpp View File

@@ -89,11 +89,14 @@ public:
bgColor,
fgColor),
fUridMap(uridMap),
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
fController(controller),
fWriteFunction(writeFunc),
fURIDs(uridMap),
fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled")
: LV2UI_INVALID_PORT_INDEX),
fWinIdWasNull(winId == 0)
{
if (widget != nullptr)
@@ -159,7 +162,11 @@ public:

DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)

const float value = *(const float*)buffer;
float value = *(const float*)buffer;

if (rindex == fBypassParameterIndex)
value = 1.0f - value;

fUI.parameterChanged(rindex-parameterOffset, value);
}
#if DISTRHO_PLUGIN_WANT_STATE
@@ -253,10 +260,13 @@ protected:
fUiTouch->touch(fUiTouch->handle, rindex, started);
}

void setParameterValue(const uint32_t rindex, const float value)
void setParameterValue(const uint32_t rindex, float value)
{
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);

if (rindex == fBypassParameterIndex)
value = 1.0f - value;

fWriteFunction(fController, rindex, sizeof(float), 0, &value);
}

@@ -345,6 +355,7 @@ private:

// LV2 features
const LV2_URID_Map* const fUridMap;
const LV2UI_Port_Map* const fUiPortMap;
const LV2UI_Request_Value* const fUiRequestValue;
const LV2UI_Touch* const fUiTouch;

@@ -383,8 +394,11 @@ private:
}
} fURIDs;

// index of bypass parameter, if present
const uint32_t fBypassParameterIndex;

// using ui:showInterface if true
bool fWinIdWasNull;
const bool fWinIdWasNull;

// -------------------------------------------------------------------
// Callbacks


+ 150
- 112
dpf/distrho/src/DistrhoUIPrivateData.hpp View File

@@ -20,8 +20,9 @@
#include "../DistrhoUI.hpp"
#include "../../dgl/Application.hpp"

#ifndef DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../../dgl/Window.hpp"
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../../dgl/src/WindowPrivateData.hpp"
# include "../../dgl/src/pugl.hpp"
#endif

#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
@@ -35,20 +36,9 @@
# define DISTRHO_UI_USER_RESIZABLE 0
#endif

START_NAMESPACE_DISTRHO

using DGL_NAMESPACE::Application;
using DGL_NAMESPACE::Window;

// -----------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);
START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details
@@ -73,7 +63,118 @@ public:
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};

class PluginWindow;
// -----------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO external ui stuff
class PluginWindow
{
UI* const ui;

public:
explicit PluginWindow(UI* const uiPtr,
PluginApplication& app,
const uintptr_t parentWindowHandle,
const uint width,
const uint height,
const double scaleFactor)
: Window(app, parentWindowHandle, width, height, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
ui(uiPtr) {}

uint getWidth() const noexcept
{
return ui->getWidth();
}

uint getHeight() const noexcept
{
return ui->getHeight();
}

bool isVisible() const noexcept
{
return ui->isRunning();
}

uintptr_t getNativeWindowHandle() const noexcept
{
return 0;
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#else
class PluginWindow : public Window
{
UI* const ui;

public:
explicit PluginWindow(UI* const uiPtr,
PluginApplication& app,
const uintptr_t parentWindowHandle,
const uint width,
const uint height,
const double scaleFactor)
: Window(app, parentWindowHandle, width, height, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
ui(uiPtr)
{
puglBackendEnter(pData->view);
}

void leaveContext()
{
puglBackendLeave(pData->view);
}

protected:
void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiFocus(focus, mode);
}

void onReshape(const uint width, const uint height) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiReshape(width, height);
}

void onScaleFactorChanged(const double scaleFactor) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiScaleFactorChanged(scaleFactor);
}

# ifndef DGL_FILE_BROWSER_DISABLED
void onFileSelected(const char* filename) override;
# endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

END_NAMESPACE_DGL

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

START_NAMESPACE_DISTRHO

using DGL_NAMESPACE::PluginApplication;
using DGL_NAMESPACE::PluginWindow;

// -----------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// UI private data
@@ -143,16 +244,18 @@ struct UI::PrivateData {
#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# if DISTRHO_PLUGIN_WANT_STATE
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
# endif
#endif
}

~PrivateData() noexcept
{
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
std::free(uiStateFileKeyRequest);
#endif
}

void editParamCallback(const uint32_t rindex, const bool started)
@@ -192,100 +295,6 @@ struct UI::PrivateData {
static PluginWindow& createNextWindow(UI* ui, uint width, uint height);
};

// -----------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO external ui stuff
class PluginWindow
{
UI* const ui;

public:
explicit PluginWindow(UI* const uiPtr, UI::PrivateData* const pData, const uint width, const uint height)
: Window(pData->app, pData->winId, pData->scaleFactor, width, height, DISTRHO_UI_USER_RESIZABLE),
ui(uiPtr) {}

uint getWidth() const noexcept
{
return ui->getWidth();
}

uint getHeight() const noexcept
{
return ui->getHeight();
}

bool isVisible() const noexcept
{
return ui->isRunning();
}

uintptr_t getNativeWindowHandle() const noexcept
{
return 0;
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#else
class PluginWindow : public Window
{
UI* const ui;

public:
explicit PluginWindow(UI* const uiPtr, UI::PrivateData* const pData, const uint width, const uint height)
: Window(pData->app, pData->winId, width, height, pData->scaleFactor, DISTRHO_UI_USER_RESIZABLE),
ui(uiPtr) {}

protected:
void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiFocus(focus, mode);
}

void onReshape(const uint width, const uint height) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiReshape(width, height);
}

void onScaleFactorChanged(const double scaleFactor) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiScaleFactorChanged(scaleFactor);
}

# ifndef DGL_FILE_BROWSER_DISABLED
void onFileSelected(const char* const filename) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

# if DISTRHO_PLUGIN_WANT_STATEFILES
if (char* const key = ui->uiData->uiStateFileKeyRequest)
{
ui->uiData->uiStateFileKeyRequest = nullptr;
// notify DSP
ui->setState(key, filename);
// notify UI
ui->stateChanged(key, filename);
std::free(key);
return;
}
# endif

ui->uiFileBrowserSelected(filename);
}
# endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

// -----------------------------------------------------------------------
// UI private data fileRequestCallback, which requires PluginWindow definitions

@@ -311,8 +320,37 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)
return false;
}

END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// PluginWindow onFileSelected that require UI::PrivateData definitions

END_NAMESPACE_DISTRHO
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
START_NAMESPACE_DGL

inline void PluginWindow::onFileSelected(const char* const filename)
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

# if DISTRHO_PLUGIN_WANT_STATEFILES
if (char* const key = ui->uiData->uiStateFileKeyRequest)
{
ui->uiData->uiStateFileKeyRequest = nullptr;
// notify DSP
ui->setState(key, filename);
// notify UI
ui->stateChanged(key, filename);
std::free(key);
return;
}
# endif

ui->uiFileBrowserSelected(filename);
}

END_NAMESPACE_DGL
#endif

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

#endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED

+ 294
- 153
dpf/distrho/src/jackbridge/JackBridge.cpp
File diff suppressed because it is too large
View File


+ 6
- 2
dpf/distrho/src/jackbridge/JackBridge.hpp View File

@@ -99,6 +99,8 @@
#define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1) /* includes trailing null */
#define JACK_UUID_EMPTY_INITIALIZER 0

#define JACK_TICK_DOUBLE

extern "C" {

enum JackOptions {
@@ -153,7 +155,8 @@ enum JackPositionBits {
JackPositionTimecode = 0x020,
JackBBTFrameOffset = 0x040,
JackAudioVideoRatio = 0x080,
JackVideoFrameOffset = 0x100
JackVideoFrameOffset = 0x100,
JackTickDouble = 0x200
};

enum JackSessionEventType {
@@ -223,7 +226,8 @@ struct _jack_position {
jack_nframes_t bbt_offset;
float audio_frames_per_video_frame;
jack_nframes_t video_offset;
int32_t padding[7];
double tick_double;
int32_t padding[5];
jack_unique_t unique_2;
} POST_PACKED_STRUCTURE;



+ 216
- 0
dpf/distrho/src/jackbridge/RtAudioBridge.hpp View File

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

#ifndef RTAUDIOBRIDGE_HPP_INCLUDED
#define RTAUDIOBRIDGE_HPP_INCLUDED

#include "JackBridge.hpp"

#if defined(DISTRHO_OS_MAC)
# 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
#elif 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 RTAUDIO_API_TYPE
# define Point CorePoint /* fix conflict between DGL and macOS Point name */
# include "rtaudio/RtAudio.h"
# undef Point
# include "../../extra/ScopedPointer.hpp"

struct RtAudioBridge {
// pointer to RtAudio instance
ScopedPointer<RtAudio> 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
float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS];

// TODO midi buffer, likely using ringbuffer class

bool open()
{
ScopedPointer<RtAudio> rtAudio;

try {
rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE);
} DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false);

uint rtAudioBufferFrames = 512;

RtAudio::StreamParameters inParams;
inParams.deviceId = rtAudio->getDefaultInputDevice();
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS;

RtAudio::StreamParameters outParams;
outParams.deviceId = rtAudio->getDefaultOutputDevice();
outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS;

RtAudio::StreamOptions opts;
opts.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_ALSA_USE_DEFAULT;

try {
rtAudio->openStream(&outParams, &inParams, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, RtAudioCallback, this, &opts, nullptr);
} DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false);

handle = rtAudio;
bufferSize = rtAudioBufferFrames;
sampleRate = handle->getStreamSampleRate();
return true;
}

bool close()
{
DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false);

if (handle->isStreamRunning())
{
try {
handle->abortStream();
} DISTRHO_SAFE_EXCEPTION("handle->abortStream()");
}

handle = nullptr;
return true;
}

bool activate()
{
DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false);

try {
handle->startStream();
} DISTRHO_SAFE_EXCEPTION("handle->startStream()");

return true;
}

bool deactivate()
{
DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, false);

try {
handle->stopStream();
} DISTRHO_SAFE_EXCEPTION("handle->stopStream()");

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 ? 0x1000 : 0x2000) | (isInput ? 0x4000 : 0x8000);

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;

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (portMask & 0x1000)
return audioBuffers[(portMask & 0x4000 ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)];
#endif

return nullptr;
}

static int RtAudioCallback(void* const outputBuffer,
void* const inputBuffer,
const uint numFrames,
const double /* streamTime */,
const RtAudioStreamStatus /* status */,
void* const userData)
{
RtAudioBridge* const self = (RtAudioBridge*)userData;

if (self->jackProcessCallback == nullptr)
{
if (outputBuffer != nullptr)
std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS);
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)
{
for (uint j=0; j<DISTRHO_PLUGIN_NUM_INPUTS; ++j, ++i)
selfAudioBuffers[i] = insPtr + (j * numFrames);
}
# endif
# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (float* const outsPtr = (float*)outputBuffer)
{
for (uint j=0; j<DISTRHO_PLUGIN_NUM_OUTPUTS; ++j, ++i)
selfAudioBuffers[i] = outsPtr + (j * numFrames);
}
# endif
#endif

self->jackProcessCallback(numFrames, self->jackProcessArg);
return 0;

#if DISTRHO_PLUGIN_NUM_INPUTS == 0
// unused
(void)inputBuffer;
#endif
}
};

#endif // RTAUDIO_API_TYPE
#endif // RTAUDIOBRIDGE_HPP_INCLUDED

+ 27
- 0
dpf/distrho/src/jackbridge/rtaudio/LICENSE View File

@@ -0,0 +1,27 @@

RtAudio: a set of realtime audio i/o C++ classes
Copyright (c) 2001-2019 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.

+ 10911
- 0
dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp
File diff suppressed because it is too large
View File


+ 1185
- 0
dpf/distrho/src/jackbridge/rtaudio/RtAudio.h
File diff suppressed because it is too large
View File


+ 42
- 40
dpf/tests/Demo.cpp View File

@@ -404,33 +404,35 @@ public:

DemoWindow(Application& app)
: StandaloneWindow(app),
wColor(this),
wImages(this),
wRects(this),
wShapes(this),
#ifdef DGL_OPENGL
wText(this),
#endif
wLeft(this, this),
resizer(this),
curWidget(nullptr)
{
wColor.hide();
wImages.hide();
wRects.hide();
wShapes.hide();
#ifdef DGL_OPENGL
wText.hide();
#endif
const ScopedGraphicsContext sgc(*this);

wColor = new ExampleColorSubWidget(this);
wColor->hide();
wColor->setAbsoluteX(kSidebarWidth);

wImages = new ExampleImagesSubWidget(this);
wImages->hide();
wImages->setAbsoluteX(kSidebarWidth);

wRects = new ExampleRectanglesSubWidget(this);
wRects->hide();
wRects->setAbsoluteX(kSidebarWidth);

wShapes = new ExampleShapesSubWidget(this);
wShapes->hide();
wShapes->setAbsoluteX(kSidebarWidth);

wColor.setAbsoluteX(kSidebarWidth);
wImages.setAbsoluteX(kSidebarWidth);
wRects.setAbsoluteX(kSidebarWidth);
wShapes.setAbsoluteX(kSidebarWidth);
#ifdef DGL_OPENGL
wText.setAbsoluteX(kSidebarWidth);
wText = new ExampleTextSubWidget(this),
wText->hide();
wText->setAbsoluteX(kSidebarWidth);
#endif
wLeft.setAbsolutePos(2, 2);
wLeft = new LeftSideWidget(this, this),
wLeft->setAbsolutePos(2, 2);

resizer = new ResizeHandle(this);

curPageChanged(0);
}
@@ -444,20 +446,20 @@ protected:
switch (curPage)
{
case 0:
curWidget = &wColor;
curWidget = wColor;
break;
case 1:
curWidget = &wImages;
curWidget = wImages;
break;
case 2:
curWidget = &wRects;
curWidget = wRects;
break;
case 3:
curWidget = &wShapes;
curWidget = wShapes;
break;
#ifdef DGL_OPENGL
case 4:
curWidget = &wText;
curWidget = wText;
break;
#endif
default:
@@ -481,26 +483,26 @@ protected:
return;

Size<uint> size(width-kSidebarWidth, height);
wColor.setSize(size);
wImages.setSize(size);
wRects.setSize(size);
wShapes.setSize(size);
wColor->setSize(size);
wImages->setSize(size);
wRects->setSize(size);
wShapes->setSize(size);
#ifdef DGL_OPENGL
wText.setSize(size);
wText->setSize(size);
#endif
wLeft.setSize(kSidebarWidth-4, height-4);
wLeft->setSize(kSidebarWidth-4, height-4);
}

private:
ExampleColorSubWidget wColor;
ExampleImagesSubWidget wImages;
ExampleRectanglesSubWidget wRects;
ExampleShapesSubWidget wShapes;
ScopedPointer<ExampleColorSubWidget> wColor;
ScopedPointer<ExampleImagesSubWidget> wImages;
ScopedPointer<ExampleRectanglesSubWidget> wRects;
ScopedPointer<ExampleShapesSubWidget> wShapes;
#ifdef DGL_OPENGL
ExampleTextSubWidget wText;
ScopedPointer<ExampleTextSubWidget> wText;
#endif
LeftSideWidget wLeft;
ResizeHandle resizer;
ScopedPointer<LeftSideWidget> wLeft;
ScopedPointer<ResizeHandle> resizer;

Widget* curWidget;
};


+ 0
- 13
dpf/utils/README-DPF-Windows.txt View File

@@ -1,13 +0,0 @@
######################################
## Generic README for DISTRHO Plugins
This folder contains audio plugins downloaded from http://distrho.sourceforge.net/plugins
The included *.lv2 folder is an LV2 bundle and the *.dll file is a VST.
Check your host documentation to know where to place these files.
Note that most hosts support VST but not LV2.
If your host is not listing these plugins, make sure the binaries you downloaded match your host architecture.
32bit plugins go into 32bit hosts, same for 64bit.
Some hosts are able to load plugins with different architecture, but that's the exception rather than the rule.

+ 1
- 1
dpf/utils/generate-ttl.sh View File

@@ -1,7 +1,7 @@
#!/bin/bash

# function not available on some systems
if ! which realpath 2>/dev/null; then
if ! which realpath &>/dev/null; then
function realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}


+ 0
- 54
dpf/utils/pack-win-dlls.sh View File

@@ -1,54 +0,0 @@
#!/bin/bash

set -e

# --------------------------------------------------------------------------------------------------------------------------------
# extract debs and pack them

function compressFolderAsZip() {
rm -f "$1.zip"
zip -X -r "$1" "$1"
rm -r "$1"
}

# --------------------------------------------------------------------------------------------------------------------------------

if [ "$1" == "" ]; then
echo Missing argument
exit
fi

# --------------------------------------------------------------------------------------------------------------------------------

cd bin

mkdir -p tmp
rm -rf tmp/*

NAME="$1"

_mingw32-build make -C .. clean
_mingw32-build make -C ..
for i in `ls *-vst.dll`; do mv $i `echo $i | awk 'sub("-vst","")'`; done
rm -rf *ladspa* *dssi* *vst*
mkdir -p "$NAME-win32bit"
mv *.dll *.lv2/ "$NAME-win32bit"
cp "../dpf/utils/README-DPF-Windows.txt" "$NAME-win32bit/README.txt"
compressFolderAsZip "$NAME-win32bit"
rm -rf tmp/*

_mingw64-build make -C .. clean
_mingw64-build make -C ..
for i in `ls *-vst.dll`; do mv $i `echo $i | awk 'sub("-vst","")'`; done
rm -rf *ladspa* *dssi* *vst*
mkdir -p "$NAME-win64bit"
mv *.dll *.lv2/ "$NAME-win64bit"
cp "../dpf/utils/README-DPF-Windows.txt" "$NAME-win64bit/README.txt"
compressFolderAsZip "$NAME-win64bit"
rm -rf tmp/*

_mingw64-build make -C .. clean

cd ..

# --------------------------------------------------------------------------------------------------------------------------------

+ 51
- 0
dpf/utils/package-osx-bundles.sh View File

@@ -0,0 +1,51 @@
#!/bin/bash

set -e

if [ -d bin ]; then
cd bin
else
echo "Please run this script from the root folder"
exit
fi

NAME="$(basename $(git rev-parse --show-toplevel))"
SNAME="$(echo ${NAME} | tr -d ' ' | tr '/' '-')"

rm -rf lv2
rm -rf vst2

mkdir lv2 vst2
mv *.lv2 lv2/
mv *.vst vst2/

pkgbuild \
--identifier "studio.kx.distrho.plugins.${SNAME}.lv2bundles" \
--install-location "/Library/Audio/Plug-Ins/LV2/" \
--root "${PWD}/lv2/" \
../dpf-${SNAME}-lv2bundles.pkg

pkgbuild \
--identifier "studio.kx.distrho.plugins.${SNAME}.vst2bundles" \
--install-location "/Library/Audio/Plug-Ins/VST/" \
--root "${PWD}/vst2/" \
../dpf-${SNAME}-vst2bundles.pkg

cd ..

DPF_UTILS_DIR=$(dirname ${0})

sed -e "s|@name@|${NAME}|" ${DPF_UTILS_DIR}/plugin.pkg/welcome.txt.in > build/welcome.txt
sed -e "s|@builddir@|${PWD}/build|" \
-e "s|@lv2bundleref@|dpf-${SNAME}-lv2bundles.pkg|" \
-e "s|@vst2bundleref@|dpf-${SNAME}-vst2bundles.pkg|" \
-e "s|@name@|${NAME}|g" \
-e "s|@sname@|${SNAME}|g" \
${DPF_UTILS_DIR}/plugin.pkg/package.xml.in > build/package.xml

productbuild \
--distribution build/package.xml \
--identifier "studio.kx.distrho.${SNAME}" \
--package-path "${PWD}" \
--version 0 \
${SNAME}-macOS.pkg

+ 18
- 0
dpf/utils/plugin.pkg/package.xml.in View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<installer-gui-script minSpecVersion="1">
<title>@name@</title>
<domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true" />
<options customize="always" hostArchitectures="x86_64" require-scripts="false" rootVolumeOnly="true" />
<pkg-ref id="studio.kx.distrho.@sname@" />
<welcome file="@builddir@/welcome.txt" mime-type="text/plain" />
<choice id="studio.kx.distrho.@sname@-lv2" title="LV2" description="Install LV2 plugins" visible="true">
<pkg-ref id="studio.kx.distrho.@sname@-lv2bundles" version="0">@lv2bundleref@</pkg-ref>
</choice>
<choice id="studio.kx.distrho.@sname@-vst2" title="VST2" description="Install VST2 plugins" visible="true">
<pkg-ref id="studio.kx.distrho.@sname@-vst2bundles" version="0">@vst2bundleref@</pkg-ref>
</choice>
<choices-outline>
<line choice="studio.kx.distrho.@sname@-lv2"/>
<line choice="studio.kx.distrho.@sname@-vst2"/>
</choices-outline>
</installer-gui-script>

+ 3
- 0
dpf/utils/plugin.pkg/welcome.txt.in View File

@@ -0,0 +1,3 @@
@name@

This is an audio plugin installer based on DPF.

+ 1
- 1
dpf/utils/plugin.vst/Contents/Info.plist View File

@@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>net.sf.distrho.X-PROJECTNAME-X</string>
<string>studio.kx.distrho.X-PROJECTNAME-X</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>


+ 1
- 2
plugins/Kars/LICENSE View File

@@ -1,5 +1,4 @@
DISTRHO Plugin Framework (DPF)
Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>

Permission to use, copy, modify, and/or distribute this software for any purpose with
or without fee is hereby granted, provided that the above copyright notice and this


+ 7
- 2
plugins/MVerb/DistrhoUIMVerb.cpp View File

@@ -29,7 +29,7 @@ using DGL::Color;

DistrhoUIMVerb::DistrhoUIMVerb()
: UI(Art::backgroundWidth, Art::backgroundHeight),
fImgBackground(Art::backgroundData, Art::backgroundWidth, Art::backgroundHeight, GL_BGR)
fImgBackground(Art::backgroundData, Art::backgroundWidth, Art::backgroundHeight, kImageFormatBGR)
{
// text
fNanoText.loadSharedResources();
@@ -122,6 +122,9 @@ DistrhoUIMVerb::DistrhoUIMVerb()

// set initial values
programLoaded(0);

// TODO auto-scale but non-resizable
// setGeometryConstraints(Art::backgroundWidth, Art::backgroundHeight, true, true);
}

DistrhoUIMVerb::~DistrhoUIMVerb()
@@ -225,7 +228,9 @@ void DistrhoUIMVerb::imageKnobValueChanged(ImageKnob* knob, float value)

void DistrhoUIMVerb::onDisplay()
{
fImgBackground.draw();
const GraphicsContext& context(getGraphicsContext());

fImgBackground.draw(context);

// text display
fNanoText.beginFrame(this);


Loading…
Cancel
Save