View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0002777 | ardour | bugs | public | 2009-07-12 00:37 | 2020-04-19 20:14 |
Reporter | nedko | Assigned To | paul | ||
Priority | normal | Severity | crash | Reproducibility | always |
Status | closed | Resolution | fixed | ||
Product Version | 2.8.1 | ||||
Target Version | 2.8.3 | ||||
Summary | 0002777: [PATCH] missing support for lv2 external ui extension | ||||
Description | ATM two plugins use this extension. lv2fil and lv2nekobee. Only the former is loadable in ardour2 because lv2nekobee is an instrument/synth. | ||||
Additional Information | The attached patch fixes the issue for me. | ||||
Tags | No tags attached. | ||||
2009-07-12 00:37
|
ardour-2.8.1-lv2-external-ui.patch (17,499 bytes)
diff --git a/gtk2_ardour/lv2_external_ui.h b/gtk2_ardour/lv2_external_ui.h new file mode 100644 index 0000000..242271e --- /dev/null +++ b/gtk2_ardour/lv2_external_ui.h @@ -0,0 +1,101 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/***************************************************************************** + * + * This work is in public domain. + * + * This file 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. + * + * If you have questions, contact Nedko Arnaudov <nedko@arnaudov.name> or + * ask in #lad channel, FreeNode IRC network. + * + *****************************************************************************/ + +#ifndef LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED +#define LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED + +/** UI extension suitable for out-of-process UIs */ +#define LV2_EXTERNAL_UI_URI "http://lv2plug.in/ns/extensions/ui#external" + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* Adjust editor indent */ +#endif + +/** + * When LV2_EXTERNAL_UI_URI UI is instantiated, the returned + * LV2UI_Widget handle must be cast to pointer to struct lv2_external_ui. + * UI is created in invisible state. + */ +struct lv2_external_ui +{ + /** + * Host calls this function regulary. UI library implementing the + * callback may do IPC or redraw the UI. + * + * @param _this_ the UI context + */ + void (* run)(struct lv2_external_ui * _this_); + + /** + * Host calls this function to make the plugin UI visible. + * + * @param _this_ the UI context + */ + void (* show)(struct lv2_external_ui * _this_); + + /** + * Host calls this function to make the plugin UI invisible again. + * + * @param _this_ the UI context + */ + void (* hide)(struct lv2_external_ui * _this_); +}; + +#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) +#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) +#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) + +/** + * On UI instantiation, host must supply LV2_EXTERNAL_UI_URI + * feature. LV2_Feature::data must be pointer to struct lv2_external_ui_host. */ +struct lv2_external_ui_host +{ + /** + * Callback that plugin UI will call + * when UI (GUI window) is closed by user. + * This callback wil; be called during execution of lv2_external_ui::run() + * (i.e. not from background thread). + * + * After this callback is called, UI is defunct. Host must call + * LV2UI_Descriptor::cleanup(). If host wants to make the UI visible + * again UI must be reinstantiated. + * + * @param controller Host context associated with plugin UI, as + * supplied to LV2UI_Descriptor::instantiate() + */ + void (* ui_closed)(LV2UI_Controller controller); + + /** + * Optional (may be NULL) "user friendly" identifier which the UI + * may display to allow a user to easily associate this particular + * UI instance with the correct plugin instance as it is represented + * by the host (e.g. "track 1" or "channel 4"). + * + * If supplied by host, the string will be referenced only during + * LV2UI_Descriptor::instantiate() + */ + const char * plugin_human_id; +}; + +#if 0 +{ /* Adjust editor indent */ +#endif +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED */ diff --git a/gtk2_ardour/lv2_plugin_ui.cc b/gtk2_ardour/lv2_plugin_ui.cc index 26a7074..7fb2c87 100644 --- a/gtk2_ardour/lv2_plugin_ui.cc +++ b/gtk2_ardour/lv2_plugin_ui.cc @@ -28,6 +28,22 @@ using namespace Gtk; using namespace ARDOUR; using namespace PBD; +std::vector<struct lv2_external_ui*> g_external_uis; + +void close_external_ui_windows() +{ + struct lv2_external_ui* external_ui_ptr; + + //cout << "close_external_ui_windows" << endl; + + while (!g_external_uis.empty()) { + //cout << "pop" << endl; + external_ui_ptr = g_external_uis.back(); + LV2_EXTERNAL_UI_HIDE(external_ui_ptr); + g_external_uis.pop_back(); + } +} + void LV2PluginUI::lv2_ui_write(LV2UI_Controller controller, uint32_t port_index, @@ -35,14 +51,36 @@ LV2PluginUI::lv2_ui_write(LV2UI_Controller controller, uint32_t format, const void* buffer) { + //cout << "lv2_ui_write" << endl; LV2PluginUI* me = (LV2PluginUI*)controller; - if (*(float*)buffer != me->_values[port_index]) + if (*(float*)buffer != me->_values[port_index]) { + //cout << "set_parameter " << port_index << ":" << *(float*)buffer << endl; me->_lv2->set_parameter(port_index, *(float*)buffer); + } +} + +void LV2PluginUI::on_external_ui_closed(LV2UI_Controller controller) +{ + //cout << "on_external_ui_closed" << endl; + + LV2PluginUI* me = (LV2PluginUI*)controller; + me->_screen_update_connection.disconnect(); + //me->insert->set_gui(0); + + for (vector<struct lv2_external_ui*>::iterator it = g_external_uis.begin() ; it < g_external_uis.end(); it++) { + if (*it == me->_external_ui_ptr) { + g_external_uis.erase(it); + } + } + + //slv2_ui_instance_get_descriptor(me->_inst)->cleanup(me->_inst); + me->_external_ui_ptr = NULL; } void LV2PluginUI::parameter_changed (uint32_t port_index, float val) { + //cout << "parameter_changed" << endl; if (val != _values[port_index]) { parameter_update(port_index, val); } @@ -51,16 +89,24 @@ LV2PluginUI::parameter_changed (uint32_t port_index, float val) void LV2PluginUI::parameter_update (uint32_t port_index, float val) { + if (!_inst) { + return; + } + const LV2UI_Descriptor* ui_desc = slv2_ui_instance_get_descriptor(_inst); LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(_inst); - if (ui_desc->port_event) + if (ui_desc->port_event) { + //cout << "port_event " << port_index << ":" << val << endl; ui_desc->port_event(ui_handle, port_index, 4, 0, &val); + } _values[port_index] = val; } bool LV2PluginUI::start_updating(GdkEventAny* event) { + //cout << "start_updating" << endl; + if (!_output_ports.empty()) { _screen_update_connection.disconnect(); _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect @@ -72,7 +118,10 @@ LV2PluginUI::start_updating(GdkEventAny* event) bool LV2PluginUI::stop_updating(GdkEventAny* event) { - if (!_output_ports.empty()) { + //cout << "stop_updating" << endl; + + if (//!_external_ui_ptr && + !_output_ports.empty()) { _screen_update_connection.disconnect(); } return false; @@ -81,6 +130,11 @@ LV2PluginUI::stop_updating(GdkEventAny* event) void LV2PluginUI::output_update() { + //cout << "output_update" << endl; + if (_external_ui_ptr) { + LV2_EXTERNAL_UI_RUN(_external_ui_ptr); + } + /* FIXME only works with control output ports (which is all we support now anyway) */ uint32_t nports = _output_ports.size(); for (uint32_t i = 0; i < nports; ++i) { @@ -93,30 +147,84 @@ LV2PluginUI::output_update() LV2PluginUI::LV2PluginUI (boost::shared_ptr<PluginInsert> pi, boost::shared_ptr<LV2Plugin> lv2p) : PlugUIBase (pi) , _lv2(lv2p) + , _inst(NULL) + , _values(NULL) + , _external_ui_ptr(NULL) +{ + if (!_lv2->is_external_ui()) { + lv2ui_instantiate("gtk2gui"); + } +} + +void +LV2PluginUI::lv2ui_instantiate(const Glib::ustring& title) { + LV2_Feature** features; + LV2_Feature** features_src; + LV2_Feature** features_dst; + size_t features_count; + bool is_external_ui; + + is_external_ui = _lv2->is_external_ui(); + + if (is_external_ui) { + _external_ui_host.ui_closed = LV2PluginUI::on_external_ui_closed; + _external_ui_host.plugin_human_id = strdup(title.c_str()); + + _external_ui_feature.URI = LV2_EXTERNAL_UI_URI; + _external_ui_feature.data = &_external_ui_host; + + features_src = features = (LV2_Feature**)_lv2->features(); + features_count = 2; + while (*features++) { + features_count++; + } + + features_dst = features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * features_count); + features_dst[--features_count] = NULL; + features_dst[--features_count] = &_external_ui_feature; + while (features_count--) { + *features++ = *features_src++; + } + } else { + features_dst = (LV2_Feature**)_lv2->features(); + } + _inst = slv2_ui_instantiate( _lv2->slv2_plugin(), _lv2->slv2_ui(), LV2PluginUI::lv2_ui_write, this, - _lv2->features()); + features_dst); + + if (is_external_ui) { + free(features_dst); + } - uint32_t num_ports = slv2_plugin_get_num_ports(lv2p->slv2_plugin()); + uint32_t num_ports = slv2_plugin_get_num_ports(_lv2->slv2_plugin()); for (uint32_t i = 0; i < num_ports; ++i) { - if (lv2p->parameter_is_output(i) && lv2p->parameter_is_control(i) && is_update_wanted(i)) { + if (_lv2->parameter_is_output(i) && _lv2->parameter_is_control(i) && is_update_wanted(i)) { _output_ports.push_back(i); } } - - GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_inst); - _gui_widget = Glib::wrap(c_widget); - _gui_widget->show_all(); - pack_start(*_gui_widget, true, true); + + _external_ui_ptr = NULL; + if (_inst) { + if (!is_external_ui) { + GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_inst); + _gui_widget = Glib::wrap(c_widget); + _gui_widget->show_all(); + pack_start(*_gui_widget, true, true); + } else { + _external_ui_ptr = (struct lv2_external_ui *)slv2_ui_instance_get_widget(_inst); + g_external_uis.push_back(_external_ui_ptr); + } + } _values = new float[num_ports]; for (uint32_t i = 0; i < num_ports; ++i) { bool ok; - uint32_t port = lv2p->nth_parameter(i, ok); + uint32_t port = _lv2->nth_parameter(i, ok); if (ok) { - _values[port] = lv2p->get_parameter(port); - if (lv2p->parameter_is_control(port) && lv2p->parameter_is_input(port)) { + _values[port] = _lv2->get_parameter(port); + if (_lv2->parameter_is_control(port) && _lv2->parameter_is_input(port)) { parameter_update(port, _values[port]); } } @@ -127,8 +235,12 @@ LV2PluginUI::LV2PluginUI (boost::shared_ptr<PluginInsert> pi, boost::shared_ptr< LV2PluginUI::~LV2PluginUI () { - delete[] _values; - // plugin destructor destroys the GUI + //cout << "LV2PluginUI destructor called" << endl; + + if (_values) { + delete[] _values; + } + // plugin destructor destroys the GTK GUI } int @@ -148,10 +260,15 @@ LV2PluginUI::get_preferred_width () int LV2PluginUI::package (Gtk::Window& win) { - /* forward configure events to plugin window */ - win.signal_configure_event().connect (mem_fun (*this, &LV2PluginUI::configure_handler)); - win.signal_map_event().connect (mem_fun (*this, &LV2PluginUI::start_updating)); - win.signal_unmap_event().connect (mem_fun (*this, &LV2PluginUI::stop_updating)); + //cout << "package" << endl; + if (_external_ui_ptr) { + _win_ptr = &win; + } else { + /* forward configure events to plugin window */ + win.signal_configure_event().connect (mem_fun (*this, &LV2PluginUI::configure_handler)); + win.signal_map_event().connect (mem_fun (*this, &LV2PluginUI::start_updating)); + win.signal_unmap_event().connect (mem_fun (*this, &LV2PluginUI::stop_updating)); + } return 0; } @@ -168,3 +285,41 @@ LV2PluginUI::is_update_wanted(uint32_t index) /* FIXME this should check the port notification properties, which nobody sets now anyway :) */ return true; } + +bool +LV2PluginUI::on_window_show(const Glib::ustring& title) +{ + //cout << "on_window_show - " << title << endl; flush(cout); + + if (_lv2->is_external_ui()) { + if (_external_ui_ptr) { + LV2_EXTERNAL_UI_SHOW(_external_ui_ptr); + return false; + } + lv2ui_instantiate(title); + if (!_external_ui_ptr) { + return false; + } + + LV2_EXTERNAL_UI_SHOW(_external_ui_ptr); + _screen_update_connection.disconnect(); + _screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect + (mem_fun(*this, &LV2PluginUI::output_update)); + return false; + } + + return true; +} + +void +LV2PluginUI::on_window_hide() +{ + //cout << "on_window_hide" << endl; flush(cout); + + if (_external_ui_ptr) { + LV2_EXTERNAL_UI_HIDE(_external_ui_ptr); + //slv2_ui_instance_get_descriptor(_inst)->cleanup(_inst); + //_external_ui_ptr = NULL; + //_screen_update_connection.disconnect(); + } +} diff --git a/gtk2_ardour/lv2_plugin_ui.h b/gtk2_ardour/lv2_plugin_ui.h index fe8dd85..9bb1fe6 100644 --- a/gtk2_ardour/lv2_plugin_ui.h +++ b/gtk2_ardour/lv2_plugin_ui.h @@ -34,6 +34,8 @@ #ifdef HAVE_LV2 +#include "lv2_external_ui.h" + namespace ARDOUR { class PluginInsert; class LV2Plugin; @@ -60,6 +62,13 @@ class LV2PluginUI : public PlugUIBase, public Gtk::VBox Gtk::Widget* _gui_widget; SLV2UIInstance _inst; float* _values; + + struct lv2_external_ui_host _external_ui_host; + LV2_Feature _external_ui_feature; + struct lv2_external_ui* _external_ui_ptr; + Gtk::Window* _win_ptr; + + static void on_external_ui_closed(LV2UI_Controller controller); static void lv2_ui_write( LV2UI_Controller controller, @@ -68,12 +77,17 @@ class LV2PluginUI : public PlugUIBase, public Gtk::VBox uint32_t format, const void* buffer); + void lv2ui_instantiate(const Glib::ustring& title); + void parameter_changed(uint32_t, float); void parameter_update(uint32_t, float); bool configure_handler (GdkEventConfigure*); void save_plugin_setting (); void output_update(); bool is_update_wanted(uint32_t index); + + virtual bool on_window_show(const Glib::ustring& title); + virtual void on_window_hide(); }; #endif // HAVE_LV2 diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc index 5d79cf0..04ebaeb 100644 --- a/gtk2_ardour/main.cc +++ b/gtk2_ardour/main.cc @@ -247,6 +247,10 @@ sigpipe_handler (int sig) cerr << _("SIGPIPE received - JACK has probably died") << endl; } +#ifdef HAVE_LV2 +void close_external_ui_windows(); +#endif + #ifdef VST_SUPPORT extern int gui_init (int* argc, char** argv[]); @@ -348,6 +352,9 @@ int main (int argc, char* argv[]) ARDOUR::cleanup (); pthread_cancel_all (); +#ifdef HAVE_LV2 + close_external_ui_windows(); +#endif return 0; } #ifdef VST_SUPPORT diff --git a/gtk2_ardour/plugin_ui.cc b/gtk2_ardour/plugin_ui.cc index 0705d22..f9bd9f4 100644 --- a/gtk2_ardour/plugin_ui.cc +++ b/gtk2_ardour/plugin_ui.cc @@ -190,11 +190,17 @@ PluginUIWindow::on_focus_out_event (GdkEventFocus *ev) void PluginUIWindow::on_show () { + set_role("plugin_ui"); + if (_pluginui) { _pluginui->update_presets (); } - Window::on_show (); + if (_pluginui) { + if (_pluginui->on_window_show (_title)) { + Window::on_show (); + } + } if (parent) { // set_transient_for (*parent); @@ -205,6 +211,18 @@ void PluginUIWindow::on_hide () { Window::on_hide (); + + if (_pluginui) { + _pluginui->on_window_hide (); + } +} + +void +PluginUIWindow::set_title(const Glib::ustring& title) +{ + //cout << "PluginUIWindow::set_title(\"" << title << "\"" << endl; + Gtk::Window::set_title(title); + _title = title; } bool diff --git a/gtk2_ardour/plugin_ui.h b/gtk2_ardour/plugin_ui.h index 3e027b6..887bd6c 100644 --- a/gtk2_ardour/plugin_ui.h +++ b/gtk2_ardour/plugin_ui.h @@ -82,6 +82,9 @@ class PlugUIBase : public virtual sigc::trackable virtual void update_presets (); + virtual bool on_window_show(const Glib::ustring& title) { return true; } + virtual void on_window_hide() {} + protected: boost::shared_ptr<ARDOUR::PluginInsert> insert; boost::shared_ptr<ARDOUR::Plugin> plugin; @@ -230,8 +233,10 @@ class PluginUIWindow : public Gtk::Window void on_hide (); void on_map (); + void set_title(const Glib::ustring& title); private: + Glib::ustring _title; PlugUIBase* _pluginui; sigc::connection death_connection; Gtk::Window* parent; diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index de8eb61..29f3465 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -64,6 +64,7 @@ class LV2Plugin : public ARDOUR::Plugin SLV2Plugin slv2_plugin() { return _plugin; } SLV2UI slv2_ui() { return _ui; } + bool is_external_ui() const; SLV2Port slv2_port(uint32_t i) { return slv2_plugin_get_port_by_index(_plugin, i); } const char* port_symbol(uint32_t port); @@ -159,6 +160,7 @@ struct LV2World { SLV2Value toggled; SLV2Value srate; SLV2Value gtk_gui; + SLV2Value external_gui; }; diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 10648c3..62d899b 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -149,6 +149,17 @@ LV2Plugin::init (LV2World& world, SLV2Plugin plugin, nframes_t rate) break; } } + + // if gtk gui is not available, try to find external gui + if (!_ui) { + for (unsigned i=0; i < slv2_uis_size(uis); ++i) { + SLV2UI ui = slv2_uis_get_at(uis, i); + if (slv2_ui_is_a(ui, _world.external_gui)) { + _ui = ui; + break; + } + } + } } Plugin::setup_controls (); @@ -176,6 +187,12 @@ LV2Plugin::~LV2Plugin () } } +bool +LV2Plugin::is_external_ui() const +{ + return slv2_ui_is_a(_ui, _world.external_gui); +} + string LV2Plugin::unique_id() const { @@ -552,6 +569,7 @@ LV2World::LV2World() toggled = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "toggled"); srate = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "sampleRate"); gtk_gui = slv2_value_new_uri(world, "http://lv2plug.in/ns/extensions/ui#GtkUI"); + external_gui = slv2_value_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external"); } LV2World::~LV2World() |
|
This is probably bug in ardour patch for external UI: http://nedko.arnaudov.name/soft/lv2fil/trac/ticket/4 Last time I checked ardour pluginui C++ objects where not freed, |
|
fwiw, this patch has worked flawlessly here for a while. would be nice to have in ardour. |
|
patch still applies to current 2.0 svn (r5494). |
|
two (very minor) problems with this patch (or maybe with external UIs in general): a) the external UI window is not "always on top" (the host-generated LADSPA and lv2 GUIs are) - this should be changed for a consistent user experience b) the external UI window (and process) stay up after ardour has been terminated. it would be kind of nice if ardour signaled all external UIs to terminate themselves as well. [Update:] this latter allegation is wrong. under normal circumstances, the UI terminates along with ardour. the undead UIs only occur when ardour crashes. |
|
committed to 2.0-ongoing and 3.0 |
|
something must have gone wrong here. i've been using that patch for weeks without problems. now i did an "svn revert -R .", followed by an "svn up" to pull the latest changes to a clean tree, and now a session is disconnected from JACK the moment i attempt to open nedko's 4band parametric (the only plugin so far that uses this patch). reconnection attempts fail. any old sessions that already contain the 4band parametric will load, but disconnect from JACK the moment i try to play them or position the playhead. |
|
nedko pointed me to http://nedko.arnaudov.name/soft/lv2fil/trac/ticket/9, and indeed an update of lv2fil fixes the issue. i just wonder why the first time i see this problem coincides with an ardour update. i'll leave the issue open for now, until some more testing has been done. |
|
no further problems after the update from nedko's tree and several days of heavy use. |
|
Issue has been closed automatically, by Trigger Close Plugin. Feel free to re-open with additional information if you think the issue is not resolved. |
Date Modified | Username | Field | Change |
---|---|---|---|
2009-07-12 00:37 | nedko | New Issue | |
2009-07-12 00:37 | nedko | File Added: ardour-2.8.1-lv2-external-ui.patch | |
2009-07-20 15:43 | paul | Status | new => assigned |
2009-07-20 15:43 | paul | Assigned To | => paul |
2009-07-30 18:36 | nedko | Note Added: 0006476 | |
2009-08-06 23:25 | nettings | Note Added: 0006482 | |
2009-08-06 23:31 | nettings | Note Added: 0006483 | |
2009-08-14 09:27 | nettings | Note Added: 0006525 | |
2009-08-16 12:35 | nettings | Note Edited: 0006525 | |
2009-08-17 19:14 | nettings | cost | => 0.00 |
2009-08-17 19:14 | nettings | Summary | missing support for lv2 external ui extension => [PATCH] missing support for lv2 external ui extension |
2009-10-01 16:32 | paul | Note Added: 0006667 | |
2009-10-01 16:32 | paul | Status | assigned => resolved |
2009-10-01 16:32 | paul | Resolution | open => fixed |
2009-10-03 21:18 | nettings | Note Added: 0006685 | |
2009-10-03 21:19 | nettings | Severity | major => crash |
2009-10-03 21:19 | nettings | Status | resolved => feedback |
2009-10-03 21:19 | nettings | Resolution | fixed => reopened |
2009-10-03 21:19 | nettings | Target Version | => 2.8.3 |
2009-10-03 21:45 | nettings | Note Added: 0006686 | |
2009-10-03 21:45 | nettings | Resolution | reopened => fixed |
2009-10-24 21:29 | nettings | Note Added: 0006881 | |
2009-10-24 21:29 | nettings | Status | feedback => resolved |
2010-04-24 10:28 | cth103 | Category | bugs => bugs2 |
2010-04-24 10:31 | cth103 | Category | bugs2 => bugs |
2020-04-19 20:14 | system | Note Added: 0021953 | |
2020-04-19 20:14 | system | Status | resolved => closed |