/* * DISTRHO Plugin Toolkit (DPT) * Copyright (C) 2012-2013 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * For a full copy of the license see the LGPL.txt file */ #include "DistrhoDefines.h" #if defined(DISTRHO_PLUGIN_TARGET_LV2) && DISTRHO_PLUGIN_HAS_UI #include "DistrhoUIInternal.h" #include "lv2-sdk/lv2.h" //#include "lv2-sdk/atom.h" //#include "lv2-sdk/atom-util.h" //#include "lv2-sdk/midi.h" #include "lv2-sdk/patch.h" #include "lv2-sdk/programs.h" //#include "lv2-sdk/state.h" #include "lv2-sdk/urid.h" #include "lv2-sdk/ui.h" #include #include #include #ifdef DISTRHO_UI_QT4 # include #endif #ifndef DISTRHO_PLUGIN_URI # error DISTRHO_PLUGIN_URI undefined! #endif #define DISTRHO_LV2UI_USE_EXTENSION_DATA (DISTRHO_PLUGIN_WANT_PROGRAMS) // ------------------------------------------------- START_NAMESPACE_DISTRHO #ifdef DISTRHO_UI_QT4 class UILv2 : public QObject #else class UILv2 #endif { public: UILv2(intptr_t parent, LV2UI_Controller controller, LV2UI_Write_Function writeFunction, LV2UI_Widget* widget, const LV2_Feature* const* features) : ui(this, parent, setParameterCallback, setStateCallback, uiNoteCallback, uiResizeCallback), lv2Controller(controller), lv2WriteFunction(writeFunction), lv2UiResize(nullptr), #if DISTRHO_PLUGIN_WANT_STATE uridIdPatchMessage(0), #endif #ifdef DISTRHO_UI_QT4 uiTimer(0) #else threadExitNow(false), threadRunning(false), thread(uiThreadCallback, this) #endif { // Get Features for (uint32_t i = 0; features[i]; i++) { if (strcmp(features[i]->URI, LV2_URID__map) == 0) { #if DISTRHO_PLUGIN_WANT_STATE LV2_URID_Map* uridMap = (LV2_URID_Map*)features[i]->data; uridIdPatchMessage = uridMap->map(uridMap->handle, LV2_PATCH__Message); #endif } else if (strcmp(features[i]->URI, LV2_UI__resize) == 0) lv2UiResize = (LV2UI_Resize*)features[i]->data; } #ifndef DISTRHO_UI_QT4 assert(parent); if (parent == 0) return; #endif if (lv2UiResize) lv2UiResize->ui_resize(lv2UiResize->handle, ui.getWidth(), ui.getHeight()); #ifdef DISTRHO_UI_QT4 uiTimer = startTimer(30); #endif *widget = (void*)ui.getWindowId(); } ~UILv2() { #ifdef DISTRHO_UI_QT4 if (uiTimer) killTimer(uiTimer); #else uiThreadClose(); #endif } // --------------------------------------------- void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) { if (format == 0) { if (bufferSize != sizeof(float)) return; if (int32_t(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS) < 0) return; float value = *(float*)buffer; ui.parameterChanged(portIndex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS, value); } // TODO - atom events } #if DISTRHO_PLUGIN_WANT_PROGRAMS void lv2ui_select_program(uint32_t bank, uint32_t program) { const uint32_t index = bank * 128 + program; ui.programChanged(index); } #endif // --------------------------------------------- protected: void setParameterValue(uint32_t index, float value) { if (lv2WriteFunction) lv2WriteFunction(lv2Controller, DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + index, sizeof(float), 0, &value); } void setState(const char* key, const char* value) { // TODO (void)key; (void)value; //if (lv2WriteFunction && uridMap) { } } void uiResize(unsigned int width, unsigned int height) { if (lv2UiResize) lv2UiResize->ui_resize(lv2UiResize->handle, width, height); } #ifdef DISTRHO_UI_QT4 void timerEvent(QTimerEvent* event) { if (event->timerId() == uiTimer) ui.idle(); QObject::timerEvent(event); } #else void uiThreadRun() { threadRunning = true; while (! threadExitNow) { ui.idle(); d_msleep(1000 / 25); // 25 FPS } thread.detach(); threadRunning = false; } void uiThreadClose() { threadExitNow = true; while (threadRunning) d_msleep(1000 / 25); // 25 FPS } #endif private: UIInternal ui; // LV2 Stuff LV2UI_Controller const lv2Controller; LV2UI_Write_Function const lv2WriteFunction; const LV2UI_Resize* lv2UiResize; #if DISTRHO_PLUGIN_WANT_STATE LV2_URID uridIdPatchMessage; #endif #ifdef DISTRHO_UI_QT4 int uiTimer; #else // UI Thread bool threadExitNow; bool threadRunning; std::thread thread; #endif // --------------------------------------------- // Callbacks static void setParameterCallback(void* ptr, uint32_t index, float value) { UILv2* _this_ = (UILv2*)ptr; assert(_this_); _this_->setParameterValue(index, value); } static void setStateCallback(void* ptr, const char* key, const char* value) { #if DISTRHO_PLUGIN_WANT_STATE UILv2* _this_ = (UILv2*)ptr; assert(_this_); _this_->setState(key, value); #else // unused (void)ptr; (void)key; (void)value; #endif } static void uiNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) { #if DISTRHO_PLUGIN_IS_SYNTH UILv2* _this_ = (UILv2*)ptr; assert(_this_); _this_->uiNote(onOff, channel, note, velocity); #else // unused (void)ptr; (void)onOff; (void)channel; (void)note; (void)velocity; #endif } static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) { UILv2* _this_ = (UILv2*)ptr; assert(_this_); _this_->uiResize(width, height); } #ifndef DISTRHO_UI_QT4 static void uiThreadCallback(void* ptr) { UILv2* _this_ = (UILv2*)ptr; assert(_this_); _this_->uiThreadRun(); } #endif }; // ------------------------------------------------- static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char*, LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) { if (strcmp(uri, DISTRHO_PLUGIN_URI) != 0) return nullptr; // Get parent intptr_t parent = 0; for (uint32_t i = 0; features[i]; i++) { if (strcmp(features[i]->URI, LV2_UI__parent) == 0) { parent = (intptr_t)features[i]->data; break; } } return new UILv2(parent, controller, writeFunction, widget, features); } static void lv2ui_cleanup(LV2UI_Handle instance) { UILv2* ui = (UILv2*)instance; assert(ui); delete ui; } static void lv2ui_port_event(LV2UI_Handle instance, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) { UILv2* ui = (UILv2*)instance; assert(ui); ui->lv2ui_port_event(portIndex, bufferSize, format, buffer); } #if DISTRHO_LV2UI_USE_EXTENSION_DATA # if DISTRHO_PLUGIN_WANT_PROGRAMS static void lv2ui_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) { UILv2* ui = (UILv2*)instance; assert(ui); ui->lv2ui_select_program(bank, program); } # endif static const void* lv2ui_extension_data(const char* uri) { # if DISTRHO_PLUGIN_WANT_PROGRAMS static const LV2_Programs_UI_Interface programs = { lv2ui_select_program }; if (strcmp(uri, LV2_PROGRAMS__UIInterface) == 0) return &programs; # endif return nullptr; } #endif // ------------------------------------------------- static LV2UI_Descriptor uidescriptor = { DISTRHO_UI_URI, lv2ui_instantiate, lv2ui_cleanup, lv2ui_port_event, #if DISTRHO_LV2UI_USE_EXTENSION_DATA lv2ui_extension_data #else /* extension_data */ nullptr #endif }; END_NAMESPACE_DISTRHO // ------------------------------------------------- DISTRHO_PLUGIN_EXPORT const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) { USE_NAMESPACE_DISTRHO return (index == 0) ? &uidescriptor : nullptr; } // ------------------------------------------------- #endif // DISTRHO_PLUGIN_TARGET_LV2 && DISTRHO_PLUGIN_HAS_UI