enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / lv2_plugin_ui.cc
index 8e57eeed6094985379f7ae7dd7bd438fcd302632..75b3ff0bb89f7823e97f855972335611b8513f80 100644 (file)
@@ -20,7 +20,9 @@
 #include "ardour/lv2_plugin.h"
 #include "ardour/session.h"
 #include "pbd/error.h"
+#include "pbd/stacktrace.h"
 
+#include "gui_thread.h"
 #include "lv2_plugin_ui.h"
 #include "timers.h"
 
@@ -29,7 +31,7 @@
 #include <lilv/lilv.h>
 #include <suil/suil.h>
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 using namespace Gtk;
@@ -53,8 +55,11 @@ LV2PluginUI::write_from_ui(void*       controller,
                }
 
                boost::shared_ptr<AutomationControl> ac = me->_controllables[port_index];
+
+               me->_updates.insert (port_index);
+
                if (ac) {
-                       ac->set_value(*(const float*)buffer);
+                       ac->set_value(*(const float*)buffer, Controllable::NoGroup);
                }
        } else if (format == URIMap::instance().urids.atom_eventTransfer) {
 
@@ -120,34 +125,24 @@ LV2PluginUI::on_external_ui_closed(void* controller)
 }
 
 void
-LV2PluginUI::parameter_changed(uint32_t port_index, float val)
+LV2PluginUI::control_changed (uint32_t port_index)
 {
-       PlugUIBase::parameter_changed(port_index, val);
-
-       if (val != _values[port_index]) {
-               parameter_update(port_index, val);
+       /* Must run in GUI thread because we modify _updates with no lock */
+       if (_lv2->get_parameter (port_index) != _values_last_sent_to_ui[port_index]) {
+               /* current plugin parameter does not match last value received
+                  from GUI, so queue an update to push it to the GUI during
+                  our regular timeout.
+               */
+               _updates.insert (port_index);
        }
 }
 
-void
-LV2PluginUI::parameter_update(uint32_t port_index, float val)
-{
-       if (!_inst) {
-               return;
-       }
-
-       suil_instance_port_event((SuilInstance*)_inst, port_index, 4, 0, &val);
-       _values[port_index] = val;
-}
-
 bool
 LV2PluginUI::start_updating(GdkEventAny*)
 {
-       if (!_output_ports.empty()) {
-               _screen_update_connection.disconnect();
-               _screen_update_connection = Timers::super_rapid_connect
-                       (sigc::mem_fun(*this, &LV2PluginUI::output_update));
-       }
+       _screen_update_connection.disconnect();
+       _screen_update_connection = Timers::super_rapid_connect
+               (sigc::mem_fun(*this, &LV2PluginUI::output_update));
        return false;
 }
 
@@ -155,11 +150,21 @@ bool
 LV2PluginUI::stop_updating(GdkEventAny*)
 {
        //cout << "stop_updating" << endl;
+       _screen_update_connection.disconnect();
+       return false;
+}
 
-       if (!_output_ports.empty()) {
-               _screen_update_connection.disconnect();
+void
+LV2PluginUI::queue_port_update()
+{
+       const uint32_t num_ports = _lv2->num_ports();
+       for (uint32_t i = 0; i < num_ports; ++i) {
+               bool     ok;
+               uint32_t port = _lv2->nth_parameter(i, ok);
+               if (ok) {
+                       _updates.insert (port);
+               }
        }
-       return false;
 }
 
 void
@@ -183,13 +188,37 @@ LV2PluginUI::output_update()
                }
        }
 
-       /* FIXME only works with control output ports (which is all we support now anyway) */
+       if (!_inst) {
+               return;
+       }
+
+       /* output ports (values set by DSP) need propagating to GUI */
+
        uint32_t nports = _output_ports.size();
        for (uint32_t i = 0; i < nports; ++i) {
                uint32_t index = _output_ports[i];
-               parameter_changed(index, _lv2->get_parameter(index));
+               float val = _lv2->get_parameter (index);
+
+               if (val != _values_last_sent_to_ui[index]) {
+                       /* Send to GUI */
+                       suil_instance_port_event ((SuilInstance*)_inst, index, 4, 0, &val);
+                       /* Cache current value */
+                       _values_last_sent_to_ui[index] = val;
+               }
        }
 
