From 9441e8d484964e0f8942c82b0112c8ad92aa9e3e Mon Sep 17 00:00:00 2001 From: Len Ovens Date: Mon, 12 Jun 2017 12:29:58 -0700 Subject: OSC: select channel plugin support with paging. --- libs/surfaces/osc/osc.cc | 230 +++++++++++++++++++++++++++++-- libs/surfaces/osc/osc.h | 11 +- libs/surfaces/osc/osc_select_observer.cc | 112 +++++++++++++-- libs/surfaces/osc/osc_select_observer.h | 11 +- 4 files changed, 333 insertions(+), 31 deletions(-) diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc index 4b554e6f2b..0c7d109904 100644 --- a/libs/surfaces/osc/osc.cc +++ b/libs/surfaces/osc/osc.cc @@ -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. 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 s; + if (sur->expand_enable) { + s = get_strip (sur->expand, addr); + } else { + s = _select; + } + if (s) { + boost::shared_ptr r = boost::dynamic_pointer_cast(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 proc = r->nth_plugin (sur->plugin_id - 1); + boost::shared_ptr pi; + if (!(pi = boost::dynamic_pointer_cast(proc))) { + PBD::warning << "OSC: Plugin: " << sur->plugin_id << " does not seem to be a plugin" << endmsg; + return 1; + } + boost::shared_ptr 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 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 r = boost::dynamic_pointer_cast(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 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 s; + if (sur->expand_enable) { + s = get_strip (sur->expand, get_address (msg)); + } else { + s = _select; + } + boost::shared_ptr r = boost::dynamic_pointer_cast(s); + if (!r) { + return 1; + } + + boost::shared_ptr proc = r->nth_plugin (sur->plugin_id - 1); + boost::shared_ptr pi; + if (!(pi = boost::dynamic_pointer_cast(proc))) { + return 1; + } + boost::shared_ptr 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 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) { diff --git a/libs/surfaces/osc/osc.h b/libs/surfaces/osc/osc.h index 0f9cbeba7b..8fbb64b2d9 100644 --- a/libs/surfaces/osc/osc.h +++ b/libs/surfaces/osc/osc.h @@ -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 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 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 boost::shared_ptr 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 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); diff --git a/libs/surfaces/osc/osc_select_observer.cc b/libs/surfaces/osc/osc_select_observer.cc index c4a5eae09c..41f75139c4 100644 --- a/libs/surfaces/osc/osc_select_observer.cc +++ b/libs/surfaces/osc/osc_select_observer.cc @@ -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 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 r = boost::dynamic_pointer_cast(_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 r = boost::dynamic_pointer_cast(_strip); + if (!r) { + return; + } + + // we have a plugin number now get the processor + boost::shared_ptr proc = r->nth_plugin (sur->plugin_id - 1); + boost::shared_ptr pi; + if (!(pi = boost::dynamic_pointer_cast(proc))) { + return; + } + boost::shared_ptr 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 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 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 diff --git a/libs/surfaces/osc/osc_select_observer.h b/libs/surfaces/osc/osc_select_observer.h index 5fdb976aca..4114ecb2d5 100644 --- a/libs/surfaces/osc/osc_select_observer.h +++ b/libs/surfaces/osc/osc_select_observer.h @@ -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 _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 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 controllable); void send_gain (uint32_t id, boost::shared_ptr controllable); void send_enable (std::string path, uint32_t id, boost::shared_ptr 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); -- cgit v1.2.3