enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / lv2_plugin_ui.cc
index 9d7f8dad6a382ba0bf4996683ebb0ea85b3491d6..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)
-{
-       PlugUIBase::parameter_changed(port_index, val);
-
-       if (val != _values[port_index]) {
-               parameter_update(port_index, val);
-       }
-}
-
-void
-LV2PluginUI::parameter_update(uint32_t port_index, float val)
+LV2PluginUI::control_changed (uint32_t port_index)
 {
-       if (!_inst) {
-               return;
+       /* 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);
        }
-
-       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,7 +227,7 @@ 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)
 {
@@ -212,6 +241,8 @@ LV2PluginUI::LV2PluginUI(boost::shared_ptr<PluginInsert> pi,
        _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
@@ -253,7 +284,6 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
                _ardour_buttons_box.show_all();
 
                _gui_widget = Gtk::manage((container = new Gtk::Alignment()));
-               container->set (.5, .5, 0, 0);
                pack_start(*_gui_widget, true, true);
                _gui_widget->show();
 
@@ -353,19 +383,29 @@ LV2PluginUI::lv2ui_instantiate(const std::string& title)
                _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);
                }
        }
 
@@ -402,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();