OSC: select channel plugin support with paging.
authorLen Ovens <len@ovenwerks.net>
Mon, 12 Jun 2017 19:29:58 +0000 (12:29 -0700)
committerLen Ovens <len@ovenwerks.net>
Mon, 12 Jun 2017 19:31:04 +0000 (12:31 -0700)
libs/surfaces/osc/osc.cc
libs/surfaces/osc/osc.h
libs/surfaces/osc/osc_select_observer.cc
libs/surfaces/osc/osc_select_observer.h

index 4b554e6f2b421ee7b257f2e8c04dab8990ef1c5a..0c7d1099046827c8f0971fc81e44b5573d4e1fb5 100644 (file)
@@ -893,6 +893,11 @@ OSC::catchall (const char *path, const char* types, lo_arg **argv, int argc, lo_
 
                ret = cue_parse (path, types, argv, argc, msg);
 
+       } else
+       if (!strncmp (path, "/select/plugin/parameter", 24)) {
+
+               ret = select_plugin_parameter (path, types, argv, argc, msg);
+
        } else
        if (!strncmp (path, "/access_action/", 15)) {
                check_surface (msg);
@@ -1461,11 +1466,6 @@ OSC::get_surface (lo_address addr)
                        return &_surface[it];
                }
        }
-       // if we do this when OSC is started we get the wrong stripable
-       // we don't need this until we actually have a surface to deal with
-       if (!_select || (_select != ControlProtocol::first_selected_stripable())) {
-               gui_selection_changed();
-       }
 
        // No surface create one with default values
        OSCSurface s;
@@ -1487,10 +1487,14 @@ OSC::get_surface (lo_address addr)
        s.send_page_size = 0;
        s.plug_page = 1;
        s.plug_page_size = 0;
-       s.plugin = 1;
+       s.plugin_id = 1;
 
        s.nstrips = s.strips.size();
        _surface.push_back (s);
+       // moved this down here as selection may need s.<anything to do with select> set
+       if (!_select || (_select != ControlProtocol::first_selected_stripable())) {
+               gui_selection_changed();
+       }
 
        // set bank and strip feedback
        _set_bank(s.bank, addr);
@@ -1796,13 +1800,80 @@ OSC::sel_plug_page (int page, lo_message msg)
 }
 
 int
-OSC::sel_plugin (uint32_t id, lo_message msg)
+OSC::sel_plugin (int delta, lo_message msg)
 {
-       OSCSurface *s = get_surface(get_address (msg));
-       s->plugin = id;
-       s->plug_page = 1;
-       s->sel_obs->renew_plugin();
-       return 0;
+       OSCSurface *sur = get_surface(get_address (msg));
+       return _sel_plugin (sur->plugin_id + delta, get_address (msg));
+}
+
+int
+OSC::_sel_plugin (int id, lo_address addr)
+{
+       OSCSurface *sur = get_surface(addr);
+       boost::shared_ptr<Stripable> s;
+       if (sur->expand_enable) {
+               s = get_strip (sur->expand, addr);
+       } else {
+               s = _select;
+       }
+       if (s) {
+               boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(s);
+               if (!r) {
+                       return 1;
+               }
+
+               // find out how many plugins we have
+               bool plugs;
+               int nplugs  = 0;
+               do {
+                       plugs = false;
+                       if (r->nth_plugin (nplugs)) {
+                               plugs = true;
+                               nplugs++;
+                       }
+               } while (plugs);
+
+               // limit plugin_id to actual plugins
+               if (nplugs < id) {
+                       sur->plugin_id = nplugs;
+               } else if (!nplugs) {
+                       sur->plugin_id = 0;
+               } else  if (nplugs && !id) {
+                       sur->plugin_id = 1;
+               } else {
+                       sur->plugin_id = id;
+               }
+
+               // we have a plugin number now get the processor
+               boost::shared_ptr<Processor> proc = r->nth_plugin (sur->plugin_id - 1);
+               boost::shared_ptr<PluginInsert> pi;
+               if (!(pi = boost::dynamic_pointer_cast<PluginInsert>(proc))) {
+                       PBD::warning << "OSC: Plugin: " << sur->plugin_id << " does not seem to be a plugin" << endmsg;                 
+                       return 1;
+               }
+               boost::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
+               bool ok = false;
+               // put only input controls into a vector
+               sur->plug_params.clear ();
+               uint32_t nplug_params  = pip->parameter_count();
+               for ( uint32_t ppi = 0;  ppi < nplug_params; ++ppi) {
+                       uint32_t controlid = pip->nth_parameter(ppi, ok);
+                       if (!ok) {
+                               continue;
+                       }
+                       if (pip->parameter_is_input(controlid)) {
+                               sur->plug_params.push_back (ppi);
+                       }
+               }
+
+               sur->plug_page = 1;
+
+               if (sur->sel_obs) {
+                       sur->sel_obs->renew_plugin();
+               }
+               return 0;
+       }
+       return 1;
 }
 
 void
