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