+       /* Input ports marked for update because the control value changed
+          since the last redisplay.
+       */
+
+       for (Updates::iterator i = _updates.begin(); i != _updates.end(); ++i) {
+               float val = _lv2->get_parameter (*i);
+               /* push current value to the GUI */
+               suil_instance_port_event ((SuilInstance*)_inst, (*i), 4, 0, &val);
+               _values_last_sent_to_ui[(*i)] = val;
+       }
+
+       _updates.clear ();
 }
 
 LV2PluginUI::LV2PluginUI(boost::shared_ptr<PluginInsert> pi,
@@ -198,19 +227,22 @@ LV2PluginUI::LV2PluginUI(boost::shared_ptr<PluginInsert> pi,
        , _pi(pi)
        , _lv2(lv2p)
        , _gui_widget(NULL)
-       , _values(NULL)
+       , _values_last_sent_to_ui(NULL)
        , _external_ui_ptr(NULL)
        , _inst(NULL)
 {
        _ardour_buttons_box.set_spacing (6);
        _ardour_buttons_box.set_border_width (6);
        _ardour_buttons_box.pack_end (focus_button, false, false);
-       _ardour_buttons_box.pack_end (bypass_button, false, false, 10);
+       _ardour_buttons_box.pack_end (bypass_button, false, false, 4);
+       _ardour_buttons_box.pack_end (reset_button, false, false, 4);
        _ardour_buttons_box.pack_end (delete_button, false, false);
        _ardour_buttons_box.pack_end (save_button, false, false);
        _ardour_buttons_box.pack_end (add_button, false, false);
        _ardour_buttons_box.pack_end (_preset_combo, false, false);
        _ardour_buttons_box.pack_end (_preset_modified, false, false);
+
+       plugin->PresetLoaded.connect (*this, invalidator (*this), boost::bind (&LV2PluginUI::queue_port_update, this), gui_context ());
 }
 
 void
@@ -285,7 +317,7 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
        const LilvUI*   ui     = (const LilvUI*)_lv2->c_ui();
        const LilvNode* bundle = lilv_ui_get_bundle_uri(ui);
        const LilvNode* binary = lilv_ui_get_binary_uri(ui);
-#ifdef HAVE_LILV_0_21_1
+#ifdef HAVE_LILV_0_21_3
        char* ui_bundle_path = lilv_file_uri_parse(lilv_node_as_uri(bundle), NULL);
        char* ui_binary_path = lilv_file_uri_parse(lilv_node_as_uri(binary), NULL);
 #else
@@ -315,6 +347,11 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
        free(ui_binary_path);
        free(features);
 
+       if (!_inst) {
+               error << _("failed to instantiate LV2 GUI") << endmsg;
+               return;
+       }
+
 #define GET_WIDGET(inst) suil_instance_get_widget((SuilInstance*)inst);
 
        const uint32_t num_ports = _lv2->num_ports();
@@ -327,40 +364,48 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
        }
 
        _external_ui_ptr = NULL;
-       if (_inst) {
-               if (!is_external_ui) {
-                       GtkWidget* c_widget = (GtkWidget*)GET_WIDGET(_inst);
-                       if (!c_widget) {
-                               error << _("failed to get LV2 UI widget") << endmsg;
-                               suil_instance_free((SuilInstance*)_inst);
-                               _inst = NULL;
-                               return;
-                       }
-                       if (!container->get_child()) {
-                               // Suil didn't add the UI to the container for us, so do it now
-                               container->add(*Gtk::manage(Glib::wrap(c_widget)));
-                       }
-                       container->show_all();
-                       gtk_widget_set_can_focus(c_widget, true);
-                       gtk_widget_grab_focus(c_widget);
-               } else {
-                       _external_ui_ptr = (struct lv2_external_ui*)GET_WIDGET(_inst);
+       if (!is_external_ui) {
+               GtkWidget* c_widget = (GtkWidget*)GET_WIDGET(_inst);
+               if (!c_widget) {
+                       error << _("failed to get LV2 UI widget") << endmsg;
+                       suil_instance_free((SuilInstance*)_inst);
+                       _inst = NULL;
+                       return;
+               }
+               if (!container->get_child()) {
+                       // Suil didn't add the UI to the container for us, so do it now
+                       container->add(*Gtk::manage(Glib::wrap(c_widget)));
                }
+               container->show_all();
+               gtk_widget_set_can_focus(c_widget, true);
+               gtk_widget_grab_focus(c_widget);
+       } else {
+               _external_ui_ptr = (struct lv2_external_ui*)GET_WIDGET(_inst);
        }
 
-       _values = new float[num_ports];
+       _values_last_sent_to_ui = new float[num_ports];
        _controllables.resize(num_ports);
+
        for (uint32_t i = 0; i < num_ports; ++i) {
                bool     ok;
                uint32_t port = _lv2->nth_parameter(i, ok);
                if (ok) {
-                       _values[port]        = _lv2->get_parameter(port);
+                       /* Cache initial value of the parameter, regardless of
+                          whether it is input or output
+                       */
+
+                       _values_last_sent_to_ui[port]        = _lv2->get_parameter(port);
                        _controllables[port] = boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (
                                insert->control(Evoral::Parameter(PluginAutomation, 0, port)));
 
                        if (_lv2->parameter_is_control(port) && _lv2->parameter_is_input(port)) {
-                               parameter_update(port, _values[port]);
+                               if (_controllables[port]) {
+                                       _controllables[port]->Changed.connect (control_connections, invalidator (*this), boost::bind (&LV2PluginUI::control_changed, this, port), gui_context());
+                               }
                        }
+
+                       /* queue for first update ("push") to GUI */
+                       _updates.insert (port);
                }
        }
 
@@ -397,9 +442,7 @@ LV2PluginUI::lv2ui_free()
 
 LV2PluginUI::~LV2PluginUI ()
 {
-       if (_values) {
-               delete[] _values;
-       }
+       delete [] _values_last_sent_to_ui;
 
        _message_update_connection.disconnect();
        _screen_update_connection.disconnect();
@@ -501,7 +544,7 @@ LV2PluginUI::on_window_show(const std::string& title)
                lv2ui_instantiate("gtk2gui");
        }
 
-       return true;
+       return _inst ? true : false;
 }
 
 void