@@ -2844,16 +2915,26 @@ OSC::_strip_select (boost::shared_ptr<Stripable> s, lo_address addr)
                s->DropReferences.connect (*this, MISSING_INVALIDATOR, boost::bind (&OSC::recalcbanks, this), this);
                sur->sel_obs = sel_fb;
        } else if (sur->expand_enable) {
+               // expand doesn't point to a stripable, turn it off and use select
                sur->expand = 0;
                sur->expand_enable = false;
                if (_select && feedback_on) {
-                       OSCSelectObserver* sel_fb = new OSCSelectObserver (_select, addr, sur);
-                       _select->DropReferences.connect (*this, MISSING_INVALIDATOR, boost::bind (&OSC::recalcbanks, this), this);
+                       s = _select;
+                       OSCSelectObserver* sel_fb = new OSCSelectObserver (s, addr, sur);
+                       s->DropReferences.connect (*this, MISSING_INVALIDATOR, boost::bind (&OSC::recalcbanks, this), this);
                        sur->sel_obs = sel_fb;
                }
        } else if (feedback_on) {
                route_send_fail ("select", sur->expand, 0 , addr);
        }
+       // need to set monitor for processor changed signal
+       // detecting processor changes requires cast to route
+       boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(s);
+       if (r) {
+               r->processors_changed.connect  (sur->proc_connection, MISSING_INVALIDATOR, boost::bind (&OSC::processor_changed, this, addr), this);
+               processor_changed (addr);
+       }
+
        if (!feedback_on) {
                return 0;
        }
@@ -2904,6 +2985,18 @@ OSC::_strip_select (boost::shared_ptr<Stripable> s, lo_address addr)
        return 0;
 }
 
+void
+OSC::processor_changed (lo_address addr)
+{
+       OSCSurface *sur = get_surface (addr);
+       sur->proc_connection.disconnect ();
+       _sel_plugin (sur->plugin_id, addr);
+       if (sur->sel_obs) {
+               sur->sel_obs->renew_sends ();
+               sur->sel_obs->eq_restart (-1);
+       }
+}
+
 int
 OSC::strip_gui_select (int ssid, int yn, lo_message msg)
 {
@@ -3381,6 +3474,115 @@ OSC::sel_sendenable (int id, float val, lo_message msg)
        return sel_send_fail ("send_enable", id, 0, get_address (msg));
 }
 
