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