69067b9d99
Main features: Plugin (Select & Edit) 1. Plugin Select: When a track is selected that has PluginInserts, pushing the "Plug-In" button on a mackie will list these across the strips. Clicking a vpot of a strip enables editing the parameters of this selected plugin. 2. Plugin Edit: When a Plugin is selected for editing, the input parameters of the plugin are shown across the channel strips and the vpot is assigned the corresponsing AutomationControl for the parameter. Minor features - When the number of plugins or the number of parameters exceeds the number of strips available on the surface, one can flip through "pages" of views using the Cursor Left and Right keys (this logic I took from http://www.emagic.de/media/support/content/manuals/LogicControl_en.pdf) - When in the Plugin Select mode, rearranging the plugins in the mixer strip is reflected on the surface. - When in Plugin Edit mode, rearranging the plugins in the mixer strip still retains the edit view of the selected plugin (rearranging does not take away the current subview) - When removing a plugin in the mixer strip, this is reflected in Plugin Select, while the view jumps to Pan/Surround (the None subview) when in Plugin Edit mode. - Removing a track resets the subview to None - When in a Subview that is track-specific (Track, EQ, Send, Plug-In, Inst), selecting a different track retains the subview but updates the channel displays and vpot assignments accordingly. When in Plugin Edit mode for track A, and track B is selected, it changes to Plugin Select mode for track B (if plugins are present).
1234 lines
35 KiB
C++
1234 lines
35 KiB
C++
/*
|
|
* Copyright (C) 2006-2007 John Anderson
|
|
* Copyright (C) 2007-2010 David Robillard <d@drobilla.net>
|
|
* Copyright (C) 2007-2017 Paul Davis <paul@linuxaudiosystems.com>
|
|
* Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
|
|
* Copyright (C) 2015-2016 Len Ovens <len@ovenwerks.net>
|
|
* Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
|
|
* Copyright (C) 2016-2018 Ben Loftis <ben@harrisonconsoles.com>
|
|
*
|
|
* 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<Subview> SubviewFactory::create_subview(
|
|
SubViewMode svm,
|
|
MackieControlProtocol& mcp,
|
|
boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
|
|
{
|
|
switch (svm) {
|
|
case SubViewMode::EQ:
|
|
return boost::make_shared<EQSubview>(mcp, subview_stripable);
|
|
case SubViewMode::Dynamics:
|
|
return boost::make_shared<DynamicsSubview>(mcp, subview_stripable);
|
|
case SubViewMode::Sends:
|
|
return boost::make_shared<SendsSubview>(mcp, subview_stripable);
|
|
case SubViewMode::TrackView:
|
|
return boost::make_shared<TrackViewSubview>(mcp, subview_stripable);
|
|
case SubViewMode::Plugin:
|
|
return boost::make_shared<PluginSubview>(mcp, subview_stripable);
|
|
case SubViewMode::None:
|
|
default:
|
|
return boost::make_shared<NoneSubview>(mcp, subview_stripable);
|
|
}
|
|
}
|
|
|
|
|
|
Subview::Subview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> 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<Pot*>::iterator iter = _strip_vpots_over_all_surfaces.begin(); iter != _strip_vpots_over_all_surfaces.end(); ) {
|
|
std::vector<Pot*>::iterator tmp;
|
|
|
|
tmp = iter;
|
|
++tmp;
|
|
|
|
if (*iter != 0)
|
|
{
|
|
(*iter)->set_control (boost::shared_ptr<AutomationControl>());
|
|
}
|
|
|
|
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<AutomationControl> 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 (SubViewMode mode, boost::shared_ptr<Stripable> r, std::string& reason_why_not)
|
|
{
|
|
switch (mode) {
|
|
case SubViewMode::None:
|
|
return NoneSubview::subview_mode_would_be_ok(r, reason_why_not);
|
|
case SubViewMode::Sends:
|
|
return SendsSubview::subview_mode_would_be_ok(r, reason_why_not);
|
|
case SubViewMode::EQ:
|
|
return EQSubview::subview_mode_would_be_ok(r, reason_why_not);
|
|
case SubViewMode::Dynamics:
|
|
return DynamicsSubview::subview_mode_would_be_ok(r, reason_why_not);
|
|
case SubViewMode::TrackView:
|
|
return TrackViewSubview::subview_mode_would_be_ok(r, reason_why_not);
|
|
case SubViewMode::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<ARDOUR::Stripable> subview_stripable)
|
|
: Subview(mcp, subview_stripable)
|
|
{}
|
|
|
|
NoneSubview::~NoneSubview()
|
|
{}
|
|
|
|
bool NoneSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> 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<ARDOUR::Stripable> subview_stripable)
|
|
: Subview(mcp, subview_stripable)
|
|
{}
|
|
|
|
EQSubview::~EQSubview()
|
|
{}
|
|
|
|
bool EQSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> 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<AutomationControl> 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<AutomationControl>(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<AutomationControl>());
|
|
pending_display[0] = std::string();
|
|
pending_display[1] = std::string();
|
|
}
|
|
|
|
notify_change (boost::weak_ptr<AutomationControl>(pc), global_strip_position, true);
|
|
}
|
|
|
|
void EQSubview::notify_change (boost::weak_ptr<ARDOUR::AutomationControl> 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<AutomationControl> 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<ARDOUR::Stripable> subview_stripable)
|
|
: Subview(mcp, subview_stripable)
|
|
{}
|
|
|
|
DynamicsSubview::~DynamicsSubview()
|
|
{}
|
|
|
|
bool DynamicsSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> 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<AutomationControl> tc = _subview_stripable->comp_threshold_controllable ();
|
|
boost::shared_ptr<AutomationControl> sc = _subview_stripable->comp_speed_controllable ();
|
|
boost::shared_ptr<AutomationControl> mc = _subview_stripable->comp_mode_controllable ();
|
|
boost::shared_ptr<AutomationControl> kc = _subview_stripable->comp_makeup_controllable ();
|
|
boost::shared_ptr<AutomationControl> ec = _subview_stripable->comp_enable_controllable ();
|
|
|
|
#ifdef MIXBUS32C //Mixbus32C needs to spill the filter controls into the comp section
|
|
boost::shared_ptr<AutomationControl> hpfc = _subview_stripable->filter_freq_controllable (true);
|
|
boost::shared_ptr<AutomationControl> lpfc = _subview_stripable->filter_freq_controllable (false);
|
|
boost::shared_ptr<AutomationControl> 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::pair<boost::shared_ptr<AutomationControl>, std::string > > available;
|
|
std::vector<AutomationType> 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<AutomationControl>());
|
|
pending_display[0] = std::string();
|
|
pending_display[1] = std::string();
|
|
return;
|
|
}
|
|
|
|
boost::shared_ptr<AutomationControl> 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<AutomationControl>(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<AutomationControl>(pc), global_strip_position, true, false);
|
|
}
|
|
|
|
void
|
|
DynamicsSubview::notify_change (boost::weak_ptr<ARDOUR::AutomationControl> 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<AutomationControl> 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<ARDOUR::Stripable> subview_stripable)
|
|
: Subview(mcp, subview_stripable)
|
|
{}
|
|
|
|
SendsSubview::~SendsSubview()
|
|
{}
|
|
|
|
bool SendsSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> 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<AutomationControl> pc = _subview_stripable->send_level_controllable (global_strip_position);
|
|
|
|
if (!pc) {
|
|
/* nothing to control */
|
|
vpot->set_control (boost::shared_ptr<AutomationControl>());
|
|
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<AutomationControl> 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<AutomationControl> 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<ARDOUR::Stripable> subview_stripable)
|
|
: Subview(mcp, subview_stripable)
|
|
{}
|
|
|
|
TrackViewSubview::~TrackViewSubview()
|
|
{}
|
|
|
|
bool TrackViewSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> 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<AutomationControl>());
|
|
pending_display[0] = std::string();
|
|
pending_display[1] = std::string();
|
|
return;
|
|
}
|
|
|
|
if (!_subview_stripable) {
|
|
return;
|
|
}
|
|
|
|
boost::shared_ptr<AutomationControl> pc;
|
|
boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (_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<AutomationControl> control;
|
|
boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (_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<ARDOUR::Stripable> subview_stripable)
|
|
: Subview(mcp, subview_stripable)
|
|
{
|
|
_plugin_subview_state = boost::make_shared<PluginSelect>(*this);
|
|
connect_processors_changed_signal();
|
|
}
|
|
|
|
PluginSubview::~PluginSubview()
|
|
{}
|
|
|
|
void PluginSubview::connect_processors_changed_signal()
|
|
{
|
|
boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (_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<ARDOUR::Stripable> r, std::string& reason_why_not)
|
|
{
|
|
if (r) {
|
|
boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (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<PluginSubviewState> 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<ARDOUR::Stripable> subview_stripable)
|
|
{
|
|
if (!subview_stripable) {
|
|
return;
|
|
}
|
|
|
|
boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (subview_stripable);
|
|
if (!route) {
|
|
return;
|
|
}
|
|
|
|
uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position);
|
|
|
|
boost::shared_ptr<Processor> 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<ARDOUR::Stripable> subview_stripable)
|
|
{
|
|
/* PluginSelect mode: press selects the plugin shown on the strip's LCD */
|
|
if (!subview_stripable) {
|
|
return;
|
|
}
|
|
|
|
boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (subview_stripable);
|
|
if (!route) {
|
|
return;
|
|
}
|
|
|
|
uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position);
|
|
|
|
boost::shared_ptr<Processor> processor = route->nth_plugin(virtual_strip_position);
|
|
boost::shared_ptr<PluginInsert> plugin = boost::dynamic_pointer_cast<PluginInsert>(processor);
|
|
processor->ShowUI();
|
|
if (plugin) {
|
|
_context.set_state(boost::make_shared<PluginEdit>(_context, boost::weak_ptr<PluginInsert>(plugin)));
|
|
}
|
|
}
|
|
|
|
void PluginSelect::bank_changed()
|
|
{
|
|
_context.mcp().redisplay_subview_mode();
|
|
}
|
|
|
|
|
|
|
|
PluginEdit::PluginEdit(PluginSubview& context, boost::weak_ptr<PluginInsert> 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<PluginInsert> plugin_insert = _weak_subview_plugin_insert.lock();
|
|
_weak_subview_plugin = boost::weak_ptr<ARDOUR::Plugin>(plugin_insert->plugin());
|
|
boost::shared_ptr<ARDOUR::Plugin> 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<AutomationControl> 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<AutomationControl>();
|
|
}
|
|
|
|
boost::shared_ptr<PluginInsert> plugin_insert = _weak_subview_plugin_insert.lock();
|
|
boost::shared_ptr<ARDOUR::Plugin> subview_plugin = _weak_subview_plugin.lock();
|
|
if (!plugin_insert || !subview_plugin) {
|
|
return boost::shared_ptr<AutomationControl>();
|
|
}
|
|
|
|
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<AutomationControl>();
|
|
}
|
|
return plugin_insert->automation_control(Evoral::Parameter(PluginAutomation, 0, controlid));
|
|
}
|
|
|
|
bool PluginEdit::plugin_went_away() const
|
|
{
|
|
// is shared_ptr reset?
|
|
boost::shared_ptr<PluginInsert> plugin_insert = _weak_subview_plugin_insert.lock();
|
|
boost::shared_ptr<ARDOUR::Plugin> 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> route = boost::dynamic_pointer_cast<Route> (_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::make_shared<PluginSelect>(_context));
|
|
}
|
|
|
|
void PluginEdit::setup_vpot(
|
|
Strip* strip,
|
|
Pot* vpot,
|
|
std::string pending_display[2],
|
|
uint32_t global_strip_position,
|
|
boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
|
|
{
|
|
if (plugin_went_away()) {
|
|
switch_to_plugin_select_state();
|
|
return;
|
|
}
|
|
|
|
boost::shared_ptr<AutomationControl> c = parameter_control(global_strip_position);
|
|
|
|
if (!c) {
|
|
vpot->set_control (boost::shared_ptr<AutomationControl>());
|
|
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<AutomationControl> 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<ARDOUR::Stripable> subview_stripable)
|
|
{
|
|
}
|
|
|
|
void PluginEdit::bank_changed()
|
|
{
|
|
_context.mcp().redisplay_subview_mode();
|
|
}
|