+int
+OSC::select_plugin_parameter (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg) {
+       OSCSurface *sur = get_surface(get_address (msg));
+       int paid;
+       int piid = sur->plugin_id;
+       float value = 0;
+       if (argc > 1) {
+               // no inline args
+               if (argc == 2) {
+                       // change parameter in already selected plugin
+                       if (argv[0]->f) {
+                               paid = (int) argv[0]->f;
+                       } else {
+                               paid = argv[0]->i;
+                       }
+                       value = argv[1]->f;
+               } else if (argc == 3) {
+                       if (argv[0]->f) {
+                               piid = (int) argv[0]->f;
+                       } else {
+                               piid = argv[0]->i;
+                       }
+                       _sel_plugin (piid, get_address (msg));
+                       if (argv[1]->f) {
+                               paid = (int) argv[1]->f;
+                       } else {
+                               paid = argv[1]->i;
+                       }
+                       value = argv[2]->f;
+               } else if (argc > 3) {
+                       PBD::warning << "OSC: Too many parameters: " << argc << endmsg;
+                       return -1;
+               }
+       } else if (argc) {
+               const char * par = strstr (&path[25], "/");
+               if (par) {
+                       piid = atoi (&path[25]);
+                       _sel_plugin (piid, msg);
+                       paid = atoi (&par[1]);
+                       value = argv[0]->f;
+                       // we have plugin id too
+               } else {
+                       // just parameter
+                       paid = atoi (&path[25]);
+                       value = argv[0]->f;
+               }
+       } else {
+               PBD::warning << "OSC: Must have parameters." << endmsg;
+               return -1;
+       }
+       if (piid != sur->plugin_id) {
+               // if the user is sending to a non-existant plugin, don't adjust one we do have
+               PBD::warning << "OSC: plugin: " << piid << " out of range" << endmsg;
+               return -1;
+       }
+       if (sur->plug_page_size && (paid > (int)sur->plug_page_size)) {
+               return sel_send_fail ("plugin/parameter", paid, 0, get_address (msg));
+       }
+       boost::shared_ptr<Stripable> s;
+       if (sur->expand_enable) {
+               s = get_strip (sur->expand, get_address (msg));
+       } else {
+               s = _select;
+       }
+       boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(s);
+       if (!r) {
+               return 1;
+       }
+
+       boost::shared_ptr<Processor> proc = r->nth_plugin (sur->plugin_id - 1);
+       boost::shared_ptr<PluginInsert> pi;
+       if (!(pi = boost::dynamic_pointer_cast<PluginInsert>(proc))) {
+               return 1;
+       }
+       boost::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
+       // paid is paged parameter convert to absolute
+       int parid = paid + (int)(sur->plug_page_size * (sur->plug_page - 1));
+       if (parid > (int) sur->plug_params.size ()) {
+               if (sur->feedback[13]) {
+                       sel_send_fail ("plugin/parameter", paid, 0, get_address (msg));
+               }
+               return 0;
+       }
+
+       bool ok = false;
+       uint32_t controlid = pip->nth_parameter(sur->plug_params[parid - 1], ok);
+       if (!ok) {
+               return 1;
+       }
+       ParameterDescriptor pd;
+       pip->get_parameter_descriptor(controlid, pd);
+       if ( pip->parameter_is_input(controlid) || pip->parameter_is_control(controlid) ) {
+               boost::shared_ptr<AutomationControl> c = pi->automation_control(Evoral::Parameter(PluginAutomation, 0, controlid));
+               if (c) {
+                       if (pd.integer_step && pd.upper == 1) {
+                               if (c->get_value () && value < 1.0) {
+                                       c->set_value (0, PBD::Controllable::NoGroup);
+                               } else if (!c->get_value () && value) {
+                                       c->set_value (1, PBD::Controllable::NoGroup);
+                               }
+                       } else {
+                               c->set_value (c->interface_to_internal (value), PBD::Controllable::NoGroup);
+                       }
+                       return 0;
+               }
+       }
+       return 1;
+}
+
 int
 OSC::route_plugin_list (int ssid, lo_message msg) {
        if (!session) {
index 0f9cbeba7b02889476e17eec864d63134df1a742..8fbb64b2d9a848490aa9f478d4eb59c293ad5925 100644 (file)
@@ -38,6 +38,7 @@
 
 #include "ardour/types.h"
 #include "ardour/send.h"
+#include "ardour/plugin.h"
 #include "control_protocol/control_protocol.h"
 
 #include "pbd/i18n.h"
@@ -123,9 +124,11 @@ class OSC : public ARDOUR::ControlProtocol, public AbstractUI<OSCUIRequest>
                uint32_t bank_size;                     // size of banks for this surface
                int plug_page;                          // current plugin page
                uint32_t plug_page_size;        // plugin page size (number of controls)
-               uint32_t plugin;                        // id of current plugin
+               int plugin_id;                  // id of current plugin
+               std::vector<int> plug_params; // vector to store ports that are controls
                int send_page;                          // current send page
                uint32_t send_page_size;        // send page size in channels
+               PBD::ScopedConnection proc_connection; // for processor signal monitoring
                std::bitset<32> strip_types;// what strip types are a part of this bank
                uint32_t nstrips;                       // how many strips are there for strip_types
                std::bitset<32> feedback;       // What is fed back? strips/meters/timecode/bar_beat/global
@@ -266,6 +269,8 @@ class OSC : public ARDOUR::ControlProtocol, public AbstractUI<OSCUIRequest>
        boost::shared_ptr<ARDOUR::Send> cue_get_send (uint32_t id, lo_address addr);
        // end cue
 
+       int select_plugin_parameter (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg);
+
 #define OSC_DEBUG \
        if (_debugmode == All) { \
                debugmsg (dgettext(PACKAGE, "OSC"), path, types, argv, argc); \
@@ -592,7 +597,9 @@ class OSC : public ARDOUR::ControlProtocol, public AbstractUI<OSCUIRequest>
        int sel_send_page (int page, lo_message msg);
        int sel_plug_pagesize (uint32_t size, lo_message msg);
        int sel_plug_page (int page, lo_message msg);
-       int sel_plugin (uint32_t id, lo_message msg);
+       int sel_plugin (int delta, lo_message msg);
+       int _sel_plugin (int id, lo_address addr);
+       void processor_changed (lo_address addr);
 
        int scrub (float delta, lo_message msg);
        int jog (float delta, lo_message msg);
index c4a5eae09cea26e1bf5f9b80c84a304c8e3eeeed..41f75139c4e29b19e04cfe12b4969a617c5f79e4 100644 (file)
@@ -30,6 +30,8 @@
 #include "ardour/solo_safe_control.h"
 #include "ardour/route.h"
 #include "ardour/send.h"
+#include "ardour/plugin.h"
+#include "ardour/plugin_insert.h"
 #include "ardour/processor.h"
 #include "ardour/readonly_control.h"
 
@@ -142,14 +144,8 @@ OSCSelectObserver::OSCSelectObserver (boost::shared_ptr<Stripable> s, lo_address
                        change_message ("/select/pan_lfe_control", _strip->pan_lfe_control());
                }
 
-               // sends and eq
-               // detecting processor changes requires cast to route
-               boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(_strip);
-               if (r) {
-                       r->processors_changed.connect  (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCSelectObserver::send_restart, this, -1), OSC::instance());
-                       send_init();
-                       eq_init();
-               }
+               // sends, plugins and eq
+               // detecting processor changes is now in osc.cc
 
                // Compressor
                if (_strip->comp_enable_controllable ()) {
@@ -231,6 +227,7 @@ OSCSelectObserver::~OSCSelectObserver ()
                send_float ("/select/comp_makeup", 0);
        }
        send_end();
+       plugin_end();
        eq_end();
 
        lo_address_free (addr);
@@ -244,7 +241,8 @@ OSCSelectObserver::renew_sends () {
 
 void
 OSCSelectObserver::renew_plugin () {
-       // to be written :)
+       plugin_end();
+       plugin_init();
 }
 
 void
@@ -264,6 +262,7 @@ OSCSelectObserver::send_init()
                return;
        }
 
+       // paging should be done in osc.cc in case there is no feedback
        send_size = nsends;
        if (sur->send_page_size) {
                send_size = sur->send_page_size;
@@ -311,6 +310,79 @@ OSCSelectObserver::send_init()
        }
 }
 
+void
+OSCSelectObserver::plugin_init()
+{
+       if (!sur->plugin_id) {
+               return;
+       }
+
+       boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(_strip);
+       if (!r) {
+               return;
+       }
+
+       // we have a plugin number now get the processor
+       boost::shared_ptr<Processor> proc = r->nth_plugin (sur->plugin_id - 1);
+       boost::shared_ptr<PluginInsert> pi;
+       if (!(pi = boost::dynamic_pointer_cast<PluginInsert>(proc))) {
+               return;
+       }
+       boost::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
+
+       bool ok = false;
+       nplug_params = sur->plug_params.size ();
+
+       // default of 0 page size means show all
+       plug_size = nplug_params;
+       if (sur->plug_page_size) {
+               plug_size = sur->plug_page_size;
+       }
+       text_message ("/select/plugin/name", pip->name());
+       uint32_t page_end = nplug_params;
+       uint32_t max_page = 1;
+       if (plug_size) {
+               max_page = (uint32_t)((nplug_params - 1) / plug_size) + 1;
+       }
+
+       if (sur->plug_page < 1) {
+               sur->plug_page = 1;
+       }
+       if ((uint32_t)sur->plug_page > max_page) {
+               sur->plug_page = max_page;
+       }
+       uint32_t page_start = ((sur->plug_page - 1) * plug_size);
+       page_end = sur->plug_page * plug_size;
+
+       int pid = 1;
+       for ( uint32_t ppi = page_start;  ppi < page_end; ++ppi, ++pid) {
+               if (ppi >= nplug_params) {
+                       text_with_id ("/select/plugin/parameter/name", pid, " ");
+                       send_float_with_id ("/select/plugin/parameter", pid, 0);
+                       continue;
+               }
+
+               uint32_t controlid = pip->nth_parameter(sur->plug_params[ppi], ok);
+               if (!ok) {
+                       continue;
+               }
+               ParameterDescriptor pd;
+               pip->get_parameter_descriptor(controlid, pd);
+               text_with_id ("/select/plugin/parameter/name", pid, pd.label);
+               if ( pip->parameter_is_input(controlid)) {
+                       boost::shared_ptr<AutomationControl> c = pi->automation_control(Evoral::Parameter(PluginAutomation, 0, controlid));
+                       if (c) {
+                               bool swtch = false;
+                               if (pd.integer_step && pd.upper == 1) {
+                                       swtch = true;
+                               }
+                               c->Changed.connect (plugin_connections, MISSING_INVALIDATOR, boost::bind (&OSCSelectObserver::plugin_parameter_changed, this, pid, swtch, c), OSC::instance());
+                               plugin_parameter_changed (pid, swtch, c);
+                       }
+               }
+       }
+}
+
 void
 OSCSelectObserver::send_end ()
 {
@@ -332,10 +404,26 @@ OSCSelectObserver::send_end ()
 }
 
 void
-OSCSelectObserver::send_restart(int x)
+OSCSelectObserver::plugin_parameter_changed (int pid, bool swtch, boost::shared_ptr<PBD::Controllable> controllable)
 {
-       send_end();
-       send_init();
+       if (swtch) {
+               enable_message_with_id ("/select/plugin/parameter", pid, controllable);
+       } else {
+               change_message_with_id ("/select/plugin/parameter", pid, controllable);
+       }
+}
+
+void
+OSCSelectObserver::plugin_end ()
+{
+       plugin_connections.drop_connections ();
+       text_message ("/select/plugin/name", " ");
+       for (uint32_t i = 1; i <= plug_size; i++) {
+               send_float_with_id ("/select/plugin/parameter", i, 0);
+               // next name
+               text_with_id ("/select/plugin/parameter/name", i, " ");
+       }
+       nplug_params = 0;
 }
 
 void
index 5fdb976aca422c59fc8ab593e862dd95f3c0ccc1..4114ecb2d51412b5c168d5a91778d05ec6726c65 100644 (file)
@@ -45,13 +45,15 @@ class OSCSelectObserver
        void tick (void);
        void renew_sends (void);
        void renew_plugin (void);
+       void eq_restart (int);
 
   private:
        boost::shared_ptr<ARDOUR::Stripable> _strip;
 
        PBD::ScopedConnectionList strip_connections;
-       // sends and eq need their own
+       // sends, plugins and eq need their own
        PBD::ScopedConnectionList send_connections;
+       PBD::ScopedConnectionList plugin_connections;
        PBD::ScopedConnectionList eq_connections;
 
        lo_address addr;
@@ -66,6 +68,8 @@ class OSCSelectObserver
        float _last_gain;
        ARDOUR::AutoState as;
        uint32_t send_size;
+       uint32_t nplug_params;
+       uint32_t plug_size;
 
        void name_changed (const PBD::PropertyChange& what_changed);
        void change_message (std::string path, boost::shared_ptr<PBD::Controllable> controllable);
@@ -82,12 +86,13 @@ class OSCSelectObserver
        // sends stuff
        void send_init (void);
        void send_end (void);
-       void send_restart (int);
+       void plugin_init (void);
+       void plugin_end (void);
+       void plugin_parameter_changed (int pid, bool swtch, boost::shared_ptr<PBD::Controllable> controllable);
        void send_gain (uint32_t id, boost::shared_ptr<PBD::Controllable> controllable);
        void send_enable (std::string path, uint32_t id, boost::shared_ptr<ARDOUR::Processor> proc);
        void eq_init (void);
        void eq_end (void);
-       void eq_restart (int);
        std::string set_path (std::string path, uint32_t id);
        void send_float (std::string path, float val);
        void send_float_with_id (std::string path, uint32_t id, float val);