DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit 50ba9251fedc4bc98d77bc7530f62081b7058edf
parent 1e77b5127220429839a07af6019d544dead73bd2
Author: falkTX <falktx@falktx.com>
Date:   Tue,  6 Sep 2022 16:02:40 +0100

More clap things, deal with parameters and UI

Diffstat:
Mdistrho/DistrhoUIMain.cpp | 1-
Mdistrho/src/DistrhoPluginCLAP.cpp | 583++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mdistrho/src/clap/ext/gui.h | 1+
3 files changed, 434 insertions(+), 151 deletions(-)

diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp @@ -20,7 +20,6 @@ # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 #elif defined(DISTRHO_PLUGIN_TARGET_CLAP) # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 -# include "src/DistrhoUIStub.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_JACK) # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 #elif defined(DISTRHO_PLUGIN_TARGET_DSSI) diff --git a/distrho/src/DistrhoPluginCLAP.cpp b/distrho/src/DistrhoPluginCLAP.cpp @@ -17,11 +17,9 @@ #include "DistrhoPluginInternal.hpp" #include "extra/ScopedPointer.hpp" -#undef DISTRHO_PLUGIN_HAS_UI -#define DISTRHO_PLUGIN_HAS_UI 0 - #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" +# include "extra/Mutex.hpp" #endif #include "clap/entry.h" @@ -36,6 +34,60 @@ START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- +struct ClapEventQueue +{ + enum EventType { + kEventGestureBegin, + kEventGestureEnd, + kEventParamSet + }; + + struct Event { + EventType type; + uint32_t index; + double value; + }; + + struct Queue { + Mutex lock; + uint allocated; + uint used; + Event* events; + + Queue() + : allocated(0), + used(0), + events(nullptr) {} + + ~Queue() + { + delete[] events; + } + + void addEventFromUI(const Event& event) + { + const MutexLocker cml(lock); + + if (events == nullptr) + { + events = static_cast<Event*>(std::malloc(sizeof(Event) * 8)); + allocated = 8; + } + else if (used + 1 > allocated) + { + allocated = used * 2; + events = static_cast<Event*>(std::realloc(events, sizeof(Event) * allocated)); + } + + std::memcpy(&events[used++], &event, sizeof(Event)); + } + } fEventQueue; + + virtual ~ClapEventQueue() {} +}; + +// -------------------------------------------------------------------------------------------------------------------- + #if ! DISTRHO_PLUGIN_WANT_STATE static constexpr const setStateFunc setStateCallback = nullptr; #endif @@ -49,35 +101,254 @@ static constexpr const sendNoteFunc sendNoteCallback = nullptr; class ClapUI { public: - ClapUI(const intptr_t winId, - const double sampleRate, - const char* const bundlePath, - void* const dspPtr, - const float scaleFactor) - : fUI(this, winId, sampleRate, - editParameterCallback, - setParameterCallback, - setStateCallback, - sendNoteCallback, - setSizeCallback, - fileRequestCallback, - bundlePath, dspPtr, scaleFactor) + ClapUI(PluginExporter& plugin, ClapEventQueue* const eventQueue, const bool isFloating) + : fPlugin(plugin), + fEventQueue(eventQueue->fEventQueue), + fUI(), + fIsFloating(isFloating), + fScaleFactor(0.0), + fParentWindow(0), + fTransientWindow(0) + { + } + + bool setScaleFactor(const double scaleFactor) + { + if (d_isEqual(fScaleFactor, scaleFactor)) + return true; + + fScaleFactor = scaleFactor; + + if (UIExporter* const ui = fUI.get()) + ui->notifyScaleFactorChanged(scaleFactor); + + return true; + } + + bool getSize(uint32_t* const width, uint32_t* const height) const { + if (UIExporter* const ui = fUI.get()) + { + *width = ui->getWidth(); + *height = ui->getHeight(); + return true; + } + + #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) + *width = DISTRHO_UI_DEFAULT_WIDTH; + *height = DISTRHO_UI_DEFAULT_HEIGHT; + #else + UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, + fPlugin.getInstancePointer(), fScaleFactor); + *width = tmpUI.getWidth(); + *height = tmpUI.getHeight(); + tmpUI.quit(); + #endif + + return true; + } + + bool canResize() const noexcept + { + #if DISTRHO_UI_USER_RESIZABLE + if (UIExporter* const ui = fUI.get()) + return ui->isResizable(); + #endif + return false; + } + + bool getResizeHints(clap_gui_resize_hints_t* const hints) const + { + if (canResize()) + { + uint minimumWidth, minimumHeight; + bool keepAspectRatio; + fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); + + hints->can_resize_horizontally = true; + hints->can_resize_vertically = true; + hints->preserve_aspect_ratio = keepAspectRatio; + hints->aspect_ratio_width = minimumWidth; + hints->aspect_ratio_height = minimumHeight; + + return true; + } + + hints->can_resize_horizontally = false; + hints->can_resize_vertically = false; + hints->preserve_aspect_ratio = false; + hints->aspect_ratio_width = 0; + hints->aspect_ratio_height = 0; + + return false; + } + + bool adjustSize(uint32_t* const width, uint32_t* const height) const + { + if (canResize()) + { + uint minimumWidth, minimumHeight; + bool keepAspectRatio; + fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); + + if (keepAspectRatio) + { + if (*width < 1) + *width = 1; + if (*height < 1) + *height = 1; + + const double ratio = static_cast<double>(minimumWidth) / static_cast<double>(minimumHeight); + const double reqRatio = static_cast<double>(*width) / static_cast<double>(*height); + + if (d_isNotEqual(ratio, reqRatio)) + { + // fix width + if (reqRatio > ratio) + *width = static_cast<int32_t>(*height * ratio + 0.5); + // fix height + else + *height = static_cast<int32_t>(static_cast<double>(*width) / ratio + 0.5); + } + } + + if (minimumWidth > *width) + *width = minimumWidth; + if (minimumHeight > *height) + *height = minimumHeight; + + return true; + } + + return false; + } + + bool setSizeFromHost(const uint32_t width, const uint32_t height) + { + if (UIExporter* const ui = fUI.get()) + { + ui->setWindowSizeFromHost(width, height); + return true; + } + + return false; + } + + bool setParent(const clap_window_t* const window) + { + if (fIsFloating) + return false; + + fParentWindow = window->uptr; + + /* + if (fUI != nullptr) + createUI(); + */ + + return true; + } + + bool setTransient(const clap_window_t* const window) + { + if (! fIsFloating) + return false; + + fTransientWindow = window->uptr; + + if (UIExporter* const ui = fUI.get()) + ui->setWindowTransientWinId(window->uptr); + + return true; + } + + void suggestTitle(const char* const title) + { + if (! fIsFloating) + return; + + fWindowTitle = title; + + if (UIExporter* const ui = fUI.get()) + ui->setWindowTitle(title); + } + + bool show() + { + if (fUI == nullptr) + createUI(); + + if (fIsFloating) + fUI->setWindowVisible(true); + + return true; + } + + bool hide() + { + if (UIExporter* const ui = fUI.get()) + ui->setWindowVisible(false); + + return true; } // ---------------------------------------------------------------------------------------------------------------- private: - // Stub stuff here + // Plugin and UI + PluginExporter& fPlugin; + ClapEventQueue::Queue& fEventQueue; + ScopedPointer<UIExporter> fUI; + + const bool fIsFloating; + + // Temporary data + double fScaleFactor; + uintptr_t fParentWindow; + uintptr_t fTransientWindow; + String fWindowTitle; + + // ---------------------------------------------------------------------------------------------------------------- + + void createUI() + { + DISTRHO_SAFE_ASSERT_RETURN(fUI == nullptr,); + + fUI = new UIExporter(this, + fParentWindow, + fPlugin.getSampleRate(), + editParameterCallback, + setParameterCallback, + setStateCallback, + sendNoteCallback, + setSizeCallback, + fileRequestCallback, + d_nextBundlePath, + fPlugin.getInstancePointer(), + fScaleFactor); + + if (fIsFloating) + { + if (fWindowTitle.isNotEmpty()) + fUI->setWindowTitle(fWindowTitle); - // Plugin UI (after Stub stuff so the UI can call into us during its constructor) - UIExporter fUI; + if (fTransientWindow != 0) + fUI->setWindowTransientWinId(fTransientWindow); + } + + } // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks - void editParameter(uint32_t, bool) const + void editParameter(const uint32_t rindex, const bool started) const { + const ClapEventQueue::Event ev = { + started ? ClapEventQueue::kEventGestureBegin : ClapEventQueue::kEventGestureBegin, + rindex, 0.0 + }; + fEventQueue.addEventFromUI(ev); } static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) @@ -85,8 +356,19 @@ private: static_cast<ClapUI*>(ptr)->editParameter(rindex, started); } - void setParameterValue(uint32_t, float) + void setParameterValue(const uint32_t rindex, const float plain) { + double value; + if (fPlugin.isParameterInteger(rindex)) + value = plain; + else + value = fPlugin.getParameterRanges(rindex).getNormalizedValue(static_cast<double>(plain)); + + const ClapEventQueue::Event ev = { + ClapEventQueue::kEventParamSet, + rindex, value + }; + fEventQueue.addEventFromUI(ev); } static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) @@ -94,13 +376,13 @@ private: static_cast<ClapUI*>(ptr)->setParameterValue(rindex, value); } - void setSize(uint, uint) + void setSizeFromPlugin(uint, uint) { } static void setSizeCallback(void* const ptr, const uint width, const uint height) { - static_cast<ClapUI*>(ptr)->setSize(width, height); + static_cast<ClapUI*>(ptr)->setSizeFromPlugin(width, height); } #if DISTRHO_PLUGIN_WANT_STATE @@ -156,6 +438,9 @@ static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; * CLAP plugin class. */ class PluginCLAP +#if DISTRHO_PLUGIN_HAS_UI + : ClapEventQueue +#endif { public: PluginCLAP(const clap_host_t* const host) @@ -194,6 +479,53 @@ public: bool process(const clap_process_t* const process) { + #if DISTRHO_PLUGIN_HAS_UI + if (const clap_output_events_t* const outputEvents = process->out_events) + { + const MutexTryLocker cmtl(fEventQueue.lock); + + if (cmtl.wasLocked()) + { + // reuse the same struct for gesture and parameters, they are compatible up to where it matters + clap_event_param_value_t clapEvent = { + { 0, 0, 0, 0, CLAP_EVENT_IS_LIVE }, + 0, nullptr, 0, 0, 0, 0, 0.0 + }; + + for (uint32_t i=0; i<fEventQueue.used; ++i) + { + const Event& event(fEventQueue.events[i]); + + switch (event.type) + { + case kEventGestureBegin: + clapEvent.header.size = sizeof(clap_event_param_gesture_t); + clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_BEGIN; + clapEvent.param_id = event.index; + break; + case kEventGestureEnd: + clapEvent.header.size = sizeof(clap_event_param_gesture_t); + clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_END; + clapEvent.param_id = event.index; + break; + case kEventParamSet: + clapEvent.header.size = sizeof(clap_event_param_value_t); + clapEvent.header.type = CLAP_EVENT_PARAM_VALUE; + clapEvent.param_id = event.index; + clapEvent.value = event.value; + break; + default: + continue; + } + + outputEvents->try_push(outputEvents, &clapEvent.header); + } + + fEventQueue.used = 0; + } + } + #endif + #if DISTRHO_PLUGIN_WANT_TIMEPOS if (const clap_event_transport_t* const transport = process->transport) { @@ -495,9 +827,9 @@ public: // gui #if DISTRHO_PLUGIN_HAS_UI - bool createUI(const bool floating) + bool createUI(const bool isFloating) { - fUI = new ClapUI(); + fUI = new ClapUI(fPlugin, this, isFloating); return true; } @@ -506,125 +838,19 @@ public: fUI = nullptr; } - bool setScale(const double scale) - { - fUI.scale = scale; - - if (ClapUI* const ui = fUI.instance) - ui->notifyScaleFactorChange(scale); - - return true; - } - - bool getSize(uint32_t* const width, uint32_t* const height) const - { - if (ClapUI* const ui = fUI.instance) - { - *width = ui->getWidth(); - *height = ui->getHeight(); - } - else - { - // TODO - } - return true; - } - - bool canResize() const + ClapUI* getUI() const noexcept { - if (ClapUI* const ui = fUI.instance) - return ui->canResize(); - - return DISTRHO_PLUGIN_IS_UI_USER_RESIZABLE != 0; - } - - bool getResizeHints(clap_gui_resize_hints_t* const hints) const - { - // TODO - return true; - } - - bool adjustSize(uint32_t* const width, uint32_t* const height) const - { - // TODO - return true; - } - - bool setSize(const uint32_t width, const uint32_t height) - { - // TODO - return true; - } - - bool setParent(const clap_window_t* const window) - { - // TODO - - if (ClapUI* const ui = fUI.instance) - { - // TODO - } - - return true; - } - - bool setTransient(const clap_window_t* const window) - { - fUI.transient = window; - - if (ClapUI* const ui = fUI.instance) - ui->setTransient(window); - } - - void suggestTitle(const char* const title) - { - fUI.title = window; - - if (ClapUI* const ui = fUI.instance) - ui->setTitle(title); - } - - bool show() - { - if (fUI.instance == nullptr) - fUI.instance = new ClapUI(); - - fUI.instance->show(); - return true; - } - - bool hide() - { - if (ClapUI* const ui = fUI.instance) - ui->hide(); - return true; + return fUI.get(); } #endif // ---------------------------------------------------------------------------------------------------------------- private: - // Plugin + // Plugin and UI PluginExporter fPlugin; - #if DISTRHO_PLUGIN_HAS_UI - // UI - struct UI { - double scale; - uint32_t hostSetWidth, hostSetHeight; - clap_window_t parent, transient; - String title; - ScopedPointer<ClapUI> instance; - - UI() - : scale(0.0), - hostSetWidth(0), - hostSetHeight(0), - parent(0), - transient(0), - title(), - instance() {} - } fUI; + ScopedPointer<ClapUI> fUI; #endif // CLAP stuff @@ -682,20 +908,49 @@ static ScopedPointer<PluginExporter> sPlugin; // plugin gui #if DISTRHO_PLUGIN_HAS_UI -static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, const bool is_floating) + +static const char* const kSupportedAPIs[] = { +#if defined(DISTRHO_OS_WINDOWS) + CLAP_WINDOW_API_WIN32, +#elif defined(DISTRHO_OS_MAC) + CLAP_WINDOW_API_COCOA, +#else + CLAP_WINDOW_API_X11, +#endif +}; + +// TODO DPF external UI +static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool) { - return true; + for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i) + { + if (std::strcmp(kSupportedAPIs[i], api) == 0) + return true; + } + + return false; } +// TODO DPF external UI static bool clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating) { + *api = kSupportedAPIs[0]; + *is_floating = false; return true; } static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating) { - PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); - return instance->createUI(); + for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i) + { + if (std::strcmp(kSupportedAPIs[i], api) == 0) + { + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + return instance->createUI(is_floating); + } + } + + return false; } static void clap_gui_destroy(const clap_plugin_t* const plugin) @@ -707,61 +962,89 @@ static void clap_gui_destroy(const clap_plugin_t* const plugin) static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale) { PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); - UICLAP* const gui = instance->getUI(); - return gui->setScale(scale); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setScaleFactor(scale); } static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) { PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); - UICLAP* const gui = instance->getUI(); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); return gui->getSize(width, height); } static bool clap_gui_can_resize(const clap_plugin_t* const plugin) { PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); - UICLAP* const gui = instance->getUI(); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); return gui->canResize(); } static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints) { - return true; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->getResizeHints(hints); } static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) { - return true; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->adjustSize(width, height); } static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height) { - return true; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setSizeFromHost(width, height); } static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window) { - return true; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setParent(window); } static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window) { - return true; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->setTransient(window); } static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title) { + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,); + return gui->suggestTitle(title); } static bool clap_gui_show(const clap_plugin_t* const plugin) { - return true; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->show(); } static bool clap_gui_hide(const clap_plugin_t* const plugin) { - return true; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + ClapUI* const gui = instance->getUI(); + DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); + return gui->hide(); } static const clap_plugin_gui_t clap_plugin_gui = { diff --git a/distrho/src/clap/ext/gui.h b/distrho/src/clap/ext/gui.h @@ -77,6 +77,7 @@ typedef struct clap_window { clap_xwnd x11; clap_hwnd win32; void *ptr; // for anything defined outside of clap + uintptr_t uptr; }; } clap_window_t;