From 7d3679bf768522ff935ff4f618eef7e2e0a85e45 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 7 Aug 2011 20:23:18 +0100 Subject: [PATCH] Massive UI work --- demo.lv2/Juce_Demo_Plugin.ttl | 5 + .../audio/plugin_client/LV2/juce_LV2_Wrapper.cpp | 406 +++++++++++++++++++-- 2 files changed, 379 insertions(+), 32 deletions(-) diff --git a/demo.lv2/Juce_Demo_Plugin.ttl b/demo.lv2/Juce_Demo_Plugin.ttl index 40bb197..61fb5ec 100644 --- a/demo.lv2/Juce_Demo_Plugin.ttl +++ b/demo.lv2/Juce_Demo_Plugin.ttl @@ -3,8 +3,13 @@ @prefix lv2ev: . @prefix lv2ui: . + + a lv2ui:external ; + lv2ui:binary . + a lv2:Plugin ; + lv2ui:ui ; lv2:port [ a lv2:InputPort, lv2ev:EventPort; diff --git a/juce/source/src/audio/plugin_client/LV2/juce_LV2_Wrapper.cpp b/juce/source/src/audio/plugin_client/LV2/juce_LV2_Wrapper.cpp index fdf1d58..f765c03 100644 --- a/juce/source/src/audio/plugin_client/LV2/juce_LV2_Wrapper.cpp +++ b/juce/source/src/audio/plugin_client/LV2/juce_LV2_Wrapper.cpp @@ -293,7 +293,7 @@ void generate_ttl() //============================================================================== BEGIN_JUCE_NAMESPACE - extern void juce_callAnyTimersSynchronously(); + //extern void juce_callAnyTimersSynchronously(); #if JUCE_MAC extern void initialiseMac(); @@ -349,9 +349,7 @@ static Array activePlugins; //============================================================================== // Create a new JUCE LV2 Plugin -class JuceLV2Wrapper : - private Timer, - public AudioProcessorListener +class JuceLV2Wrapper : private Timer { public: JuceLV2Wrapper(const LV2_Descriptor* descriptor_, double sample_rate_, const LV2_Feature* const* features) : @@ -690,11 +688,13 @@ public: { stopTimer(); - //if (descriptor) - //{ - // free((void*)descriptor->URI); - // delete descriptor; - //} +#if 0 + if (descriptor) + { + free((void*)descriptor->URI); + delete descriptor; + } +#endif } //============================================================================== @@ -702,16 +702,6 @@ public: AudioProcessor* getFilter() { return filter; } - void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) - { - // TODO - } - - void audioProcessorChanged (AudioProcessor*) - { - // TODO - } - //============================================================================== int32_t getChunk (void** data) { @@ -753,18 +743,6 @@ public: } } - void doIdleCallback() - { - if (MessageManager::getInstance()->isThisTheMessageThread()) - { - JUCE_AUTORELEASEPOOL - juce_callAnyTimersSynchronously(); - - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); - } - } - //============================================================================== private: AudioProcessor* filter; @@ -805,6 +783,279 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLV2Wrapper); }; +class JuceLv2ExternalUI; +static Array activeExternalUIs; + +enum JUCE_EXTERNAL_UI_Flags { + JUCE_EXTERNAL_UI_NONE = 0, + JUCE_EXTERNAL_UI_RUN = 0x1, + JUCE_EXTERNAL_UI_SHOW = 0x2, + JUCE_EXTERNAL_UI_HIDE = 0x4 +}; + +//============================================================================== +// Create a new JUCE External UI +class JuceLv2ExternalUI : private Timer, + public DocumentWindow +{ +public: + JuceLv2ExternalUI (JuceLV2Wrapper* const wrapper_, AudioProcessorEditor* const editor_, lv2_external_ui_host* external_ui_host_, LV2UI_Controller controller_) : + DocumentWindow("", Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton, true), + wrapper(wrapper_), + editor(editor_), + external_ui_host(external_ui_host_), + controller(controller_), + do_ui_flags(JUCE_EXTERNAL_UI_NONE) + { + setOpaque (true); + editor->setOpaque (true); + + String title = external_ui_host->plugin_human_id != nullptr ? String(external_ui_host->plugin_human_id) : wrapper->getFilter()->getName(); + setName(title); + + setTitleBarHeight (0); + setDropShadowEnabled(false); + setUsingNativeTitleBar(true); + + setContentNonOwned(editor, true); + + external_ui.run = do_external_ui_run; + external_ui.show = do_external_ui_show; + external_ui.hide = do_external_ui_hide; + + activeExternalUIs.add(this); + + startTimer(200); + } + + ~JuceLv2ExternalUI() + { + activeExternalUIs.removeValue(this); + } + + AudioProcessorEditor* getEditorComp() const + { + return dynamic_cast (getContentComponent()); + } + + void closeButtonPressed() + { + delete this; + external_ui_host->ui_closed(controller); + } + + void timerCallback() + { + if (do_ui_flags & JUCE_EXTERNAL_UI_SHOW) + setVisible(true); + + if (do_ui_flags & JUCE_EXTERNAL_UI_HIDE) + setVisible(false); + + if (do_ui_flags & JUCE_EXTERNAL_UI_RUN) + { + editor->repaint(); + repaint(); + } + + do_ui_flags = JUCE_EXTERNAL_UI_NONE; + } + + //============================================================================== + static void do_external_ui_run(lv2_external_ui* _this_) + { + for (int i = 0; i < activeExternalUIs.size(); i++) + { + JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i); + if (Lv2UI->getLv2Widget() == _this_) + { + Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_RUN); + break; + } + } + } + + static void do_external_ui_show(lv2_external_ui* _this_) + { + for (int i = 0; i < activeExternalUIs.size(); i++) + { + JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i); + if (Lv2UI->getLv2Widget() == _this_) + { + Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_SHOW); + break; + } + } + } + + static void do_external_ui_hide(lv2_external_ui* _this_) + { + for (int i = 0; i < activeExternalUIs.size(); i++) + { + JuceLv2ExternalUI* Lv2UI = (JuceLv2ExternalUI*)activeExternalUIs.getUnchecked(i); + if (Lv2UI->getLv2Widget() == _this_) + { + Lv2UI->setUiFlag(JUCE_EXTERNAL_UI_HIDE); + break; + } + } + } + + LV2UI_Widget getLv2Widget() + { + return &external_ui; + } + + void setUiFlag(JUCE_EXTERNAL_UI_Flags flags) + { + do_ui_flags = flags; + } + +private: + JuceLV2Wrapper* wrapper; + AudioProcessorEditor* editor; + + lv2_external_ui external_ui; + lv2_external_ui_host* external_ui_host; + LV2UI_Controller controller; + + JUCE_EXTERNAL_UI_Flags do_ui_flags; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUI); +}; + +//============================================================================== +// Create a new JUCE Editor +class JuceLv2Editor : public AudioProcessorListener +{ +public: + JuceLv2Editor (JuceLV2Wrapper* const wrapper_, const LV2UI_Descriptor* ui_descriptor_, LV2UI_Write_Function write_function_, LV2UI_Controller controller_, LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternalUI) : + wrapper(wrapper_), + externalUI(nullptr), + ui_descriptor(ui_descriptor_), + write_function(write_function_), + controller(controller_), + editor(nullptr) + { + filter = wrapper->getFilter(); + filter->addListener(this); + + if (filter != nullptr && filter->hasEditor()) + { + printf("TEST - Before\n"); + editor = filter->createEditorIfNeeded(); + printf("TEST - After\n"); + } + + if (editor != nullptr) + { + if (isExternalUI) + { + // External UI + lv2_external_ui_host* external_ui_host = nullptr; + + for (uint16_t j = 0; features[j]; j++) + { + if (strcmp(features[j]->URI, LV2_EXTERNAL_UI_URI) == 0 && features[j]->data != nullptr) + { + external_ui_host = (lv2_external_ui_host*)features[j]->data; + break; + } + } + + if (external_ui_host) + { + externalUI = new JuceLv2ExternalUI(wrapper, editor, external_ui_host, controller); + *widget = externalUI->getLv2Widget(); + } + else + { + widget = nullptr; + std::cerr << "Failed to init external UI" << std::cout; + } + } + else + { + // JUCE UI + editor->setOpaque (true); + editor->setVisible (true); + *widget = editor; + } + } + else + { + widget = nullptr; + std::cerr << "Failed to init UI" << std::cout; + } + + // Padding for control ports + ctrl_pad = 0; +#if JucePlugin_WantsMidiInput + ctrl_pad += 1; +#endif +#if JucePlugin_ProducesMidiOutput + ctrl_pad += 1; +#endif + ctrl_pad += JucePlugin_MaxNumInputChannels; + ctrl_pad += JucePlugin_MaxNumOutputChannels; + } + + ~JuceLv2Editor() + { + JUCE_AUTORELEASEPOOL + PopupMenu::dismissAllActiveMenus(); + + filter->removeListener(this); + filter->editorBeingDeleted (editor); + + if (externalUI != nullptr) + delete externalUI; + + //if (editor != nullptr) + // deleteAndZero (editor); + } + + void do_port_event(uint32_t port_index, float value) + { + filter->setParameter(port_index-ctrl_pad, value); + } + + void do_cleanup() + { + // We should only do this after UI gets working +#if 0 + if (ui_descriptor) + { + free((void*)ui_descriptor->URI); + delete ui_descriptor; + } +#endif + } + + //============================================================================== + void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) + { + if (controller && write_function) + write_function(controller, index+ctrl_pad, sizeof(float), 0, &newValue); + } + + void audioProcessorChanged (AudioProcessor*) {} + +private: + JuceLV2Wrapper* wrapper; + JuceLv2ExternalUI* externalUI; + AudioProcessor* filter; + AudioProcessorEditor* editor; + + const LV2UI_Descriptor* ui_descriptor; + LV2UI_Write_Function write_function; + LV2UI_Controller controller; + + int32_t ctrl_pad; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Editor); +}; + //============================================================================== // LV2 descriptor functions LV2_Handle juce_lv2_instantiate(const LV2_Descriptor* descriptor, double sample_rate, const char* bundle_path, const LV2_Feature* const* features) @@ -851,11 +1102,60 @@ const void* juce_lv2_extension_data(const char* uri) } //============================================================================== +LV2UI_Handle juce_lv2ui_instantiate(const LV2UI_Descriptor* descriptor, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternalUI) +{ + const MessageManagerLock mmLock; + + for (uint16_t i = 0; features[i]; i++) + { + if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data != nullptr) + { + JuceLV2Wrapper* wrapper = (JuceLV2Wrapper*)features[i]->data; + JuceLv2Editor* editor = new JuceLv2Editor(wrapper, descriptor, write_function, controller, widget, features, isExternalUI); + return editor; + } + } + + std::cerr << "Host does not support instance data - cannot use UI" << std::cout; + return nullptr; +} + +LV2UI_Handle juce_lv2ui_instantiate_JUCE(const LV2UI_Descriptor* descriptor, const char* plugin_uri, const char* bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + return juce_lv2ui_instantiate(descriptor, write_function, controller, widget, features, false); +} + +LV2UI_Handle juce_lv2ui_instantiate_external(const LV2UI_Descriptor* descriptor, const char* plugin_uri, const char* bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + return juce_lv2ui_instantiate(descriptor, write_function, controller, widget, features, true); +} + +void juce_lv2ui_port_event(LV2UI_Handle instance, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer) +{ + const MessageManagerLock mmLock; + JuceLv2Editor* editor = (JuceLv2Editor*)instance; + + if (buffer_size == sizeof(float) && format == 0) + { + float value = *(float*)buffer; + editor->do_port_event(port_index, value); + } +} + +void juce_lv2ui_cleanup(LV2UI_Handle instance) +{ + const MessageManagerLock mmLock; + JuceLv2Editor* editor = (JuceLv2Editor*)instance; + editor->do_cleanup(); + delete editor; +} + +//============================================================================== // Create new LV2 objects LV2_Descriptor* getNewLv2Plugin() { LV2_Descriptor* const Lv2Plugin = new LV2_Descriptor; - Lv2Plugin->URI = strdup((const char*)get_uri().toUTF8()); + Lv2Plugin->URI = strdup((const char*) get_uri().toUTF8()); Lv2Plugin->instantiate = juce_lv2_instantiate; Lv2Plugin->connect_port = juce_lv2_connect_port; Lv2Plugin->activate = juce_lv2_activate; @@ -866,6 +1166,33 @@ LV2_Descriptor* getNewLv2Plugin() return Lv2Plugin; } +LV2UI_Descriptor* getNewLv2UI_JUCE() +{ + LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor; + Lv2UI->URI = strdup((const char*) get_juce_ui_uri().toUTF8()); + Lv2UI->instantiate = juce_lv2ui_instantiate_JUCE; + Lv2UI->cleanup = juce_lv2ui_cleanup; + Lv2UI->port_event = juce_lv2ui_port_event; + Lv2UI->extension_data = juce_lv2_extension_data; + return Lv2UI; +} + +LV2UI_Descriptor* getNewLv2UI_External() +{ + LV2UI_Descriptor* Lv2UI = new LV2UI_Descriptor; + Lv2UI->URI = strdup((const char*) get_external_ui_uri().toUTF8()); + Lv2UI->instantiate = juce_lv2ui_instantiate_external; + Lv2UI->cleanup = juce_lv2ui_cleanup; + Lv2UI->port_event = juce_lv2ui_port_event; + Lv2UI->extension_data = juce_lv2_extension_data; + return Lv2UI; +} + +LV2UI_Descriptor* getNewLv2UI(bool external) +{ + return external ? getNewLv2UI_External() : getNewLv2UI_JUCE(); +} + //============================================================================== // Mac startup code.. #if JUCE_MAC @@ -881,6 +1208,11 @@ LV2_Descriptor* getNewLv2Plugin() return (index == 0) ? getNewLv2Plugin() : nullptr; } + extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) + { + return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr; + } + //============================================================================== // Linux startup code.. #elif JUCE_LINUX @@ -896,6 +1228,11 @@ LV2_Descriptor* getNewLv2Plugin() return (index == 0) ? getNewLv2Plugin() : nullptr; } + extern "C" __attribute__ ((visibility("default"))) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) + { + return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr; + } + // don't put initialiseJuce_GUI or shutdownJuce_GUI in these... it will crash! __attribute__((constructor)) void myPluginInit() {} __attribute__((destructor)) void myPluginFini() {} @@ -914,6 +1251,11 @@ LV2_Descriptor* getNewLv2Plugin() return (index == 0) ? getNewLv2Plugin() : nullptr; } + extern "C" __declspec (dllexport) const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) + { + return (index <= 1) ? getNewLv2UI((index == 0)) : nullptr; + } + #endif #endif -- 2.11.4.GIT