1 /*
2  * Copyright (C) 2006-2007 John Anderson
3  * Copyright (C) 2012-2017 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2012 Carl Hetherington <carl@carlh.net>
5  * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
6  * Copyright (C) 2015-2016 Len Ovens <len@ovenwerks.net>
7  * Copyright (C) 2015-2017 Ben Loftis <ben@harrisonconsoles.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #include <sstream>
25 #include <vector>
26 #include <climits>
27 
28 #include <stdint.h>
29 
30 #include <sys/time.h>
31 
32 #include <glibmm/convert.h>
33 
34 #include "midi++/port.h"
35 
36 #include "pbd/compose.h"
37 #include "pbd/convert.h"
38 
39 #include "ardour/amp.h"
40 #include "ardour/bundle.h"
41 #include "ardour/debug.h"
42 #include "ardour/midi_ui.h"
43 #include "ardour/meter.h"
44 #include "ardour/monitor_control.h"
45 #include "ardour/plugin_insert.h"
46 #include "ardour/panner.h"
47 #include "ardour/panner_shell.h"
48 #include "ardour/phase_control.h"
49 #include "ardour/rc_configuration.h"
50 #include "ardour/record_enable_control.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/send.h"
54 #include "ardour/solo_isolate_control.h"
55 #include "ardour/track.h"
56 #include "ardour/midi_track.h"
57 #include "ardour/user_bundle.h"
58 #include "ardour/profile.h"
59 #include "ardour/value_as_string.h"
60 
61 #include "mackie_control_protocol.h"
62 #include "subview.h"
63 #include "surface_port.h"
64 #include "surface.h"
65 #include "strip.h"
66 #include "button.h"
67 #include "led.h"
68 #include "pot.h"
69 #include "fader.h"
70 #include "jog.h"
71 #include "meter.h"
72 
73 using namespace std;
74 using namespace ARDOUR;
75 using namespace PBD;
76 using namespace ArdourSurface;
77 using namespace Mackie;
78 
79 #ifndef timeradd /// only avail with __USE_BSD
80 #define timeradd(a,b,result)                         \
81   do {                                               \
82     (result)->tv_sec = (a)->tv_sec + (b)->tv_sec;    \
83     (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
84     if ((result)->tv_usec >= 1000000)                \
85     {                                                \
86       ++(result)->tv_sec;                            \
87       (result)->tv_usec -= 1000000;                  \
88     }                                                \
89   } while (0)
90 #endif
91 
92 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
93 
Strip(Surface & s,const std::string & name,int index,const map<Button::ID,StripButtonInfo> & strip_buttons)94 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
95 	: Group (name)
96 	, _solo (0)
97 	, _recenable (0)
98 	, _mute (0)
99 	, _select (0)
100 	, _vselect (0)
101 	, _fader_touch (0)
102 	, _vpot (0)
103 	, _fader (0)
104 	, _meter (0)
105 	, _index (index)
106 	, _surface (&s)
107 	, _controls_locked (false)
108 	, _transport_is_rolling (false)
109 	, _metering_active (true)
110 	, _block_screen_redisplay_until (0)
111 	, return_to_vpot_mode_display_at (UINT64_MAX)
112 	, _pan_mode (PanAzimuthAutomation)
113 	, _last_gain_position_written (-1.0)
114 	, _last_pan_azi_position_written (-1.0)
115 	, _last_pan_width_position_written (-1.0)
116 	, _last_trim_position_written (-1.0)
117 {
118 	_fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
119 	_vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
120 
121 	if (s.mcp().device_info().has_meters()) {
122 		_meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
123 	}
124 
125 	for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
126 		Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
127 		DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
128 								   _surface->number(), index, Button::id_to_name (bb->bid()),
129 								   bb->id(), b->second.base_id));
130 	}
131 }
132 
~Strip()133 Strip::~Strip ()
134 {
135 	/* surface is responsible for deleting all controls */
136 }
137 
138 void
add(Control & control)139 Strip::add (Control & control)
140 {
141 	Button* button;
142 
143 	Group::add (control);
144 
145 	/* fader, vpot, meter were all set explicitly */
146 
147 	if ((button = dynamic_cast<Button*>(&control)) != 0) {
148 		switch (button->bid()) {
149 		case Button::RecEnable:
150 			_recenable = button;
151 			break;
152 		case Button::Mute:
153 			_mute = button;
154 			break;
155 		case Button::Solo:
156 			_solo = button;
157 			break;
158 		case Button::Select:
159 			_select = button;
160 			break;
161 		case Button::VSelect:
162 			_vselect = button;
163 			break;
164 		case Button::FaderTouch:
165 			_fader_touch = button;
166 			break;
167 		default:
168 			break;
169 		}
170 	}
171 }
172 
173 void
set_stripable(boost::shared_ptr<Stripable> r,bool)174 Strip::set_stripable (boost::shared_ptr<Stripable> r, bool /*with_messages*/)
175 {
176 	if (_controls_locked) {
177 		return;
178 	}
179 
180 	mb_pan_controllable.reset();
181 
182 	stripable_connections.drop_connections ();
183 
184 	_solo->set_control (boost::shared_ptr<AutomationControl>());
185 	_mute->set_control (boost::shared_ptr<AutomationControl>());
186 	_select->set_control (boost::shared_ptr<AutomationControl>());
187 	_recenable->set_control (boost::shared_ptr<AutomationControl>());
188 	_fader->set_control (boost::shared_ptr<AutomationControl>());
189 	_vpot->set_control (boost::shared_ptr<AutomationControl>());
190 
191 	_stripable = r;
192 
193 	reset_saved_values ();
194 
195 	if (!r) {
196 		DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 Strip %2 mapped to null route\n", _surface->number(), _index));
197 		zero ();
198 		return;
199 	}
200 
201 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping stripable %3\n",
202 							   _surface->number(), _index, _stripable->name()));
203 
204 	_solo->set_control (_stripable->solo_control());
205 	_mute->set_control (_stripable->mute_control());
206 
207 	_stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
208 	_stripable->mute_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
209 
210 	boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control();
211 	if (pan_control) {
212 		pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
213 	}
214 
215 	pan_control = _stripable->pan_width_control();
216 	if (pan_control) {
217 		pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
218 	}
219 
220 	_stripable->gain_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
221 	_stripable->PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
222 	_stripable->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
223 
224 	boost::shared_ptr<AutomationControl> rec_enable_control = _stripable->rec_enable_control ();
225 
226 	if (rec_enable_control) {
227 		_recenable->set_control (rec_enable_control);
228 		rec_enable_control->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
229 	}
230 
231 	// TODO this works when a currently-banked stripable is made inactive, but not
232 	// when a stripable is activated which should be currently banked.
233 
234 	_stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_stripable_deleted, this), ui_context());
235 
236 	/* setup legal VPot modes for this stripable */
237 
238 	possible_pot_parameters.clear();
239 
240 	if (_stripable->pan_azimuth_control()) {
241 		possible_pot_parameters.push_back (PanAzimuthAutomation);
242 	}
243 	if (_stripable->pan_width_control()) {
244 		possible_pot_parameters.push_back (PanWidthAutomation);
245 	}
246 	if (_stripable->pan_elevation_control()) {
247 		possible_pot_parameters.push_back (PanElevationAutomation);
248 	}
249 	if (_stripable->pan_frontback_control()) {
250 		possible_pot_parameters.push_back (PanFrontBackAutomation);
251 	}
252 	if (_stripable->pan_lfe_control()) {
253 		possible_pot_parameters.push_back (PanLFEAutomation);
254 	}
255 
256 	_pan_mode = PanAzimuthAutomation;
257 
258 	if (_surface->mcp().subview()->subview_mode() == Subview::None) {
259 		set_vpot_parameter (_pan_mode);
260 	}
261 
262 	_fader->set_control (_stripable->gain_control());
263 
264 	notify_all ();
265 }
266 
267 void
notify_all()268 Strip::notify_all()
269 {
270 	if (!_stripable) {
271 		zero ();
272 		return;
273 	}
274 	// The active V-pot control may not be active for this strip
275 	// But if we zero it in the controls function it may erase
276 	// the one we do want
277 	_surface->write (_vpot->zero());
278 
279 	notify_solo_changed ();
280 	notify_mute_changed ();
281 	notify_gain_changed ();
282 	notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
283 	notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::selected));
284 	notify_panner_azi_changed ();
285 	notify_panner_width_changed ();
286 	notify_record_enable_changed ();
287 	notify_processor_changed ();
288 }
289 
290 void
notify_solo_changed()291 Strip::notify_solo_changed ()
292 {
293 	if (_stripable && _solo) {
294 		_surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
295 	}
296 }
297 
298 void
notify_mute_changed()299 Strip::notify_mute_changed ()
300 {
301 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
302 	if (_stripable && _mute) {
303 		DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\tstripable muted ? %1\n", _stripable->mute_control()->muted()));
304 		DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_stripable->mute_control()->muted() ? on : off)));
305 
306 		_surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
307 	}
308 }
309 
310 void
notify_record_enable_changed()311 Strip::notify_record_enable_changed ()
312 {
313 	if (_stripable && _recenable)  {
314 		boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (_stripable);
315 		if (trk) {
316 			_surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off));
317 		}
318 	}
319 }
320 
321 void
notify_stripable_deleted()322 Strip::notify_stripable_deleted ()
323 {
324 	_surface->mcp().notify_stripable_removed ();
325 	_surface->mcp().refresh_current_bank();
326 }
327 
328 void
notify_gain_changed(bool force_update)329 Strip::notify_gain_changed (bool force_update)
330 {
331 	if (!_stripable) {
332 		return;
333 	}
334 
335 	boost::shared_ptr<AutomationControl> ac = _stripable->gain_control();
336 	Control* control;
337 
338 	if (!ac) {
339 		/* doesn't seem possible but lets be safe */
340 		return;
341 	}
342 
343 	/* track gain control could be on vpot or fader, depending in
344 	 * flip mode.
345 	 */
346 
347 	if (_vpot->control() == ac) {
348 		control = _vpot;
349 	} else if (_fader->control() == ac) {
350 		control = _fader;
351 	} else {
352 		return;
353 	}
354 
355 	float gain_coefficient = ac->get_value();
356 	float normalized_position = ac->internal_to_interface (gain_coefficient);
357 
358 	if (force_update || normalized_position != _last_gain_position_written) {
359 
360 		if (!control->in_use()) {
361 			if (control == _vpot) {
362 				_surface->write (_vpot->set (normalized_position, true, Pot::wrap));
363 			} else {
364 				_surface->write (_fader->set_position (normalized_position));
365 			}
366 		}
367 
368 		do_parameter_display (ac->desc(), gain_coefficient); // GainAutomation
369 		_last_gain_position_written = normalized_position;
370 	}
371 }
372 
373 void
notify_processor_changed(bool force_update)374 Strip::notify_processor_changed (bool force_update)
375 {
376 }
377 
378 void
notify_property_changed(const PropertyChange & what_changed)379 Strip::notify_property_changed (const PropertyChange& what_changed)
380 {
381 	if (what_changed.contains (ARDOUR::Properties::name)) {
382 		show_stripable_name ();
383 	}
384 
385 	if (what_changed.contains (ARDOUR::Properties::selected)) {
386 		_surface->write (_select->set_state (_stripable->is_selected()));
387 	}
388 }
389 
390 void
update_selection_state()391 Strip::update_selection_state ()
392 {
393 	if(_stripable) {
394 		_surface->write (_select->set_state (_stripable->is_selected()));
395 	}
396 }
397 
398 void
show_stripable_name()399 Strip::show_stripable_name ()
400 {
401 	Subview::Mode svm = _surface->mcp().subview()->subview_mode();
402 
403 	if (svm != Subview::None) {
404 		/* subview mode is responsible for upper line */
405 		return;
406 	}
407 
408 	string fullname = string();
409 	if (!_stripable) {
410 		fullname = string();
411 	} else {
412 		fullname = _stripable->name();
413 	}
414 
415 	if (fullname.length() <= 6) {
416 		pending_display[0] = fullname;
417 	} else {
418 		pending_display[0] = PBD::short_version (fullname, 6);
419 	}
420 }
421 
422 void
notify_panner_azi_changed(bool force_update)423 Strip::notify_panner_azi_changed (bool force_update)
424 {
425 	if (!_stripable) {
426 		return;
427 	}
428 
429 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
430 
431 	boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
432 
433 	if (!pan_control) {
434 		/* basically impossible, since we're here because that control
435 		 *  changed, but sure, whatever.
436 		 */
437 		return;
438 	}
439 
440 	if (_vpot->control() != pan_control) {
441 		return;
442 	}
443 
444 	double normalized_pos = pan_control->internal_to_interface (pan_control->get_value(), true);
445 	double internal_pos = pan_control->get_value();
446 
447 	if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
448 
449 		_surface->write (_vpot->set (normalized_pos, true, Pot::dot));
450 		/* show actual internal value to user */
451 		do_parameter_display (pan_control->desc(), internal_pos); // PanAzimuthAutomation
452 
453 		_last_pan_azi_position_written = normalized_pos;
454 	}
455 }
456 
457 void
notify_panner_width_changed(bool force_update)458 Strip::notify_panner_width_changed (bool force_update)
459 {
460 	if (!_stripable) {
461 		return;
462 	}
463 
464 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
465 
466 	boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_width_control ();
467 
468 	if (!pan_control) {
469 		/* basically impossible, since we're here because that control
470 		 *  changed, but sure, whatever.
471 		 */
472 		return;
473 	}
474 
475 	if (_vpot->control() != pan_control) {
476 		return;
477 	}
478 
479 	double pos = pan_control->internal_to_interface (pan_control->get_value());
480 
481 	if (force_update || pos != _last_pan_width_position_written) {
482 
483 		_surface->write (_vpot->set (pos, true, Pot::spread));
484 		do_parameter_display (pan_control->desc(), pos); // PanWidthAutomation
485 
486 		_last_pan_width_position_written = pos;
487 	}
488 }
489 
490 void
select_event(Button &,ButtonState bs)491 Strip::select_event (Button&, ButtonState bs)
492 {
493 	DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
494 
495 	if (bs == press) {
496 
497 		int ms = _surface->mcp().main_modifier_state();
498 
499 		if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
500 			_controls_locked = !_controls_locked;
501 			_surface->write (display (1,_controls_locked ?  "Locked" : "Unlock"));
502 			block_vpot_mode_display_for (1000);
503 			return;
504 		}
505 
506 		DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
507 		_surface->mcp().add_down_select_button (_surface->number(), _index);
508 		_surface->mcp().select_range (_surface->mcp().global_index (*this));
509 
510 	} else {
511 		DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
512 		_surface->mcp().remove_down_select_button (_surface->number(), _index);
513 	}
514 }
515 
516 void
vselect_event(Button &,ButtonState bs)517 Strip::vselect_event (Button&, ButtonState bs)
518 {
519 	if (_surface->mcp().subview()->subview_mode() != Subview::None) {
520 		/* most subview modes: vpot press acts like a button for toggle parameters */
521 		if (bs != press) {
522 			return;
523 		}
524 
525 		_surface->mcp().subview()->handle_vselect_event(_surface->mcp().global_index (*this));
526 		return;
527 	}
528 
529 	if (bs == press) {
530 
531 		int ms = _surface->mcp().main_modifier_state();
532 
533 		if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
534 
535 			boost::shared_ptr<AutomationControl> ac = _vpot->control ();
536 
537 			if (ac) {
538 
539 				/* reset to default/normal value */
540 				ac->set_value (ac->normal(), Controllable::NoGroup);
541 			}
542 
543 		}  else {
544 
545 #ifdef MIXBUS
546 			if (_stripable) {
547 				boost::shared_ptr<AutomationControl> ac = _stripable->master_send_enable_controllable ();
548 				if (ac) {
549 					Controllable::GroupControlDisposition gcd;
550 
551 					if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
552 						gcd = Controllable::InverseGroup;
553 					} else {
554 						gcd = Controllable::UseGroup;
555 					}
556 
557 					bool enabled = ac->get_value();
558 					ac->set_value (!enabled, gcd);
559 				}
560 			}
561 #else
562 			DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
563 			/* switch vpot to control next available parameter */
564 			next_pot_mode ();
565 #endif
566 		}
567 
568 	}
569 }
570 
571 void
fader_touch_event(Button &,ButtonState bs)572 Strip::fader_touch_event (Button&, ButtonState bs)
573 {
574 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
575 
576 	if (bs == press) {
577 
578 		boost::shared_ptr<AutomationControl> ac = _fader->control ();
579 
580 		_fader->set_in_use (true);
581 		_fader->start_touch (_surface->mcp().transport_sample());
582 
583 		if (ac) {
584 			do_parameter_display (ac->desc(), ac->get_value());
585 		}
586 
587 	} else {
588 
589 		_fader->set_in_use (false);
590 		_fader->stop_touch (_surface->mcp().transport_sample());
591 
592 	}
593 }
594 
595 
596 void
handle_button(Button & button,ButtonState bs)597 Strip::handle_button (Button& button, ButtonState bs)
598 {
599 	boost::shared_ptr<AutomationControl> control;
600 
601 	if (bs == press) {
602 		button.set_in_use (true);
603 	} else {
604 		button.set_in_use (false);
605 	}
606 
607 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
608 
609 	switch (button.bid()) {
610 	case Button::Select:
611 		select_event (button, bs);
612 		break;
613 
614 	case Button::VSelect:
615 		vselect_event (button, bs);
616 		break;
617 
618 	case Button::FaderTouch:
619 		fader_touch_event (button, bs);
620 		break;
621 
622 	default:
623 		if ((control = button.control ())) {
624 			if (bs == press) {
625 				DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
626 				_surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
627 
628 				float new_value = control->get_value() ? 0.0 : 1.0;
629 
630 				/* get all controls that either have their
631 				 * button down or are within a range of
632 				 * several down buttons
633 				 */
634 
635 				MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
636 				                                                                             _surface->mcp().global_index(*this));
637 
638 
639 				DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
640 									    controls.size(), control->parameter().type(), new_value));
641 
642 				/* apply change, with potential modifier semantics */
643 
644 				Controllable::GroupControlDisposition gcd;
645 
646 				if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
647 					gcd = Controllable::InverseGroup;
648 				} else {
649 					gcd = Controllable::UseGroup;
650 				}
651 
652 				for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
653 					(*c)->set_value (new_value, gcd);
654 				}
655 
656 			} else {
657 				DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
658 				_surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
659 			}
660 		}
661 		break;
662 	}
663 }
664 
665 std::string
format_paramater_for_display(ARDOUR::ParameterDescriptor const & desc,float val,boost::shared_ptr<ARDOUR::Stripable> stripable_for_non_mixbus_azimuth_automation,bool & overwrite_screen_hold)666 Strip::format_paramater_for_display(
667 		ARDOUR::ParameterDescriptor const& desc,
668 		float val,
669 		boost::shared_ptr<ARDOUR::Stripable> stripable_for_non_mixbus_azimuth_automation,
670 		bool& overwrite_screen_hold)
671 {
672 	std::string formatted_parameter_display;
673 	char buf[16];
674 
675 	switch (desc.type) {
676 	case GainAutomation:
677 	case BusSendLevel:
678 	case TrimAutomation:
679 		// we can't use value_as_string() that'll suffix "dB" and also use "-inf" w/o space :(
680 		if (val == 0.0) {
681 			formatted_parameter_display = " -inf ";
682 		} else {
683 			float dB = accurate_coefficient_to_dB (val);
684 			snprintf (buf, sizeof (buf), "%6.1f", dB);
685 			formatted_parameter_display = buf;
686 			overwrite_screen_hold = true;
687 		}
688 		break;
689 
690 	case PanAzimuthAutomation:
691 		if (Profile->get_mixbus()) {
692 			// XXX no _stripable check?
693 			snprintf (buf, sizeof (buf), "%2.1f", val);
694 			formatted_parameter_display = buf;
695 			overwrite_screen_hold = true;
696 		} else {
697 			if (stripable_for_non_mixbus_azimuth_automation) {
698 				boost::shared_ptr<AutomationControl> pa = stripable_for_non_mixbus_azimuth_automation->pan_azimuth_control();
699 				if (pa) {
700 					formatted_parameter_display = pa->get_user_string ();
701 					overwrite_screen_hold = true;
702 				}
703 			}
704 		}
705 		break;
706 	default:
707 		formatted_parameter_display = ARDOUR::value_as_string (desc, val);
708 		if (formatted_parameter_display.size () < 6) { // left-padding, right-align
709 			formatted_parameter_display.insert (0, 6 - formatted_parameter_display.size (), ' ');
710 		}
711 		break;
712 	}
713 
714 	return formatted_parameter_display;
715 }
716 
717 void
do_parameter_display(ARDOUR::ParameterDescriptor const & desc,float val,bool screen_hold)718 Strip::do_parameter_display (ARDOUR::ParameterDescriptor const& desc, float val, bool screen_hold)
719 {
720 	pending_display[1] = format_paramater_for_display(desc, val, _stripable, screen_hold);
721 
722 	if (screen_hold) {
723 		/* we just queued up a parameter to be displayed.
724 		   1 second from now, switch back to vpot mode display.
725 		*/
726 		block_vpot_mode_display_for (1000);
727 	}
728 }
729 
730 void
handle_fader_touch(Fader & fader,bool touch_on)731 Strip::handle_fader_touch (Fader& fader, bool touch_on)
732 {
733 	if (touch_on) {
734 		fader.start_touch (_surface->mcp().transport_sample());
735 	} else {
736 		fader.stop_touch (_surface->mcp().transport_sample());
737 	}
738 }
739 
740 void
handle_fader(Fader & fader,float position)741 Strip::handle_fader (Fader& fader, float position)
742 {
743 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
744 	boost::shared_ptr<AutomationControl> ac = fader.control();
745 	if (!ac) {
746 		return;
747 	}
748 
749 	Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
750 
751 	if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
752 		gcd = Controllable::InverseGroup;
753 	}
754 
755 	fader.set_value (position, gcd);
756 
757 	/* From the Mackie Control MIDI implementation docs:
758 
759 	   In order to ensure absolute synchronization with the host software,
760 	   Mackie Control uses a closed-loop servo system for the faders,
761 	   meaning the faders will always move to their last received position.
762 	   When a host receives a Fader Position Message, it must then
763 	   re-transmit that message to the Mackie Control or else the faders
764 	   will return to their last position.
765 	*/
766 
767 	_surface->write (fader.set_position (position));
768 }
769 
770 void
handle_pot(Pot & pot,float delta)771 Strip::handle_pot (Pot& pot, float delta)
772 {
773 	/* Pots only emit events when they move, not when they
774 	   stop moving. So to get a stop event, we need to use a timeout.
775 	*/
776 
777 	boost::shared_ptr<AutomationControl> ac = pot.control();
778 	if (!ac) {
779 		return;
780 	}
781 
782 	Controllable::GroupControlDisposition gcd;
783 
784 	if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
785 		gcd = Controllable::InverseGroup;
786 	} else {
787 		gcd = Controllable::UseGroup;
788 	}
789 
790 	if (ac->toggled()) {
791 
792 		/* make it like a single-step, directional switch */
793 
794 		if (delta > 0) {
795 			ac->set_value (1.0, gcd);
796 		} else {
797 			ac->set_value (0.0, gcd);
798 		}
799 
800 	} else if (ac->desc().enumeration || ac->desc().integer_step) {
801 
802 		/* use Controllable::get_value() to avoid the
803 		 * "scaling-to-interface" that takes place in
804 		 * Control::get_value() via the pot member.
805 		 *
806 		 * an enumeration with 4 values will have interface values of
807 		 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
808 		 * deal with that.
809 		 */
810 
811 		if (delta > 0) {
812 			ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
813 		} else {
814 			ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
815 		}
816 
817 	} else {
818 
819 		double p = ac->get_interface(true);
820 
821 		p += delta;
822 
823 		p = max (0.0, p);
824 		p = min (1.0, p);
825 
826 		ac->set_interface ( p, true);
827 	}
828 }
829 
830 void
periodic(PBD::microseconds_t now)831 Strip::periodic (PBD::microseconds_t now)
832 {
833 	update_meter ();
834 	update_automation ();
835 }
836 
837 void
redisplay(PBD::microseconds_t now,bool force)838 Strip::redisplay (PBD::microseconds_t now, bool force)
839 {
840 	if (_block_screen_redisplay_until >= now) {
841 		/* no drawing allowed */
842 		return;
843 	}
844 
845 	if (_block_screen_redisplay_until) {
846 		/* we were blocked, but the time period has elapsed, so we must
847 		 * force a redraw.
848 		 */
849 		force = true;
850 		_block_screen_redisplay_until = 0;
851 	}
852 
853 	if (force || (current_display[0] != pending_display[0])) {
854 		_surface->write (display (0, pending_display[0]));
855 		current_display[0] = pending_display[0];
856 	}
857 
858 	if (return_to_vpot_mode_display_at <= now) {
859 		return_to_vpot_mode_display_at = UINT64_MAX;
860 		return_to_vpot_mode_display ();
861 	}
862 
863 	if (force || (current_display[1] != pending_display[1])) {
864 		_surface->write (display (1, pending_display[1]));
865 		current_display[1] = pending_display[1];
866 	}
867 }
868 
869 void
update_automation()870 Strip::update_automation ()
871 {
872 	if (!_stripable) {
873 		return;
874 	}
875 
876 	ARDOUR::AutoState state = _stripable->gain_control()->automation_state();
877 
878 	if (state == Touch || state == Play) {
879 		notify_gain_changed (false);
880 	}
881 
882 	boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
883 	if (pan_control) {
884 		state = pan_control->automation_state ();
885 		if (state == Touch || state == Play) {
886 			notify_panner_azi_changed (false);
887 		}
888 	}
889 
890 	pan_control = _stripable->pan_width_control ();
891 	if (pan_control) {
892 		state = pan_control->automation_state ();
893 		if (state == Touch || state == Play) {
894 			notify_panner_width_changed (false);
895 		}
896 	}
897 }
898 
899 void
update_meter()900 Strip::update_meter ()
901 {
902 	if (!_stripable) {
903 		return;
904 	}
905 
906 	if (_surface->mcp().subview()->subview_mode() != Subview::None) {
907 		return;
908 	}
909 
910 	if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
911 		float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
912 		_meter->send_update (*_surface, dB);
913 		return;
914 	}
915 }
916 
917 void
zero()918 Strip::zero ()
919 {
920 	for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
921 		_surface->write ((*it)->zero ());
922 	}
923 
924 	_surface->write (blank_display (0));
925 	_surface->write (blank_display (1));
926 	pending_display[0] = string();
927 	pending_display[1] = string();
928 	current_display[0] = string();
929 	current_display[1] = string();
930 }
931 
932 MidiByteArray
blank_display(uint32_t line_number)933 Strip::blank_display (uint32_t line_number)
934 {
935 	return display (line_number, string());
936 }
937 
938 MidiByteArray
display(uint32_t line_number,const std::string & line)939 Strip::display (uint32_t line_number, const std::string& line)
940 {
941 	assert (line_number <= 1);
942 
943 	MidiByteArray retval;
944 
945 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
946 
947 	// sysex header
948 	retval << _surface->sysex_hdr();
949 
950 	// code for display
951 	retval << 0x12;
952 	// offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
953 	retval << (_index * 7 + (line_number * 0x38));
954 
955 	// ascii data to display. @param line is UTF-8
956 	string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
957 	string::size_type len = ascii.length();
958 	if (len > 6) {
959 		ascii = ascii.substr (0, 6);
960 		len = 6;
961 	}
962 	retval << ascii;
963 	// pad with " " out to 6 chars
964 	for (int i = len; i < 6; ++i) {
965 		retval << ' ';
966 	}
967 
968 	// column spacer, unless it's the right-hand column
969 	if (_index < 7) {
970 		retval << ' ';
971 	}
972 
973 	// sysex trailer
974 	retval << MIDI::eox;
975 
976 	return retval;
977 }
978 
979 void
lock_controls()980 Strip::lock_controls ()
981 {
982 	_controls_locked = true;
983 }
984 
985 void
unlock_controls()986 Strip::unlock_controls ()
987 {
988 	_controls_locked = false;
989 }
990 
991 string
vpot_mode_string()992 Strip::vpot_mode_string ()
993 {
994 	if (_surface->mcp().subview()->subview_mode() != Subview::None) {
995 		return string();
996 	}
997 
998 	boost::shared_ptr<AutomationControl> ac = _vpot->control();
999 
1000 	if (!ac) {
1001 		return string();
1002 	}
1003 
1004 	switch (ac->desc().type) {
1005 	case PanAzimuthAutomation:
1006 		return "Pan";
1007 	case PanWidthAutomation:
1008 		return "Width";
1009 	case PanElevationAutomation:
1010 		return "Elev";
1011 	case PanFrontBackAutomation:
1012 		return "F/Rear";
1013 	case PanLFEAutomation:
1014 		return "LFE";
1015 	default:
1016 		break;
1017 	}
1018 #ifdef MIXBUS
1019 	//"None" mode, by definition (currently) shows the pan control above the fader.
1020 	//Mixbus controllers are created from a LADSPA so they don't have ac->desc().type
1021 	//For the forseeable future, we will just return "Pan" here.
1022 	return "Pan";
1023 #endif
1024 
1025 	return "???";
1026 }
1027 
1028 void
flip_mode_changed()1029 Strip::flip_mode_changed ()
1030 {
1031 	if (_surface->mcp().subview()->permit_flipping_faders_and_pots()) {
1032 
1033 		boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1034 		boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1035 
1036 		if (pot_control && fader_control) {
1037 
1038 			_vpot->set_control (fader_control);
1039 			_fader->set_control (pot_control);
1040 
1041 			/* update fader with pot value */
1042 
1043 			_surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1044 
1045 			/* update pot with fader value */
1046 
1047 			_surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1048 
1049 
1050 			if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1051 				do_parameter_display (fader_control->desc(), fader_control->get_value());
1052 			} else {
1053 				do_parameter_display (pot_control->desc(), pot_control->get_value()); // BusSendLevel
1054 			}
1055 
1056 		}
1057 
1058 	} else {
1059 		/* do nothing */
1060 	}
1061 }
1062 
1063 void
block_screen_display_for(uint32_t msecs)1064 Strip::block_screen_display_for (uint32_t msecs)
1065 {
1066 	_block_screen_redisplay_until = PBD::get_microseconds() + (msecs * 1000);
1067 }
1068 
1069 void
block_vpot_mode_display_for(uint32_t msecs)1070 Strip::block_vpot_mode_display_for (uint32_t msecs)
1071 {
1072 	return_to_vpot_mode_display_at = PBD::get_microseconds() + (msecs * 1000);
1073 }
1074 
1075 void
return_to_vpot_mode_display()1076 Strip::return_to_vpot_mode_display ()
1077 {
1078 	/* returns the second line of the two-line per-strip display
1079 	   back the mode where it shows what the VPot controls.
1080 	*/
1081 
1082 	if (_surface->mcp().subview()->subview_mode() != Subview::None) {
1083 		/* do nothing - second line shows value of current subview parameter */
1084 		return;
1085 	} else if (_stripable) {
1086 		pending_display[1] = vpot_mode_string();
1087 	} else {
1088 		pending_display[1] = string();
1089 	}
1090 }
1091 
1092 void
next_pot_mode()1093 Strip::next_pot_mode ()
1094 {
1095 	vector<AutomationType>::iterator i;
1096 
1097 	if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1098 		/* do not change vpot mode while in flipped mode */
1099 		DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1100 		pending_display[1] = "Flip";
1101 		block_vpot_mode_display_for (1000);
1102 		return;
1103 	}
1104 
1105 
1106 	boost::shared_ptr<AutomationControl> ac = _vpot->control();
1107 
1108 	if (!ac) {
1109 		return;
1110 	}
1111 
1112 
1113 	if (_surface->mcp().subview()->subview_mode() != Subview::None) {
1114 		return;
1115 	}
1116 
1117 	if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1118 		return;
1119 	}
1120 
1121 	for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1122 		if ((*i) == ac->parameter().type()) {
1123 			break;
1124 		}
1125 	}
1126 
1127 	/* move to the next mode in the list, or back to the start (which will
1128 	   also happen if the current mode is not in the current pot mode list)
1129 	*/
1130 
1131 	if (i != possible_pot_parameters.end()) {
1132 		++i;
1133 	}
1134 
1135 	if (i == possible_pot_parameters.end()) {
1136 		i = possible_pot_parameters.begin();
1137 	}
1138 
1139 	set_vpot_parameter (*i);
1140 }
1141 
1142 void
subview_mode_changed()1143 Strip::subview_mode_changed ()
1144 {
1145 	switch (_surface->mcp().subview()->subview_mode()) {
1146 	case Subview::None:
1147 		set_vpot_parameter (_pan_mode);
1148 		/* need to show strip name again */
1149 		show_stripable_name ();
1150 		if (!_stripable) {
1151 			_surface->write (_vpot->set (0, true, Pot::wrap));
1152 			_surface->write (_fader->set_position (0.0));
1153 		}
1154 		notify_metering_state_changed ();
1155 		break;
1156 
1157 	case Subview::EQ:
1158 	case Subview::Dynamics:
1159 	case Subview::Sends:
1160 	case Subview::TrackView:
1161 	case Subview::Plugin:
1162 		_surface->mcp().subview()->setup_vpot(this, _vpot, pending_display);
1163 		break;
1164 	}
1165 }
1166 
1167 void
set_vpot_parameter(AutomationType p)1168 Strip::set_vpot_parameter (AutomationType p)
1169 {
1170 	if (!_stripable || (p == NullAutomation)) {
1171 		_vpot->set_control (boost::shared_ptr<AutomationControl>());
1172 		pending_display[1] = string();
1173 		return;
1174 	}
1175 
1176 	boost::shared_ptr<AutomationControl> pan_control;
1177 
1178 	DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1179 
1180 	reset_saved_values ();
1181 
1182 	switch (p) {
1183 	case PanAzimuthAutomation:
1184 		pan_control = _stripable->pan_azimuth_control ();
1185 		break;
1186 	case PanWidthAutomation:
1187 		pan_control = _stripable->pan_width_control ();
1188 		break;
1189 	case PanElevationAutomation:
1190 		break;
1191 	case PanFrontBackAutomation:
1192 		break;
1193 	case PanLFEAutomation:
1194 		break;
1195 	default:
1196 		return;
1197 	}
1198 
1199 	if (pan_control) {
1200 		_pan_mode = p;
1201 		_vpot->set_control (pan_control);
1202 	}
1203 
1204 	pending_display[1] = vpot_mode_string ();
1205 }
1206 
1207 bool
is_midi_track() const1208 Strip::is_midi_track () const
1209 {
1210 	return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
1211 }
1212 
1213 void
reset_saved_values()1214 Strip::reset_saved_values ()
1215 {
1216 	_last_pan_azi_position_written = -1.0;
1217 	_last_pan_width_position_written = -1.0;
1218 	_last_gain_position_written = -1.0;
1219 	_last_trim_position_written = -1.0;
1220 
1221 }
1222 
1223 void
notify_metering_state_changed()1224 Strip::notify_metering_state_changed()
1225 {
1226 	if (_surface->mcp().subview()->subview_mode() != Subview::None) {
1227 		return;
1228 	}
1229 
1230 	if (!_stripable || !_meter) {
1231 		return;
1232 	}
1233 
1234 	bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1235 	bool metering_active = _surface->mcp().metering_active ();
1236 
1237 	if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1238 		return;
1239 	}
1240 
1241 	_meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1242 
1243 	if (!transport_is_rolling || !metering_active) {
1244 		notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1245 		notify_panner_azi_changed (true);
1246 	}
1247 
1248 	_transport_is_rolling = transport_is_rolling;
1249 	_metering_active = metering_active;
1250 }
1251