1 /*
2  * Copyright (C) 2006-2007 John Anderson
3  * Copyright (C) 2007-2010 David Robillard <d@drobilla.net>
4  * Copyright (C) 2007-2017 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2015-2016 Len Ovens <len@ovenwerks.net>
7  * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
8  * Copyright (C) 2016-2018 Ben Loftis <ben@harrisonconsoles.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 
26 #include "pbd/convert.h"
27 #include "pbd/failed_constructor.h"
28 
29 #include "ardour/debug.h"
30 #include "ardour/monitor_control.h"
31 #include "ardour/phase_control.h"
32 #include "ardour/plugin.h"
33 #include "ardour/plugin_insert.h"
34 #include "ardour/route.h"
35 #include "ardour/solo_isolate_control.h"
36 #include "ardour/stripable.h"
37 #include "ardour/track.h"
38 
39 #include "mackie_control_protocol.h"
40 #include "pot.h"
41 #include "strip.h"
42 #include "subview.h"
43 #include "surface.h"
44 
45 using namespace ARDOUR;
46 using namespace ArdourSurface;
47 using namespace Mackie;
48 using namespace PBD;
49 
50 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
51 
52 SubviewFactory* SubviewFactory::_instance = 0;
53 
instance()54 SubviewFactory* SubviewFactory::instance() {
55 	if (!_instance) {
56 		_instance = new SubviewFactory();
57 	}
58 	return _instance;
59 }
60 
SubviewFactory()61 SubviewFactory::SubviewFactory() {};
62 
create_subview(Subview::Mode svm,MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)63 boost::shared_ptr<Subview> SubviewFactory::create_subview(
64 		Subview::Mode svm,
65 		MackieControlProtocol& mcp,
66 		boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
67 {
68 	switch (svm) {
69 		case Subview::EQ:
70 			return boost::shared_ptr<EQSubview>(new EQSubview (mcp, subview_stripable));
71 		case Subview::Dynamics:
72 			return boost::shared_ptr<DynamicsSubview>(new DynamicsSubview (mcp, subview_stripable));
73 		case Subview::Sends:
74 			return boost::shared_ptr<SendsSubview>(new SendsSubview (mcp, subview_stripable));
75 		case Subview::TrackView:
76 			return boost::shared_ptr<TrackViewSubview>(new TrackViewSubview (mcp, subview_stripable));
77 		case Subview::Plugin:
78 			return boost::shared_ptr<PluginSubview>(new PluginSubview (mcp, subview_stripable));
79 		case Subview::None:
80 		default:
81 			return boost::shared_ptr<NoneSubview>(new NoneSubview (mcp, subview_stripable));
82 	}
83 }
84 
85 
Subview(MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)86 Subview::Subview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
87 	: _mcp(mcp)
88 	, _subview_stripable(subview_stripable)
89 {
90 	init_strip_vectors();
91 }
92 
~Subview()93 Subview::~Subview()
94 {
95 	reset_all_vpot_controls();
96 }
97 
98 void
reset_all_vpot_controls()99 Subview::reset_all_vpot_controls()
100 {
101 	for (std::vector<Pot*>::iterator iter = _strip_vpots_over_all_surfaces.begin(); iter != _strip_vpots_over_all_surfaces.end(); ) {
102 		std::vector<Pot*>::iterator tmp;
103 
104 		tmp = iter;
105 		++tmp;
106 
107 		if (*iter != 0)
108 		{
109 			(*iter)->set_control (boost::shared_ptr<AutomationControl>());
110 		}
111 
112 		iter = tmp;
113 	}
114 }
115 
handle_vselect_event(uint32_t global_strip_position)116 void Subview::handle_vselect_event(uint32_t global_strip_position)
117 {
118 	Strip* strip = 0;
119 	Pot* vpot = 0;
120 	std::string* pending_display = 0;
121 	if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position))
122 	{
123 		return;
124 	}
125 
126 	boost::shared_ptr<AutomationControl> control = vpot->control ();
127 	if (!control) {
128 		return;
129 	}
130 
131 	Controllable::GroupControlDisposition gcd;
132 	if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
133 		gcd = Controllable::InverseGroup;
134 	} else {
135 		gcd = Controllable::UseGroup;
136 	}
137 
138 	if (control->toggled()) {
139 		if (control->toggled()) {
140 			control->set_value (!control->get_value(), gcd);
141 		}
142 
143 	} else if (control->desc().enumeration || control->desc().integer_step) {
144 
145 		double val = control->get_value ();
146 		if (val <= control->upper() - 1.0) {
147 			control->set_value (val + 1.0, gcd);
148 		} else {
149 			control->set_value (control->lower(), gcd);
150 		}
151 	}
152 }
153 
154 bool
subview_mode_would_be_ok(Subview::Mode mode,boost::shared_ptr<Stripable> r,std::string & reason_why_not)155 Subview::subview_mode_would_be_ok (Subview::Mode mode, boost::shared_ptr<Stripable> r, std::string& reason_why_not)
156 {
157 	switch (mode) {
158 	case Subview::None:
159 		return NoneSubview::subview_mode_would_be_ok(r, reason_why_not);
160 	case Subview::Sends:
161 		return SendsSubview::subview_mode_would_be_ok(r, reason_why_not);
162 	case Subview::EQ:
163 		return EQSubview::subview_mode_would_be_ok(r, reason_why_not);
164 	case Subview::Dynamics:
165 		return DynamicsSubview::subview_mode_would_be_ok(r, reason_why_not);
166 	case Subview::TrackView:
167 		return TrackViewSubview::subview_mode_would_be_ok(r, reason_why_not);
168 	case Subview::Plugin:
169 		return PluginSubview::subview_mode_would_be_ok(r, reason_why_not);
170 	}
171 
172 	return false;
173 }
174 
175 void
notify_subview_stripable_deleted()176 Subview::notify_subview_stripable_deleted ()
177 {
178 	_subview_stripable.reset ();
179 }
180 
181 void
init_strip_vectors()182 Subview::init_strip_vectors()
183 {
184 	_strips_over_all_surfaces.resize(_mcp.n_strips(), 0);
185 	_strip_vpots_over_all_surfaces.resize(_mcp.n_strips(), 0);
186 	_strip_pending_displays_over_all_surfaces.resize(_mcp.n_strips(), 0);
187 }
188 
189 void
store_pointers(Strip * strip,Pot * vpot,std::string * pending_display,uint32_t global_strip_position)190 Subview::store_pointers(Strip* strip, Pot* vpot, std::string* pending_display, uint32_t global_strip_position)
191 {
192 	if (global_strip_position >= _strips_over_all_surfaces.size() ||
193 		global_strip_position >= _strip_vpots_over_all_surfaces.size() ||
194 		global_strip_position >= _strip_pending_displays_over_all_surfaces.size())
195 	{
196 		return;
197 	}
198 
199 	_strips_over_all_surfaces[global_strip_position] = strip;
200 	_strip_vpots_over_all_surfaces[global_strip_position] = vpot;
201 	_strip_pending_displays_over_all_surfaces[global_strip_position] = pending_display;
202 }
203 
204 bool
retrieve_pointers(Strip ** strip,Pot ** vpot,std::string ** pending_display,uint32_t global_strip_position)205 Subview::retrieve_pointers(Strip** strip, Pot** vpot, std::string** pending_display, uint32_t global_strip_position)
206 {
207 	if (global_strip_position >= _strips_over_all_surfaces.size() ||
208 		global_strip_position >= _strip_vpots_over_all_surfaces.size() ||
209 		global_strip_position >= _strip_pending_displays_over_all_surfaces.size())
210 	{
211 		return false;
212 	}
213 
214 	*strip = _strips_over_all_surfaces[global_strip_position];
215 	*vpot = _strip_vpots_over_all_surfaces[global_strip_position];
216 	*pending_display = _strip_pending_displays_over_all_surfaces[global_strip_position];
217 
218 	if (!strip || !vpot || !pending_display)
219 	{
220 		return false;
221 	}
222 
223 	return true;
224 }
225 
do_parameter_display(std::string & display,const ParameterDescriptor & pd,float param_val,Strip * strip,bool screen_hold)226 void Subview::do_parameter_display(std::string& display, const ParameterDescriptor& pd, float param_val, Strip* strip, bool screen_hold)
227 {
228 	display = Strip::format_paramater_for_display(
229 			pd,
230 			param_val,
231 			strip->stripable(),
232 			screen_hold
233 		);
234 
235 	if (screen_hold) {
236 		/* we just queued up a parameter to be displayed.
237 			1 second from now, switch back to vpot mode display.
238 		*/
239 		strip->block_vpot_mode_display_for (1000);
240 	}
241 }
242 
243 
244 
NoneSubview(MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)245 NoneSubview::NoneSubview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
246 	: Subview(mcp, subview_stripable)
247 {}
248 
~NoneSubview()249 NoneSubview::~NoneSubview()
250 {}
251 
subview_mode_would_be_ok(boost::shared_ptr<ARDOUR::Stripable> r,std::string & reason_why_not)252 bool NoneSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> r, std::string& reason_why_not)
253 {
254 	// always possible
255 	return true;
256 }
257 
update_global_buttons()258 void NoneSubview::update_global_buttons()
259 {
260 	_mcp.update_global_button (Button::Send, off);
261 	_mcp.update_global_button (Button::Plugin, off);
262 	_mcp.update_global_button (Button::Eq, off);
263 	_mcp.update_global_button (Button::Dyn, off);
264 	_mcp.update_global_button (Button::Track, off);
265 	_mcp.update_global_button (Button::Pan, on);
266 }
267 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2])268 void NoneSubview::setup_vpot(
269 		Strip* strip,
270 		Pot* vpot,
271 		std::string pending_display[2])
272 {
273 	// nothing to be done here. All pots are set in strip.cc
274 }
275 
276 
277 
EQSubview(MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)278 EQSubview::EQSubview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
279 	: Subview(mcp, subview_stripable)
280 {}
281 
~EQSubview()282 EQSubview::~EQSubview()
283 {}
284 
subview_mode_would_be_ok(boost::shared_ptr<ARDOUR::Stripable> r,std::string & reason_why_not)285 bool EQSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> r, std::string& reason_why_not)
286 {
287 	if (r && r->eq_band_cnt() > 0) {
288 		return true;
289 	}
290 
291 	reason_why_not = "no EQ in the track/bus";
292 	return false;
293 }
294 
update_global_buttons()295 void EQSubview::update_global_buttons()
296 {
297 	_mcp.update_global_button (Button::Send, off);
298 	_mcp.update_global_button (Button::Plugin, off);
299 	_mcp.update_global_button (Button::Eq, on);
300 	_mcp.update_global_button (Button::Dyn, off);
301 	_mcp.update_global_button (Button::Track, off);
302 	_mcp.update_global_button (Button::Pan, off);
303 }
304 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2])305 void EQSubview::setup_vpot(
306 		Strip* strip,
307 		Pot* vpot,
308 		std::string pending_display[2])
309 {
310 	const uint32_t global_strip_position = _mcp.global_index (*strip);
311 	store_pointers(strip, vpot, pending_display, global_strip_position);
312 
313 	if (!_subview_stripable) {
314 		return;
315 	}
316 
317 
318 	boost::shared_ptr<AutomationControl> pc;
319 	std::string pot_id;
320 
321 #ifdef MIXBUS
322 	int eq_band = -1;
323 	std::string band_name;
324 	if (_subview_stripable->is_input_strip ()) {
325 
326 #ifdef MIXBUS32C
327 		switch (global_strip_position) {
328 			case 0:
329 			case 2:
330 			case 4:
331 			case 6:
332 				eq_band = global_strip_position / 2;
333 				pc = _subview_stripable->eq_freq_controllable (eq_band);
334 				band_name = _subview_stripable->eq_band_name (eq_band);
335 				pot_id = band_name + "Freq";
336 				break;
337 			case 1:
338 			case 3:
339 			case 5:
340 			case 7:
341 				eq_band = global_strip_position / 2;
342 				pc = _subview_stripable->eq_gain_controllable (eq_band);
343 				band_name = _subview_stripable->eq_band_name (eq_band);
344 				pot_id = band_name + "Gain";
345 				break;
346 			case 8:
347 				pc = _subview_stripable->eq_shape_controllable(0);  //low band "bell" button
348 				band_name = "lo";
349 				pot_id = band_name + " Shp";
350 				break;
351 			case 9:
352 				pc = _subview_stripable->eq_shape_controllable(3);  //high band "bell" button
353 				band_name = "hi";
354 				pot_id = band_name + " Shp";
355 				break;
356 			case 10:
357 				pc = _subview_stripable->eq_enable_controllable();
358 				pot_id = "EQ";
359 				break;
360 		}
361 
362 #else  //regular Mixbus channel EQ
363 
364 		switch (global_strip_position) {
365 			case 0:
366 			case 2:
367 			case 4:
368 				eq_band = global_strip_position / 2;
369 				pc = _subview_stripable->eq_gain_controllable (eq_band);
370 				band_name = _subview_stripable->eq_band_name (eq_band);
371 				pot_id = band_name + "Gain";
372 				break;
373 			case 1:
374 			case 3:
375 			case 5:
376 				eq_band = global_strip_position / 2;
377 				pc = _subview_stripable->eq_freq_controllable (eq_band);
378 				band_name = _subview_stripable->eq_band_name (eq_band);
379 				pot_id = band_name + "Freq";
380 				break;
381 			case 6:
382 				pc = _subview_stripable->eq_enable_controllable();
383 				pot_id = "EQ";
384 				break;
385 			case 7:
386 				pc = _subview_stripable->filter_freq_controllable(true);
387 				pot_id = "HP Freq";
388 				break;
389 		}
390 
391 #endif
392 
393 	} else {  //mixbus or master bus ( these are currently the same for MB & 32C )
394 		switch (global_strip_position) {
395 			case 0:
396 			case 1:
397 			case 2:
398 				eq_band = global_strip_position;
399 				pc = _subview_stripable->eq_gain_controllable (eq_band);
400 				band_name = _subview_stripable->eq_band_name (eq_band);
401 				pot_id = band_name + "Gain";
402 				break;
403 		}
404 	}
405 #endif
406 
407 	//If a controllable was found, connect it up, and put the labels in the display.
408 	if (pc) {
409 		pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&EQSubview::notify_change, this, boost::weak_ptr<AutomationControl>(pc), global_strip_position, false), ui_context());
410 		vpot->set_control (pc);
411 
412 		if (!pot_id.empty()) {
413 			pending_display[0] = pot_id;
414 		} else {
415 			pending_display[0] = std::string();
416 		}
417 
418 	} else {  //no controllable was found;  just clear this knob
419 		vpot->set_control (boost::shared_ptr<AutomationControl>());
420 		pending_display[0] = std::string();
421 		pending_display[1] = std::string();
422 	}
423 
424 	notify_change (boost::weak_ptr<AutomationControl>(pc), global_strip_position, true);
425 }
426 
notify_change(boost::weak_ptr<ARDOUR::AutomationControl> pc,uint32_t global_strip_position,bool force)427 void EQSubview::notify_change (boost::weak_ptr<ARDOUR::AutomationControl> pc, uint32_t global_strip_position, bool force)
428 {
429 	if (!_subview_stripable) {
430 		return;
431 	}
432 
433 	Strip* strip = 0;
434 	Pot* vpot = 0;
435 	std::string* pending_display = 0;
436 	if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position))
437 	{
438 		return;
439 	}
440 
441 	boost::shared_ptr<AutomationControl> control = pc.lock ();
442 	if (control) {
443 		float val = control->get_value();
444 		do_parameter_display(pending_display[1], control->desc(), val, strip, true);
445 		/* update pot/encoder */
446 		strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap));
447 	}
448 }
449 
450 
451 
DynamicsSubview(MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)452 DynamicsSubview::DynamicsSubview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
453 	: Subview(mcp, subview_stripable)
454 {}
455 
~DynamicsSubview()456 DynamicsSubview::~DynamicsSubview()
457 {}
458 
subview_mode_would_be_ok(boost::shared_ptr<ARDOUR::Stripable> r,std::string & reason_why_not)459 bool DynamicsSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> r, std::string& reason_why_not)
460 {
461 	if (r && r->comp_enable_controllable()) {
462 		return true;
463 	}
464 
465 	reason_why_not = "no dynamics in selected track/bus";
466 	return false;
467 }
468 
update_global_buttons()469 void DynamicsSubview::update_global_buttons()
470 {
471 	_mcp.update_global_button (Button::Send, off);
472 	_mcp.update_global_button (Button::Plugin, off);
473 	_mcp.update_global_button (Button::Eq, off);
474 	_mcp.update_global_button (Button::Dyn, on);
475 	_mcp.update_global_button (Button::Track, off);
476 	_mcp.update_global_button (Button::Pan, off);
477 }
478 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2])479 void DynamicsSubview::setup_vpot(
480 		Strip* strip,
481 		Pot* vpot,
482 		std::string pending_display[2])
483 {
484 	const uint32_t global_strip_position = _mcp.global_index (*strip);
485 	store_pointers(strip, vpot, pending_display, global_strip_position);
486 
487 	if (!_subview_stripable) {
488 		return;
489 	}
490 
491 	boost::shared_ptr<AutomationControl> tc = _subview_stripable->comp_threshold_controllable ();
492 	boost::shared_ptr<AutomationControl> sc = _subview_stripable->comp_speed_controllable ();
493 	boost::shared_ptr<AutomationControl> mc = _subview_stripable->comp_mode_controllable ();
494 	boost::shared_ptr<AutomationControl> kc = _subview_stripable->comp_makeup_controllable ();
495 	boost::shared_ptr<AutomationControl> ec = _subview_stripable->comp_enable_controllable ();
496 
497 #ifdef MIXBUS32C	//Mixbus32C needs to spill the filter controls into the comp section
498 	boost::shared_ptr<AutomationControl> hpfc = _subview_stripable->filter_freq_controllable (true);
499 	boost::shared_ptr<AutomationControl> lpfc = _subview_stripable->filter_freq_controllable (false);
500 	boost::shared_ptr<AutomationControl> fec = _subview_stripable->filter_enable_controllable (true); // shared HP/LP
501 #endif
502 
503 	/* we will control the global_strip_position-th available parameter, from the list in the
504 	 * order shown above.
505 	 */
506 
507 	std::vector<std::pair<boost::shared_ptr<AutomationControl>, std::string > > available;
508 	std::vector<AutomationType> params;
509 
510 	if (tc) { available.push_back (std::make_pair (tc, "Thresh")); }
511 	if (sc) { available.push_back (std::make_pair (sc, mc ? _subview_stripable->comp_speed_name (mc->get_value()) : "Speed")); }
512 	if (mc) { available.push_back (std::make_pair (mc, "Mode")); }
513 	if (kc) { available.push_back (std::make_pair (kc, "Makeup")); }
514 	if (ec) { available.push_back (std::make_pair (ec, "on/off")); }
515 
516 #ifdef MIXBUS32C	//Mixbus32C needs to spill the filter controls into the comp section
517 	if (hpfc) { available.push_back (std::make_pair (hpfc, "HPF")); }
518 	if (lpfc) { available.push_back (std::make_pair (lpfc, "LPF")); }
519 	if (fec)  { available.push_back (std::make_pair (fec, "FiltIn")); }
520 #endif
521 
522 	if (global_strip_position >= available.size()) {
523 		/* this knob is not needed to control the available parameters */
524 		vpot->set_control (boost::shared_ptr<AutomationControl>());
525 		pending_display[0] = std::string();
526 		pending_display[1] = std::string();
527 		return;
528 	}
529 
530 	boost::shared_ptr<AutomationControl> pc;
531 
532 	pc = available[global_strip_position].first;
533 	std::string pot_id = available[global_strip_position].second;
534 
535 	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());
536 	vpot->set_control (pc);
537 
538 	if (!pot_id.empty()) {
539 		pending_display[0] = pot_id;
540 	} else {
541 		pending_display[0] = std::string();
542 	}
543 
544 	notify_change (boost::weak_ptr<AutomationControl>(pc), global_strip_position, true, false);
545 }
546 
547 void
notify_change(boost::weak_ptr<ARDOUR::AutomationControl> pc,uint32_t global_strip_position,bool force,bool propagate_mode)548 DynamicsSubview::notify_change (boost::weak_ptr<ARDOUR::AutomationControl> pc, uint32_t global_strip_position, bool force, bool propagate_mode)
549 {
550 	if (!_subview_stripable)
551 	{
552 		return;
553 	}
554 
555 	Strip* strip = 0;
556 	Pot* vpot = 0;
557 	std::string* pending_display = 0;
558 	if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position))
559 	{
560 		return;
561 	}
562 
563 	boost::shared_ptr<AutomationControl> control= pc.lock ();
564 	bool reset_all = false;
565 
566 	if (propagate_mode && reset_all) {
567 		// @TODO: this line can never be reached due to reset_all being set to false. What was intended here?
568 		strip->surface()->subview_mode_changed ();
569 	}
570 
571 	if (control) {
572 		float val = control->get_value();
573 		if (control == _subview_stripable->comp_mode_controllable ()) {
574 			pending_display[1] = _subview_stripable->comp_mode_name (val);
575 		} else {
576 			do_parameter_display(pending_display[1], control->desc(), val, strip, true);
577 		}
578 		/* update pot/encoder */
579 		strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap));
580 	}
581 }
582 
583 
584 
SendsSubview(MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)585 SendsSubview::SendsSubview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
586 	: Subview(mcp, subview_stripable)
587 {}
588 
~SendsSubview()589 SendsSubview::~SendsSubview()
590 {}
591 
subview_mode_would_be_ok(boost::shared_ptr<ARDOUR::Stripable> r,std::string & reason_why_not)592 bool SendsSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> r, std::string& reason_why_not)
593 {
594 	if (r && r->send_level_controllable (0)) {
595 		return true;
596 	}
597 
598 	reason_why_not = "no sends for selected track/bus";
599 	return false;
600 }
601 
update_global_buttons()602 void SendsSubview::update_global_buttons()
603 {
604 	_mcp.update_global_button (Button::Send, on);
605 	_mcp.update_global_button (Button::Plugin, off);
606 	_mcp.update_global_button (Button::Eq, off);
607 	_mcp.update_global_button (Button::Dyn, off);
608 	_mcp.update_global_button (Button::Track, off);
609 	_mcp.update_global_button (Button::Pan, off);
610 }
611 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2])612 void SendsSubview::setup_vpot(
613 		Strip* strip,
614 		Pot* vpot,
615 		std::string pending_display[2])
616 {
617 	const uint32_t global_strip_position = _mcp.global_index (*strip);
618 	store_pointers(strip, vpot, pending_display, global_strip_position);
619 
620 	if (!_subview_stripable) {
621 		return;
622 	}
623 
624 	boost::shared_ptr<AutomationControl> pc = _subview_stripable->send_level_controllable (global_strip_position);
625 
626 	if (!pc) {
627 		/* nothing to control */
628 		vpot->set_control (boost::shared_ptr<AutomationControl>());
629 		pending_display[0] = std::string();
630 		pending_display[1] = std::string();
631 		return;
632 	}
633 
634 	pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&SendsSubview::notify_send_level_change, this, global_strip_position, false), ui_context());
635 	vpot->set_control (pc);
636 
637 	pending_display[0] = PBD::short_version (_subview_stripable->send_name (global_strip_position), 6);
638 
639 	notify_send_level_change (global_strip_position, true);
640 }
641 
642 void
notify_send_level_change(uint32_t global_strip_position,bool force)643 SendsSubview::notify_send_level_change (uint32_t global_strip_position, bool force)
644 {
645 	if (!_subview_stripable) {
646 		return;
647 	}
648 
649 	Strip* strip = 0;
650 	Pot* vpot = 0;
651 	std::string* pending_display = 0;
652 	if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position))
653 	{
654 		return;
655 	}
656 
657 	boost::shared_ptr<AutomationControl> control = _subview_stripable->send_level_controllable (global_strip_position);
658 	if (!control) {
659 		return;
660 	}
661 
662 	if (control) {
663 		float val = control->get_value();
664 		do_parameter_display(pending_display[1], control->desc(), val, strip, false);
665 
666 		if (vpot->control() == control) {
667 			/* update pot/encoder */
668 			strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap));
669 		}
670 	}
671 }
672 
handle_vselect_event(uint32_t global_strip_position)673 void SendsSubview::handle_vselect_event(uint32_t global_strip_position)
674 {
675 	/* Send mode: press enables/disables the relevant
676 		* send, but the vpot is bound to the send-level so we
677 		* need to lookup the enable/disable control
678 		* explicitly.
679 		*/
680 
681 	if (!_subview_stripable)
682 	{
683 		return;
684 	}
685 
686 	Strip* strip = 0;
687 	Pot* vpot = 0;
688 	std::string* pending_display = 0;
689 	if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position))
690 	{
691 		return;
692 	}
693 
694 	boost::shared_ptr<AutomationControl> control = _subview_stripable->send_enable_controllable(global_strip_position);
695 
696 	if (control) {
697 		bool currently_enabled = (bool) control->get_value();
698 		Controllable::GroupControlDisposition gcd;
699 
700 		if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
701 			gcd = Controllable::InverseGroup;
702 		} else {
703 			gcd = Controllable::UseGroup;
704 		}
705 
706 		control->set_value (!currently_enabled, gcd);
707 
708 		if (currently_enabled) {
709 			/* we just turned it off */
710 			pending_display[1] = "off";
711 		} else {
712 			/* we just turned it on, show the level
713 			*/
714 			control = _subview_stripable->send_level_controllable (global_strip_position);
715 			do_parameter_display(pending_display[1], control->desc(), control->get_value(), strip, false);
716 		}
717 	}
718 }
719 
720 
721 
TrackViewSubview(MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)722 TrackViewSubview::TrackViewSubview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
723 	: Subview(mcp, subview_stripable)
724 {}
725 
~TrackViewSubview()726 TrackViewSubview::~TrackViewSubview()
727 {}
728 
subview_mode_would_be_ok(boost::shared_ptr<ARDOUR::Stripable> r,std::string & reason_why_not)729 bool TrackViewSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> r, std::string& reason_why_not)
730 {
731 	if (r)  {
732 		return true;
733 	}
734 
735 	reason_why_not = "no track view possible";
736 	return false;
737 }
738 
update_global_buttons()739 void TrackViewSubview::update_global_buttons()
740 {
741 	_mcp.update_global_button (Button::Send, off);
742 	_mcp.update_global_button (Button::Plugin, off);
743 	_mcp.update_global_button (Button::Eq, off);
744 	_mcp.update_global_button (Button::Dyn, off);
745 	_mcp.update_global_button (Button::Track, on);
746 	_mcp.update_global_button (Button::Pan, off);
747 }
748 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2])749 void TrackViewSubview::setup_vpot(
750 		Strip* strip,
751 		Pot* vpot,
752 		std::string pending_display[2])
753 {
754 	const uint32_t global_strip_position = _mcp.global_index (*strip);
755 	store_pointers(strip, vpot, pending_display, global_strip_position);
756 
757 	if (global_strip_position > 4) {
758 		/* nothing to control */
759 		vpot->set_control (boost::shared_ptr<AutomationControl>());
760 		pending_display[0] = std::string();
761 		pending_display[1] = std::string();
762 		return;
763 	}
764 
765 	if (!_subview_stripable) {
766 		return;
767 	}
768 
769 	boost::shared_ptr<AutomationControl> pc;
770 	boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (_subview_stripable);
771 
772 	switch (global_strip_position) {
773 	case 0:
774 		pc = _subview_stripable->trim_control ();
775 		if (pc) {
776 			pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, TrimAutomation, global_strip_position, false), ui_context());
777 			pending_display[0] = "Trim";
778 			notify_change (TrimAutomation, global_strip_position, true);
779 		}
780 		break;
781 	case 1:
782 		if (track) {
783 			pc = track->monitoring_control();
784 			if (pc) {
785 				pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, MonitoringAutomation, global_strip_position, false), ui_context());
786 				pending_display[0] = "Mon";
787 				notify_change (MonitoringAutomation, global_strip_position, true);
788 			}
789 		}
790 		break;
791 	case 2:
792 		pc = _subview_stripable->solo_isolate_control ();
793 		if (pc) {
794 			pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, SoloIsolateAutomation, global_strip_position, false), ui_context());
795 			notify_change (SoloIsolateAutomation, global_strip_position, true);
796 			pending_display[0] = "S-Iso";
797 		}
798 		break;
799 	case 3:
800 		pc = _subview_stripable->solo_safe_control ();
801 		if (pc) {
802 			pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, SoloSafeAutomation, global_strip_position, false), ui_context());
803 			notify_change (SoloSafeAutomation, global_strip_position, true);
804 			pending_display[0] = "S-Safe";
805 		}
806 		break;
807 	case 4:
808 		pc = _subview_stripable->phase_control();
809 		if (pc) {
810 			pc->Changed.connect (_subview_connections, MISSING_INVALIDATOR, boost::bind (&TrackViewSubview::notify_change, this, PhaseAutomation, global_strip_position, false), ui_context());
811 			notify_change (PhaseAutomation, global_strip_position, true);
812 			pending_display[0] = "Phase";
813 		}
814 		break;
815 	}
816 
817 	if (!pc) {
818 		pending_display[0] = std::string();
819 		pending_display[1] = std::string();
820 		return;
821 	}
822 
823 	vpot->set_control (pc);
824 }
825 
826 void
notify_change(AutomationType type,uint32_t global_strip_position,bool force_update)827 TrackViewSubview::notify_change (AutomationType type, uint32_t global_strip_position, bool force_update)
828 {
829 	if (!_subview_stripable) {
830 		return;
831 	}
832 
833 	Strip* strip = 0;
834 	Pot* vpot = 0;
835 	std::string* pending_display = 0;
836 	if (!retrieve_pointers(&strip, &vpot, &pending_display, global_strip_position))
837 	{
838 		return;
839 	}
840 
841 	boost::shared_ptr<AutomationControl> control;
842 	boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (_subview_stripable);
843 	bool screen_hold = false;
844 
845 	switch (type) {
846 		case TrimAutomation:
847 			control = _subview_stripable->trim_control();
848 			screen_hold = true;
849 			break;
850 		case SoloIsolateAutomation:
851 			control = _subview_stripable->solo_isolate_control ();
852 			break;
853 		case SoloSafeAutomation:
854 			control = _subview_stripable->solo_safe_control ();
855 			break;
856 		case MonitoringAutomation:
857 			if (track) {
858 				control = track->monitoring_control();
859 				screen_hold = true;
860 			}
861 			break;
862 		case PhaseAutomation:
863 			control = _subview_stripable->phase_control ();
864 			screen_hold = true;
865 			break;
866 		default:
867 			break;
868 	}
869 
870 	if (control) {
871 		float val = control->get_value();
872 		do_parameter_display(pending_display[1], control->desc(), val, strip, screen_hold);
873 
874 		/* update pot/encoder */
875 		strip->surface()->write (vpot->set (control->internal_to_interface (val), true, Pot::wrap));
876 	}
877 }
878 
879 
880 
PluginSubview(MackieControlProtocol & mcp,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)881 PluginSubview::PluginSubview(MackieControlProtocol& mcp, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
882 	: Subview(mcp, subview_stripable)
883 {
884 	_plugin_subview_state = boost::shared_ptr<PluginSelect>(new PluginSelect (*this));
885 	connect_processors_changed_signal();
886 }
887 
~PluginSubview()888 PluginSubview::~PluginSubview()
889 {}
890 
connect_processors_changed_signal()891 void PluginSubview::connect_processors_changed_signal()
892 {
893 	boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (_subview_stripable);
894 	if (route)
895 	{
896 		route->processors_changed.connect(_subview_connections, MISSING_INVALIDATOR, boost::bind (&PluginSubview::handle_processors_changed, this), ui_context());
897 	}
898 }
899 
handle_processors_changed()900 void PluginSubview::handle_processors_changed()
901 {
902 	_mcp.redisplay_subview_mode();
903 }
904 
subview_mode_would_be_ok(boost::shared_ptr<ARDOUR::Stripable> r,std::string & reason_why_not)905 bool PluginSubview::subview_mode_would_be_ok (boost::shared_ptr<ARDOUR::Stripable> r, std::string& reason_why_not)
906 {
907 	if (r) {
908 		boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (r);
909 		if (route && route->nth_plugin(0)) {
910 			return true;
911 		}
912 	}
913 
914 	reason_why_not = "no plugins in selected track/bus";
915 	return false;
916 }
917 
update_global_buttons()918 void PluginSubview::update_global_buttons()
919 {
920 	_mcp.update_global_button (Button::Send, off);
921 	_mcp.update_global_button (Button::Plugin, on);
922 	_mcp.update_global_button (Button::Eq, off);
923 	_mcp.update_global_button (Button::Dyn, off);
924 	_mcp.update_global_button (Button::Track, off);
925 	_mcp.update_global_button (Button::Pan, off);
926 }
927 
permit_flipping_faders_and_pots()928 bool PluginSubview::permit_flipping_faders_and_pots()
929 {
930 	return _plugin_subview_state->permit_flipping_faders_and_pots();
931 }
932 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2])933 void PluginSubview::setup_vpot(
934 		Strip* strip,
935 		Pot* vpot,
936 		std::string pending_display[2])
937 {
938 	const uint32_t global_strip_position = _mcp.global_index (*strip);
939 	store_pointers(strip, vpot, pending_display, global_strip_position);
940 	_plugin_subview_state->setup_vpot(strip, vpot, pending_display, global_strip_position, _subview_stripable);
941 }
942 
handle_vselect_event(uint32_t global_strip_position)943 void PluginSubview::handle_vselect_event(uint32_t global_strip_position)
944 {
945 	_plugin_subview_state->handle_vselect_event(global_strip_position, _subview_stripable);
946 }
947 
handle_cursor_right_press()948 bool PluginSubview::handle_cursor_right_press()
949 {
950 	return _plugin_subview_state->handle_cursor_right_press();
951 }
952 
handle_cursor_left_press()953 bool PluginSubview::handle_cursor_left_press()
954 {
955 	return _plugin_subview_state->handle_cursor_left_press();
956 }
957 
set_state(boost::shared_ptr<PluginSubviewState> new_state)958 void PluginSubview::set_state(boost::shared_ptr<PluginSubviewState> new_state)
959 {
960 	_plugin_subview_state = new_state;
961 
962 	const uint32_t num_strips = _strips_over_all_surfaces.size();
963 	for (uint32_t strip_index = 0; strip_index < num_strips; strip_index++)
964 	{
965 		Strip* strip = 0;
966 		Pot* vpot = 0;
967 		std::string* pending_display = 0;
968 		if (!retrieve_pointers(&strip, &vpot, &pending_display, strip_index))
969 		{
970 			return;
971 		}
972 		_plugin_subview_state->setup_vpot(strip, vpot, pending_display, strip_index, _subview_stripable);
973 	}
974 }
975 
976 
977 
978 
PluginSubviewState(PluginSubview & context)979 PluginSubviewState::PluginSubviewState(PluginSubview& context)
980   : _context(context)
981   , _bank_size(context.mcp().n_strips())
982   , _current_bank(0)
983 {
984 }
985 
~PluginSubviewState()986 PluginSubviewState::~PluginSubviewState()
987 {}
988 
989 std::string
shorten_display_text(const std::string & text,std::string::size_type target_length)990 PluginSubviewState::shorten_display_text(const std::string& text, std::string::size_type target_length)
991 {
992 	if (text.length() <= target_length) {
993 		return text;
994 	}
995 
996 	return PBD::short_version (text, target_length);
997 }
998 
handle_cursor_right_press()999 bool PluginSubviewState::handle_cursor_right_press()
1000 {
1001 	_current_bank = _current_bank + 1;
1002 	bank_changed();
1003 	return true;
1004 }
1005 
handle_cursor_left_press()1006 bool PluginSubviewState::handle_cursor_left_press()
1007 {
1008 	if (_current_bank >= 1)
1009 	{
1010 		_current_bank = _current_bank - 1;
1011 	}
1012 	bank_changed();
1013 	return true;
1014 }
1015 
calculate_virtual_strip_position(uint32_t strip_index) const1016 uint32_t PluginSubviewState::calculate_virtual_strip_position(uint32_t strip_index) const
1017 {
1018 	return _current_bank * _bank_size + strip_index;
1019 }
1020 
1021 
1022 
PluginSelect(PluginSubview & context)1023 PluginSelect::PluginSelect(PluginSubview& context)
1024   : PluginSubviewState(context)
1025 {}
1026 
~PluginSelect()1027 PluginSelect::~PluginSelect()
1028 {}
1029 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2],uint32_t global_strip_position,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)1030 void PluginSelect::setup_vpot(
1031 	    Strip* strip,
1032 		Pot* vpot,
1033 		std::string pending_display[2],
1034 		uint32_t global_strip_position,
1035 		boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
1036 {
1037 	if (!subview_stripable) {
1038 		return;
1039 	}
1040 
1041 	boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (subview_stripable);
1042 	if (!route) {
1043 		return;
1044 	}
1045 
1046 	uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position);
1047 
1048 	boost::shared_ptr<Processor> plugin = route->nth_plugin(virtual_strip_position);
1049 
1050 	if (plugin) {
1051 		DEBUG_TRACE (DEBUG::MackieControl, string_compose ("plugin of strip %1 is %2\n", global_strip_position, plugin->display_name()));
1052 		pending_display[0] = string_compose("Ins%1Pl", virtual_strip_position + 1);
1053 		pending_display[1] = PluginSubviewState::shorten_display_text(plugin->display_name(), 6);
1054 	}
1055 	else {
1056 		pending_display[0] = "";
1057 		pending_display[1] = "";
1058 	}
1059 }
1060 
handle_vselect_event(uint32_t global_strip_position,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)1061 void PluginSelect::handle_vselect_event(uint32_t global_strip_position,
1062 		boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
1063 {
1064 	/* PluginSelect mode: press selects the plugin shown on the strip's LCD */
1065 	if (!subview_stripable) {
1066 		return;
1067 	}
1068 
1069 	boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (subview_stripable);
1070 	if (!route) {
1071 		return;
1072 	}
1073 
1074 	uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position);
1075 
1076 	boost::shared_ptr<Processor> processor = route->nth_plugin(virtual_strip_position);
1077 	boost::shared_ptr<PluginInsert> plugin = boost::dynamic_pointer_cast<PluginInsert>(processor);
1078 	processor->ShowUI();
1079 	if (plugin) {
1080 		_context.set_state (boost::shared_ptr<PluginEdit> (new PluginEdit (_context, boost::weak_ptr<PluginInsert>(plugin))));
1081 	}
1082 }
1083 
bank_changed()1084 void PluginSelect::bank_changed()
1085 {
1086 	_context.mcp().redisplay_subview_mode();
1087 }
1088 
1089 
1090 
PluginEdit(PluginSubview & context,boost::weak_ptr<PluginInsert> weak_subview_plugin_insert)1091 PluginEdit::PluginEdit(PluginSubview& context, boost::weak_ptr<PluginInsert> weak_subview_plugin_insert)
1092   : PluginSubviewState(context)
1093   , _weak_subview_plugin_insert(weak_subview_plugin_insert)
1094 {
1095 	try {
1096 		init();
1097 	}
1098 	catch (...) {
1099 		throw failed_constructor();
1100 	}
1101 }
1102 
~PluginEdit()1103 PluginEdit::~PluginEdit()
1104 {}
1105 
init()1106 void PluginEdit::init()
1107 {
1108 	boost::shared_ptr<PluginInsert> plugin_insert = _weak_subview_plugin_insert.lock();
1109 	_weak_subview_plugin = boost::weak_ptr<ARDOUR::Plugin>(plugin_insert->plugin());
1110 	boost::shared_ptr<ARDOUR::Plugin> subview_plugin = _weak_subview_plugin.lock();
1111 	_plugin_input_parameter_indices.clear();
1112 
1113 	if (!subview_plugin) {
1114 		return;
1115 	}
1116 
1117 	bool ok = false;
1118 	// put only input controls into a vector
1119 	uint32_t nplug_params = subview_plugin->parameter_count();
1120 	for (uint32_t ppi = 0; ppi < nplug_params; ++ppi) {
1121 		uint32_t controlid = subview_plugin->nth_parameter(ppi, ok);
1122 		if (!ok) {
1123 			continue;
1124 		}
1125 		if (subview_plugin->parameter_is_input(controlid)) {
1126 			_plugin_input_parameter_indices.push_back(ppi);
1127 		}
1128 	}
1129 }
1130 
parameter_control(uint32_t global_strip_position) const1131 boost::shared_ptr<AutomationControl> PluginEdit::parameter_control(uint32_t global_strip_position) const
1132 {
1133 	uint32_t virtual_strip_position = calculate_virtual_strip_position(global_strip_position);
1134 	if (virtual_strip_position >= _plugin_input_parameter_indices.size()) {
1135 		return boost::shared_ptr<AutomationControl>();
1136 	}
1137 
1138 	boost::shared_ptr<PluginInsert> plugin_insert = _weak_subview_plugin_insert.lock();
1139 	boost::shared_ptr<ARDOUR::Plugin> subview_plugin = _weak_subview_plugin.lock();
1140 	if (!plugin_insert || !subview_plugin) {
1141 		return boost::shared_ptr<AutomationControl>();
1142 	}
1143 
1144 	uint32_t plugin_parameter_index = _plugin_input_parameter_indices[virtual_strip_position];
1145 	bool ok = false;
1146 	uint32_t controlid = subview_plugin->nth_parameter(plugin_parameter_index, ok);
1147 	if (!ok) {
1148 		return boost::shared_ptr<AutomationControl>();
1149 	}
1150 	return plugin_insert->automation_control(Evoral::Parameter(PluginAutomation, 0, controlid));
1151 }
1152 
plugin_went_away() const1153 bool PluginEdit::plugin_went_away() const
1154 {
1155 	// is shared_ptr reset?
1156 	boost::shared_ptr<PluginInsert> plugin_insert = _weak_subview_plugin_insert.lock();
1157 	boost::shared_ptr<ARDOUR::Plugin> subview_plugin = _weak_subview_plugin.lock();
1158 	if (!plugin_insert || !subview_plugin) {
1159 		return true;
1160 	}
1161 
1162 	// is plugin not registered with stripable any more?
1163 	boost::shared_ptr<Route> route = boost::dynamic_pointer_cast<Route> (_context.subview_stripable());
1164 	if (!route) {
1165 		return true;
1166 	}
1167 
1168 	if (!route->processor_by_id(plugin_insert->id())) {
1169 		// plugin_insert is not registered with route any more -> it was removed
1170 		return true;
1171 	}
1172 
1173 	return false;
1174 }
1175 
switch_to_plugin_select_state()1176 void PluginEdit::switch_to_plugin_select_state()
1177 {
1178 	_context.set_state (boost::shared_ptr <PluginSelect>(new PluginSelect (_context)));
1179 }
1180 
setup_vpot(Strip * strip,Pot * vpot,std::string pending_display[2],uint32_t global_strip_position,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)1181 void PluginEdit::setup_vpot(
1182 		Strip* strip,
1183 		Pot* vpot,
1184 		std::string pending_display[2],
1185 		uint32_t global_strip_position,
1186 		boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
1187 {
1188 	if (plugin_went_away()) {
1189 		switch_to_plugin_select_state();
1190 		return;
1191 	}
1192 
1193 	boost::shared_ptr<AutomationControl> c = parameter_control(global_strip_position);
1194 
1195 	if (!c) {
1196 		vpot->set_control (boost::shared_ptr<AutomationControl>());
1197 		pending_display[0] = std::string();
1198 		pending_display[1] = std::string();
1199 		return;
1200 	}
1201 
1202 	c->Changed.connect (_context.subview_connections(), MISSING_INVALIDATOR, boost::bind (&PluginEdit::notify_parameter_change, this, strip, vpot, pending_display, global_strip_position), ui_context());
1203 	vpot->set_control (c);
1204 	pending_display[0] = PluginSubviewState::shorten_display_text(c->desc().label, 6);
1205 	notify_parameter_change (strip, vpot, pending_display, global_strip_position);
1206 }
1207 
1208 
notify_parameter_change(Strip * strip,Pot * vpot,std::string pending_display[2],uint32_t global_strip_position)1209 void PluginEdit::notify_parameter_change(Strip* strip, Pot* vpot, std::string pending_display[2], uint32_t global_strip_position)
1210 {
1211 	boost::shared_ptr<AutomationControl> control = parameter_control(global_strip_position);
1212 	if (!control)
1213 	{
1214 		return;
1215 	}
1216 
1217 	float val = control->get_value();
1218 	_context.do_parameter_display(pending_display[1], control->desc(), val, strip, false);
1219 
1220 	if (vpot->control() == control) {
1221 		/* update pot/encoder */
1222 		strip->surface()->write(vpot->set (control->internal_to_interface (val), true, Pot::wrap));
1223 	}
1224 }
1225 
handle_vselect_event(uint32_t global_strip_position,boost::shared_ptr<ARDOUR::Stripable> subview_stripable)1226 void PluginEdit::handle_vselect_event(uint32_t global_strip_position, boost::shared_ptr<ARDOUR::Stripable> subview_stripable)
1227 {
1228 }
1229 
bank_changed()1230 void PluginEdit::bank_changed()
1231 {
1232 	_context.mcp().redisplay_subview_mode();
1233 }
1234