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