/* * Copyright (C) 2006-2007 John Anderson * Copyright (C) 2007-2010 David Robillard * Copyright (C) 2007-2017 Paul Davis * Copyright (C) 2009-2012 Carl Hetherington * Copyright (C) 2015-2016 Len Ovens * Copyright (C) 2015-2019 Robin Gareus * Copyright (C) 2016-2018 Ben Loftis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "pbd/convert.h" #include "pbd/failed_constructor.h" #include "ardour/debug.h" #include "ardour/monitor_control.h" #include "ardour/phase_control.h" #include "ardour/plugin.h" #include "ardour/plugin_insert.h" #include "ardour/route.h" #include "ardour/solo_isolate_control.h" #include "ardour/stripable.h" #include "ardour/track.h" #include "mackie_control_protocol.h" #include "pot.h" #include "strip.h" #include "subview.h" #include "surface.h" using namespace ARDOUR; using namespace ArdourSurface; using namespace Mackie; using namespace PBD; #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */ SubviewFactory* SubviewFactory::_instance = 0; SubviewFactory* SubviewFactory::instance() { if (!_instance) { _instance = new SubviewFactory(); } return _instance; } SubviewFactory::SubviewFactory() {}; boost::shared_ptr SubviewFactory::create_subview( Subview::Mode svm, MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) { switch (svm) { case Subview::EQ: return boost::shared_ptr(new EQSubview (mcp, subview_stripable)); case Subview::Dynamics: return boost::shared_ptr(new DynamicsSubview (mcp, subview_stripable)); case Subview::Sends: return boost::shared_ptr(new SendsSubview (mcp, subview_stripable)); case Subview::TrackView: return boost::shared_ptr(new TrackViewSubview (mcp, subview_stripable)); case Subview::Plugin: return boost::shared_ptr(new PluginSubview (mcp, subview_stripable)); case Subview::None: default: return boost::shared_ptr(new NoneSubview (mcp, subview_stripable)); } } Subview::Subview(MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) : _mcp(mcp) , _subview_stripable(subview_stripable) { init_strip_vectors(); } Subview::~Subview() { reset_all_vpot_controls(); } void Subview::reset_all_vpot_controls() { for (std::vector::iterator iter = _strip_vpots_over_all_surfaces.begin(); iter != _strip_vpots_over_all_surfaces.end(); ) { std::vector::iterator tmp; tmp = iter; ++tmp; if (*iter != 0) { (*iter)->set_control (boost::shared_ptr()); } iter = tmp; } } void Subview::handle_vselect_event(uint32_t global_strip_position) { Strip* strip = 0; Pot* vpot = 0; std::string* pending_display = 0; if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position)) { return; } boost::shared_ptr control = vpot->control (); if (!control) { return; } Controllable::GroupControlDisposition gcd; if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) { gcd = Controllable::InverseGroup; } else { gcd = Controllable::UseGroup; } if (control->toggled()) { if (control->toggled()) { control->set_value (!control->get_value(), gcd); } } else if (control->desc().enumeration || control->desc().integer_step) { double val = control->get_value (); if (val <= control->upper() - 1.0) { control->set_value (val + 1.0, gcd); } else { control->set_value (control->lower(), gcd); } } } bool Subview::subview_mode_would_be_ok (Subview::Mode mode, boost::shared_ptr r, std::string& reason_why_not) { switch (mode) { case Subview::None: return NoneSubview::subview_mode_would_be_ok(r, reason_why_not); case Subview::Sends: return SendsSubview::subview_mode_would_be_ok(r, reason_why_not); case Subview::EQ: return EQSubview::subview_mode_would_be_ok(r, reason_why_not); case Subview::Dynamics: return DynamicsSubview::subview_mode_would_be_ok(r, reason_why_not); case Subview::TrackView: return TrackViewSubview::subview_mode_would_be_ok(r, reason_why_not); case Subview::Plugin: return PluginSubview::subview_mode_would_be_ok(r, reason_why_not); } return false; } void Subview::notify_subview_stripable_deleted () { _subview_stripable.reset (); } void Subview::init_strip_vectors() { _strips_over_all_surfaces.resize(_mcp.n_strips(), 0); _strip_vpots_over_all_surfaces.resize(_mcp.n_strips(), 0); _strip_pending_displays_over_all_surfaces.resize(_mcp.n_strips(), 0); } void Subview::store_pointers(Strip* strip, Pot* vpot, std::string* pending_display, uint32_t global_strip_position) { if (global_strip_position >= _strips_over_all_surfaces.size() || global_strip_position >= _strip_vpots_over_all_surfaces.size() || global_strip_position >= _strip_pending_displays_over_all_surfaces.size()) { return; } _strips_over_all_surfaces[global_strip_position] = strip; _strip_vpots_over_all_surfaces[global_strip_position] = vpot; _strip_pending_displays_over_all_surfaces[global_strip_position] = pending_display; } bool Subview::retrieve_pointers(Strip** strip, Pot** vpot, std::string** pending_display, uint32_t global_strip_position) { if (global_strip_position >= _strips_over_all_surfaces.size() || global_strip_position >= _strip_vpots_over_all_surfaces.size() || global_strip_position >= _strip_pending_displays_over_all_surfaces.size()) { return false; } *strip = _strips_over_all_surfaces[global_strip_position]; *vpot = _strip_vpots_over_all_surfaces[global_strip_position]; *pending_display = _strip_pending_displays_over_all_surfaces[global_strip_position]; if (!strip || !vpot || !pending_display) { return false; } return true; } void Subview::do_parameter_display(std::string& display, const ParameterDescriptor& pd, float param_val, Strip* strip, bool screen_hold) { display = Strip::format_paramater_for_display( pd, param_val, strip->stripable(), screen_hold ); if (screen_hold) { /* we just queued up a parameter to be displayed. 1 second from now, switch back to vpot mode display. */ strip->block_vpot_mode_display_for (1000); } } NoneSubview::NoneSubview(MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) : Subview(mcp, subview_stripable) {} NoneSubview::~NoneSubview() {} bool NoneSubview::subview_mode_would_be_ok (boost::shared_ptr r, std::string& reason_why_not) { // always possible return true; } void NoneSubview::update_global_buttons() { _mcp.update_global_button (Button::Send, off); _mcp.update_global_button (Button::Plugin, off); _mcp.update_global_button (Button::Eq, off); _mcp.update_global_button (Button::Dyn, off); _mcp.update_global_button (Button::Track, off); _mcp.update_global_button (Button::Pan, on); } void NoneSubview::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2]) { // nothing to be done here. All pots are set in strip.cc } EQSubview::EQSubview(MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) : Subview(mcp, subview_stripable) {} EQSubview::~EQSubview() {} bool EQSubview::subview_mode_would_be_ok (boost::shared_ptr r, std::string& reason_why_not) { if (r && r->eq_band_cnt() > 0) { return true; } reason_why_not = "no EQ in the track/bus"; return false; } void EQSubview::update_global_buttons() { _mcp.update_global_button (Button::Send, off); _mcp.update_global_button (Button::Plugin, off); _mcp.update_global_button (Button::Eq, on); _mcp.update_global_button (Button::Dyn, off); _mcp.update_global_button (Button::Track, off); _mcp.update_global_button (Button::Pan, off); } void EQSubview::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2]) { const uint32_t global_strip_position = _mcp.global_index (*strip); store_pointers(strip, vpot, pending_display, global_strip_position); if (!_subview_stripable) { return; } boost::shared_ptr pc; std::string pot_id; #ifdef MIXBUS int eq_band = -1; std::string band_name; if (_subview_stripable->is_input_strip ()) { #ifdef MIXBUS32C switch (global_strip_position) { case 0: case 2: case 4: case 6: eq_band = global_strip_position / 2; pc = _subview_stripable->eq_freq_controllable (eq_band); band_name = _subview_stripable->eq_band_name (eq_band); pot_id = band_name + "Freq"; break; case 1: case 3: case 5: case 7: eq_band = global_strip_position / 2; pc = _subview_stripable->eq_gain_controllable (eq_band); band_name = _subview_stripable->eq_band_name (eq_band); pot_id = band_name + "Gain"; break; case 8: pc = _subview_stripable->eq_shape_controllable(0); //low band "bell" button band_name = "lo"; pot_id = band_name + " Shp"; break; case 9: pc = _subview_stripable->eq_shape_controllable(3); //high band "bell" button band_name = "hi"; pot_id = band_name + " Shp"; break; case 10: pc = _subview_stripable->eq_enable_controllable(); pot_id = "EQ"; break; } #else //regular Mixbus channel EQ switch (global_strip_position) { case 0: case 2: case 4: eq_band = global_strip_position / 2; pc = _subview_stripable->eq_gain_controllable (eq_band); band_name = _subview_stripable->eq_band_name (eq_band); pot_id = band_name + "Gain"; break; case 1: case 3: case 5: eq_band = global_strip_position / 2; pc = _subview_stripable->eq_freq_controllable (eq_band); band_name = _subview_stripable->eq_band_name (eq_band); pot_id = band_name + "Freq"; break; case 6: pc = _subview_stripable->eq_enable_controllable(); pot_id = "EQ"; break; case 7: pc = _subview_stripable->filter_freq_controllable(true); pot_id = "HP Freq"; break; } #endif } else { //mixbus or master bus ( these are currently the same for MB & 32C ) switch (global_strip_position) { case 0: case 1: case 2: eq_band = global_strip_position; pc = _subview_stripable->eq_gain_controllable (eq_band); band_name = _subview_stripable->eq_band_name (eq_band); pot_id = band_name + "Gain"; break; } } #endif //If a controllable was found, connect it up, and put the labels in the display. if (pc) { pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&EQSubview::notify_change, this, boost::weak_ptr(pc), global_strip_position, false), ui_context()); vpot->set_control (pc); if (!pot_id.empty()) { pending_display[0] = pot_id; } else { pending_display[0] = std::string(); } } else { //no controllable was found; just clear this knob vpot->set_control (boost::shared_ptr()); pending_display[0] = std::string(); pending_display[1] = std::string(); } notify_change (boost::weak_ptr(pc), global_strip_position, true); } void EQSubview::notify_change (boost::weak_ptr pc, uint32_t global_strip_position, bool force) { if (!_subview_stripable) { return; } Strip* strip = 0; Pot* vpot = 0; std::string* pending_display = 0; if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position)) { return; } boost::shared_ptr control = pc.lock (); if (control) { float val = control->get_value(); do_parameter_display(pending_display[1], control->desc(), val, strip, true); /* update pot/encoder */ strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap)); } } DynamicsSubview::DynamicsSubview(MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) : Subview(mcp, subview_stripable) {} DynamicsSubview::~DynamicsSubview() {} bool DynamicsSubview::subview_mode_would_be_ok (boost::shared_ptr r, std::string& reason_why_not) { if (r && r->comp_enable_controllable()) { return true; } reason_why_not = "no dynamics in selected track/bus"; return false; } void DynamicsSubview::update_global_buttons() { _mcp.update_global_button (Button::Send, off); _mcp.update_global_button (Button::Plugin, off); _mcp.update_global_button (Button::Eq, off); _mcp.update_global_button (Button::Dyn, on); _mcp.update_global_button (Button::Track, off); _mcp.update_global_button (Button::Pan, off); } void DynamicsSubview::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2]) { const uint32_t global_strip_position = _mcp.global_index (*strip); store_pointers(strip, vpot, pending_display, global_strip_position); if (!_subview_stripable) { return; } boost::shared_ptr tc = _subview_stripable->comp_threshold_controllable (); boost::shared_ptr sc = _subview_stripable->comp_speed_controllable (); boost::shared_ptr mc = _subview_stripable->comp_mode_controllable (); boost::shared_ptr kc = _subview_stripable->comp_makeup_controllable (); boost::shared_ptr ec = _subview_stripable->comp_enable_controllable (); #ifdef MIXBUS32C //Mixbus32C needs to spill the filter controls into the comp section boost::shared_ptr hpfc = _subview_stripable->filter_freq_controllable (true); boost::shared_ptr lpfc = _subview_stripable->filter_freq_controllable (false); boost::shared_ptr fec = _subview_stripable->filter_enable_controllable (true); // shared HP/LP #endif /* we will control the global_strip_position-th available parameter, from the list in the * order shown above. */ std::vector, std::string > > available; std::vector params; if (tc) { available.push_back (std::make_pair (tc, "Thresh")); } if (sc) { available.push_back (std::make_pair (sc, mc ? _subview_stripable->comp_speed_name (mc->get_value()) : "Speed")); } if (mc) { available.push_back (std::make_pair (mc, "Mode")); } if (kc) { available.push_back (std::make_pair (kc, "Makeup")); } if (ec) { available.push_back (std::make_pair (ec, "on/off")); } #ifdef MIXBUS32C //Mixbus32C needs to spill the filter controls into the comp section if (hpfc) { available.push_back (std::make_pair (hpfc, "HPF")); } if (lpfc) { available.push_back (std::make_pair (lpfc, "LPF")); } if (fec) { available.push_back (std::make_pair (fec, "FiltIn")); } #endif if (global_strip_position >= available.size()) { /* this knob is not needed to control the available parameters */ vpot->set_control (boost::shared_ptr()); pending_display[0] = std::string(); pending_display[1] = std::string(); return; } boost::shared_ptr pc; pc = available[global_strip_position].first; std::string pot_id = available[global_strip_position].second; pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&DynamicsSubview::notify_change, this, boost::weak_ptr(pc), global_strip_position, false, true), ui_context()); vpot->set_control (pc); if (!pot_id.empty()) { pending_display[0] = pot_id; } else { pending_display[0] = std::string(); } notify_change (boost::weak_ptr(pc), global_strip_position, true, false); } void DynamicsSubview::notify_change (boost::weak_ptr pc, uint32_t global_strip_position, bool force, bool propagate_mode) { if (!_subview_stripable) { return; } Strip* strip = 0; Pot* vpot = 0; std::string* pending_display = 0; if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position)) { return; } boost::shared_ptr control= pc.lock (); bool reset_all = false; if (propagate_mode && reset_all) { // @TODO: this line can never be reached due to reset_all being set to false. What was intended here? strip->surface()->subview_mode_changed (); } if (control) { float val = control->get_value(); if (control == _subview_stripable->comp_mode_controllable ()) { pending_display[1] = _subview_stripable->comp_mode_name (val); } else { do_parameter_display(pending_display[1], control->desc(), val, strip, true); } /* update pot/encoder */ strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap)); } } SendsSubview::SendsSubview(MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) : Subview(mcp, subview_stripable) {} SendsSubview::~SendsSubview() {} bool SendsSubview::subview_mode_would_be_ok (boost::shared_ptr r, std::string& reason_why_not) { if (r && r->send_level_controllable (0)) { return true; } reason_why_not = "no sends for selected track/bus"; return false; } void SendsSubview::update_global_buttons() { _mcp.update_global_button (Button::Send, on); _mcp.update_global_button (Button::Plugin, off); _mcp.update_global_button (Button::Eq, off); _mcp.update_global_button (Button::Dyn, off); _mcp.update_global_button (Button::Track, off); _mcp.update_global_button (Button::Pan, off); } void SendsSubview::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2]) { const uint32_t global_strip_position = _mcp.global_index (*strip); store_pointers(strip, vpot, pending_display, global_strip_position); if (!_subview_stripable) { return; } boost::shared_ptr pc = _subview_stripable->send_level_controllable (global_strip_position); if (!pc) { /* nothing to control */ vpot->set_control (boost::shared_ptr()); pending_display[0] = std::string(); pending_display[1] = std::string(); return; } pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&SendsSubview::notify_send_level_change, this, global_strip_position, false), ui_context()); vpot->set_control (pc); pending_display[0] = PBD::short_version (_subview_stripable->send_name (global_strip_position), 6); notify_send_level_change (global_strip_position, true); } void SendsSubview::notify_send_level_change (uint32_t global_strip_position, bool force) { if (!_subview_stripable) { return; } Strip* strip = 0; Pot* vpot = 0; std::string* pending_display = 0; if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position)) { return; } boost::shared_ptr control = _subview_stripable->send_level_controllable (global_strip_position); if (!control) { return; } if (control) { float val = control->get_value(); do_parameter_display(pending_display[1], control->desc(), val, strip, false); if (vpot->control() == control) { /* update pot/encoder */ strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap)); } } } void SendsSubview::handle_vselect_event(uint32_t global_strip_position) { /* Send mode: press enables/disables the relevant * send, but the vpot is bound to the send-level so we * need to lookup the enable/disable control * explicitly. */ if (!_subview_stripable) { return; } Strip* strip = 0; Pot* vpot = 0; std::string* pending_display = 0; if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position)) { return; } boost::shared_ptr control = _subview_stripable->send_enable_controllable(global_strip_position); if (control) { bool currently_enabled = (bool) control->get_value(); Controllable::GroupControlDisposition gcd; if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) { gcd = Controllable::InverseGroup; } else { gcd = Controllable::UseGroup; } control->set_value (!currently_enabled, gcd); if (currently_enabled) { /* we just turned it off */ pending_display[1] = "off"; } else { /* we just turned it on, show the level */ control = _subview_stripable->send_level_controllable (global_strip_position); do_parameter_display(pending_display[1], control->desc(), control->get_value(), strip, false); } } } TrackViewSubview::TrackViewSubview(MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) : Subview(mcp, subview_stripable) {} TrackViewSubview::~TrackViewSubview() {} bool TrackViewSubview::subview_mode_would_be_ok (boost::shared_ptr r, std::string& reason_why_not) { if (r) { return true; } reason_why_not = "no track view possible"; return false; } void TrackViewSubview::update_global_buttons() { _mcp.update_global_button (Button::Send, off); _mcp.update_global_button (Button::Plugin, off); _mcp.update_global_button (Button::Eq, off); _mcp.update_global_button (Button::Dyn, off); _mcp.update_global_button (Button::Track, on); _mcp.update_global_button (Button::Pan, off); } void TrackViewSubview::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2]) { const uint32_t global_strip_position = _mcp.global_index (*strip); store_pointers(strip, vpot, pending_display, global_strip_position); if (global_strip_position > 4) { /* nothing to control */ vpot->set_control (boost::shared_ptr()); pending_display[0] = std::string(); pending_display[1] = std::string(); return; } if (!_subview_stripable) { return; } boost::shared_ptr pc; boost::shared_ptr track = boost::dynamic_pointer_cast (_subview_stripable); switch (global_strip_position) { case 0: pc = _subview_stripable->trim_control (); if (pc) { pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, TrimAutomation, global_strip_position, false), ui_context()); pending_display[0] = "Trim"; notify_change (TrimAutomation, global_strip_position, true); } break; case 1: if (track) { pc = track->monitoring_control(); if (pc) { pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, MonitoringAutomation, global_strip_position, false), ui_context()); pending_display[0] = "Mon"; notify_change (MonitoringAutomation, global_strip_position, true); } } break; case 2: pc = _subview_stripable->solo_isolate_control (); if (pc) { pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, SoloIsolateAutomation, global_strip_position, false), ui_context()); notify_change (SoloIsolateAutomation, global_strip_position, true); pending_display[0] = "S-Iso"; } break; case 3: pc = _subview_stripable->solo_safe_control (); if (pc) { pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, SoloSafeAutomation, global_strip_position, false), ui_context()); notify_change (SoloSafeAutomation, global_strip_position, true); pending_display[0] = "S-Safe"; } break; case 4: pc = _subview_stripable->phase_control(); if (pc) { pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, PhaseAutomation, global_strip_position, false), ui_context()); notify_change (PhaseAutomation, global_strip_position, true); pending_display[0] = "Phase"; } break; } if (!pc) { pending_display[0] = std::string(); pending_display[1] = std::string(); return; } vpot->set_control (pc); } void TrackViewSubview::notify_change (AutomationType type, uint32_t global_strip_position, bool force_update) { if (!_subview_stripable) { return; } Strip* strip = 0; Pot* vpot = 0; std::string* pending_display = 0; if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position)) { return; } boost::shared_ptr control; boost::shared_ptr track = boost::dynamic_pointer_cast (_subview_stripable); bool screen_hold = false; switch (type) { case TrimAutomation: control = _subview_stripable->trim_control(); screen_hold = true; break; case SoloIsolateAutomation: control = _subview_stripable->solo_isolate_control (); break; case SoloSafeAutomation: control = _subview_stripable->solo_safe_control (); break; case MonitoringAutomation: if (track) { control = track->monitoring_control(); screen_hold = true; } break; case PhaseAutomation: control = _subview_stripable->phase_control (); screen_hold = true; break; default: break; } if (control) { float val = control->get_value(); do_parameter_display(pending_display[1], control->desc(), val, strip, screen_hold); /* update pot/encoder */ strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap)); } } PluginSubview::PluginSubview(MackieControlProtocol& mcp, boost::shared_ptr subview_stripable) : Subview(mcp, subview_stripable) { _plugin_subview_state = boost::shared_ptr(new PluginSelect (*this)); connect_processors_changed_signal(); } PluginSubview::~PluginSubview() {} void PluginSubview::connect_processors_changed_signal() { boost::shared_ptr route = boost::dynamic_pointer_cast (_subview_stripable); if (route) { route->processors_changed.connect(_subview_connections, MISSING_INVALIDATOR, boost::bind (&PluginSubview::handle_processors_changed, this), ui_context()); } } void PluginSubview::handle_processors_changed() { _mcp.redisplay_subview_mode(); } bool PluginSubview::subview_mode_would_be_ok (boost::shared_ptr r, std::string& reason_why_not) { if (r) { boost::shared_ptr route = boost::dynamic_pointer_cast (r); if (route && route->nth_plugin(0)) { return true; } } reason_why_not = "no plugins in selected track/bus"; return false; } void PluginSubview::update_global_buttons() { _mcp.update_global_button (Button::Send, off); _mcp.update_global_button (Button::Plugin, on); _mcp.update_global_button (Button::Eq, off); _mcp.update_global_button (Button::Dyn, off); _mcp.update_global_button (Button::Track, off); _mcp.update_global_button (Button::Pan, off); } bool PluginSubview::permit_flipping_faders_and_pots() { return _plugin_subview_state->permit_flipping_faders_and_pots(); } void PluginSubview::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2]) { const uint32_t global_strip_position = _mcp.global_index (*strip); store_pointers(strip, vpot, pending_display, global_strip_position); _plugin_subview_state->setup_vpot(strip, vpot, pending_display, global_strip_position, _subview_stripable); } void PluginSubview::handle_vselect_event(uint32_t global_strip_position) { _plugin_subview_state->handle_vselect_event(global_strip_position, _subview_stripable); } bool PluginSubview::handle_cursor_right_press() { return _plugin_subview_state->handle_cursor_right_press(); } bool PluginSubview::handle_cursor_left_press() { return _plugin_subview_state->handle_cursor_left_press(); } void PluginSubview::set_state(boost::shared_ptr new_state) { _plugin_subview_state = new_state; const uint32_t num_strips = _strips_over_all_surfaces.size(); for (uint32_t strip_index = 0; strip_index < num_strips; strip_index++) { Strip* strip = 0; Pot* vpot = 0; std::string* pending_display = 0; if (!retrieve_pointers(&strip, &vpot, &pending_display, strip_index)) { return; } _plugin_subview_state->setup_vpot(strip, vpot, pending_display, strip_index, _subview_stripable); } } PluginSubviewState::PluginSubviewState(PluginSubview& context) : _context(context) , _bank_size(context.mcp().n_strips()) , _current_bank(0) { } PluginSubviewState::~PluginSubviewState() {} std::string PluginSubviewState::shorten_display_text(const std::string& text, std::string::size_type target_length) { if (text.length() <= target_length) { return text; } return PBD::short_version (text, target_length); } bool PluginSubviewState::handle_cursor_right_press() { _current_bank = _current_bank + 1; bank_changed(); return true; } bool PluginSubviewState::handle_cursor_left_press() { if (_current_bank >= 1) { _current_bank = _current_bank - 1; } bank_changed(); return true; } uint32_t PluginSubviewState::calculate_virtual_strip_position(uint32_t strip_index) const { return _current_bank * _bank_size + strip_index; } PluginSelect::PluginSelect(PluginSubview& context) : PluginSubviewState(context) {} PluginSelect::~PluginSelect() {} void PluginSelect::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2], uint32_t global_strip_position, boost::shared_ptr subview_stripable) { if (!subview_stripable) { return; } boost::shared_ptr route = boost::dynamic_pointer_cast (subview_stripable); if (!route) { return; } uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position); boost::shared_ptr plugin = route->nth_plugin(virtual_strip_position); if (plugin) { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("plugin of strip %1 is %2\n", global_strip_position, plugin->display_name())); pending_display[0] = string_compose("Ins%1Pl", virtual_strip_position + 1); pending_display[1] = PluginSubviewState::shorten_display_text(plugin->display_name(), 6); } else { pending_display[0] = ""; pending_display[1] = ""; } } void PluginSelect::handle_vselect_event(uint32_t global_strip_position, boost::shared_ptr subview_stripable) { /* PluginSelect mode: press selects the plugin shown on the strip's LCD */ if (!subview_stripable) { return; } boost::shared_ptr route = boost::dynamic_pointer_cast (subview_stripable); if (!route) { return; } uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position); boost::shared_ptr processor = route->nth_plugin(virtual_strip_position); boost::shared_ptr plugin = boost::dynamic_pointer_cast(processor); processor->ShowUI(); if (plugin) { _context.set_state (boost::shared_ptr (new PluginEdit (_context, boost::weak_ptr(plugin)))); } } void PluginSelect::bank_changed() { _context.mcp().redisplay_subview_mode(); } PluginEdit::PluginEdit(PluginSubview& context, boost::weak_ptr weak_subview_plugin_insert) : PluginSubviewState(context) , _weak_subview_plugin_insert(weak_subview_plugin_insert) { try { init(); } catch (...) { throw failed_constructor(); } } PluginEdit::~PluginEdit() {} void PluginEdit::init() { boost::shared_ptr plugin_insert = _weak_subview_plugin_insert.lock(); _weak_subview_plugin = boost::weak_ptr(plugin_insert->plugin()); boost::shared_ptr subview_plugin = _weak_subview_plugin.lock(); _plugin_input_parameter_indices.clear(); if (!subview_plugin) { return; } bool ok = false; // put only input controls into a vector uint32_t nplug_params = subview_plugin->parameter_count(); for (uint32_t ppi = 0; ppi < nplug_params; ++ppi) { uint32_t controlid = subview_plugin->nth_parameter(ppi, ok); if (!ok) { continue; } if (subview_plugin->parameter_is_input(controlid)) { _plugin_input_parameter_indices.push_back(ppi); } } } boost::shared_ptr PluginEdit::parameter_control(uint32_t global_strip_position) const { uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position); if (virtual_strip_position >= _plugin_input_parameter_indices.size()) { return boost::shared_ptr(); } boost::shared_ptr plugin_insert = _weak_subview_plugin_insert.lock(); boost::shared_ptr subview_plugin = _weak_subview_plugin.lock(); if (!plugin_insert || !subview_plugin) { return boost::shared_ptr(); } uint32_t plugin_parameter_index = _plugin_input_parameter_indices[virtual_strip_position]; bool ok = false; uint32_t controlid = subview_plugin->nth_parameter(plugin_parameter_index, ok); if (!ok) { return boost::shared_ptr(); } return plugin_insert->automation_control(Evoral::Parameter(PluginAutomation, 0, controlid)); } bool PluginEdit::plugin_went_away() const { // is shared_ptr reset? boost::shared_ptr plugin_insert = _weak_subview_plugin_insert.lock(); boost::shared_ptr subview_plugin = _weak_subview_plugin.lock(); if (!plugin_insert || !subview_plugin) { return true; } // is plugin not registered with stripable any more? boost::shared_ptr route = boost::dynamic_pointer_cast (_context.subview_stripable()); if (!route) { return true; } if (!route->processor_by_id(plugin_insert->id())) { // plugin_insert is not registered with route any more -> it was removed return true; } return false; } void PluginEdit::switch_to_plugin_select_state() { _context.set_state (boost::shared_ptr (new PluginSelect (_context))); } void PluginEdit::setup_vpot( Strip* strip, Pot* vpot, std::string pending_display[2], uint32_t global_strip_position, boost::shared_ptr subview_stripable) { if (plugin_went_away()) { switch_to_plugin_select_state(); return; } boost::shared_ptr c = parameter_control(global_strip_position); if (!c) { vpot->set_control (boost::shared_ptr()); pending_display[0] = std::string(); pending_display[1] = std::string(); return; } c->Changed.connect (_context.subview_connections(), MISSING_INVALIDATOR, boost::bind (&PluginEdit::notify_parameter_change, this, strip, vpot, pending_display, global_strip_position), ui_context()); vpot->set_control (c); pending_display[0] = PluginSubviewState::shorten_display_text(c->desc().label, 6); notify_parameter_change (strip, vpot, pending_display, global_strip_position); } void PluginEdit::notify_parameter_change(Strip* strip, Pot* vpot, std::string pending_display[2], uint32_t global_strip_position) { boost::shared_ptr control = parameter_control(global_strip_position); if (!control) { return; } float val = control->get_value(); _context.do_parameter_display(pending_display[1], control->desc(), val, strip, false); if (vpot->control() == control) { /* update pot/encoder */ strip->surface()->write(vpot->set (control->internal_to_interface (val), true, Pot::wrap)); } } void PluginEdit::handle_vselect_event(uint32_t global_strip_position, boost::shared_ptr subview_stripable) { } void PluginEdit::bank_changed() { _context.mcp().redisplay_subview_mode(); }