Remove unnecessary tests for touch-state
[ardour.git] / libs / surfaces / faderport8 / fp8_strip.cc
1 /*
2  * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #include "ardour/automation_control.h"
20 #include "ardour/gain_control.h"
21 #include "ardour/meter.h"
22 #include "ardour/plugin_insert.h"
23 #include "ardour/session.h"
24 #include "ardour/stripable.h"
25 #include "ardour/track.h"
26 #include "ardour/value_as_string.h"
27
28 #include "control_protocol/control_protocol.h"
29
30 #include "fp8_strip.h"
31
32 using namespace ARDOUR;
33 using namespace ArdourSurface;
34 using namespace ArdourSurface::FP8Types;
35
36 FP8Strip::FP8Strip (FP8Base& b, uint8_t id)
37         : _base (b)
38         , _id (id)
39         , _solo   (b, 0x08 + id)
40         , _mute   (b, 0x10 + id)
41         , _selrec (b, 0x18 + id, true)
42         , _touching (false)
43         , _strip_mode (0)
44         , _bar_mode (0)
45         , _displaymode (Stripables)
46 {
47         assert (id < 8);
48
49         _last_fader = 65535;
50         _last_meter = _last_redux = _last_barpos = 0xff;
51
52         _mute.StateChange.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_mute, this, _1));
53         _solo.StateChange.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_solo, this, _1));
54         select_button ().released.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_select, this));
55         recarm_button ().released.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_recarm, this));
56         b.Periodic.connect_same_thread (_base_connection, boost::bind (&FP8Strip::periodic, this));
57 }
58
59 FP8Strip::~FP8Strip ()
60 {
61         drop_automation_controls ();
62         _base_connection.disconnect ();
63         _button_connections.drop_connections ();
64 }
65
66 void
67 FP8Strip::drop_automation_controls ()
68 {
69         _fader_connection.disconnect ();
70         _mute_connection.disconnect ();
71         _solo_connection.disconnect ();
72         _rec_connection.disconnect ();
73         _pan_connection.disconnect ();
74         _x_select_connection.disconnect ();
75
76         _fader_ctrl.reset ();
77         _mute_ctrl.reset ();
78         _solo_ctrl.reset ();
79         _rec_ctrl.reset ();
80         _pan_ctrl.reset ();
81         _x_select_ctrl.reset ();
82         _peak_meter.reset ();
83         _redux_ctrl.reset ();
84         _select_plugin_functor.clear ();
85 }
86
87 void
88 FP8Strip::initialize ()
89 {
90         /* this is called once midi transmission is possible,
91          * ie from FaderPort8::connected()
92          */
93         _solo.set_active (false);
94         _solo.set_blinking (false);
95         _mute.set_active (false);
96
97         /* reset momentary button state */
98         _mute.reset ();
99         _solo.reset ();
100
101         drop_automation_controls ();
102
103         select_button ().set_color (0xffffffff);
104         select_button ().set_active (false);
105         select_button ().set_blinking (false);
106
107         recarm_button ().set_active (false);
108         recarm_button ().set_color (0xffffffff);
109
110         set_strip_mode (0, true);
111
112         // force unset txt
113         _last_line[0].clear ();
114         _last_line[1].clear ();
115         _last_line[2].clear ();
116         _last_line[3].clear ();
117         _base.tx_sysex (4, 0x12, _id, 0x00, 0x00);
118         _base.tx_sysex (4, 0x12, _id, 0x01, 0x00);
119         _base.tx_sysex (4, 0x12, _id, 0x02, 0x00);
120         _base.tx_sysex (4, 0x12, _id, 0x03, 0x00);
121
122         set_bar_mode (4); // off
123
124         _base.tx_midi2 (0xd0 + _id, 0); // reset meter
125         _base.tx_midi2 (0xd8 + _id, 0); // reset redux
126
127         _base.tx_midi3 (0xe0 + _id, 0, 0); // fader
128
129         /* clear cached values */
130         _last_fader = 65535;
131         _last_meter = _last_redux = _last_barpos = 0xff;
132 }
133
134
135 #define GENERATE_SET_CTRL_FUNCTION(NAME)                                            \
136 void                                                                                \
137 FP8Strip::set_ ##NAME##_controllable (boost::shared_ptr<AutomationControl> ac)      \
138 {                                                                                   \
139   if (_##NAME##_ctrl == ac) {                                                       \
140     return;                                                                         \
141   }                                                                                 \
142   _##NAME##_connection.disconnect();                                                \
143   _##NAME##_ctrl = ac;                                                              \
144                                                                                     \
145   if (ac) {                                                                         \
146     ac->Changed.connect (_##NAME##_connection, MISSING_INVALIDATOR,                 \
147       boost::bind (&FP8Strip::notify_##NAME##_changed, this), fp8_context());       \
148   }                                                                                 \
149   notify_##NAME##_changed ();                                                       \
150 }
151
152
153 GENERATE_SET_CTRL_FUNCTION (fader)
154 GENERATE_SET_CTRL_FUNCTION (mute)
155 GENERATE_SET_CTRL_FUNCTION (solo)
156 GENERATE_SET_CTRL_FUNCTION (rec)
157 GENERATE_SET_CTRL_FUNCTION (pan)
158 GENERATE_SET_CTRL_FUNCTION (x_select)
159
160 #undef GENERATE_SET_CTRL_FUNCTION
161
162 // special case -- w/_select_plugin_functor
163 void
164 FP8Strip::set_select_controllable (boost::shared_ptr<AutomationControl> ac)
165 {
166         _select_plugin_functor.clear ();
167         set_x_select_controllable (ac);
168 }
169
170 void
171 FP8Strip::set_select_cb (boost::function<void ()>& functor)
172 {
173         set_select_controllable (boost::shared_ptr<AutomationControl>());
174         _select_plugin_functor = functor;
175 }
176
177 void
178 FP8Strip::unset_controllables (int which)
179 {
180         _peak_meter = boost::shared_ptr<ARDOUR::PeakMeter>();
181         _redux_ctrl = boost::shared_ptr<ARDOUR::ReadOnlyControl>();
182         _stripable_name.clear ();
183
184         if (which & CTRL_FADER) {
185                 set_fader_controllable (boost::shared_ptr<AutomationControl>());
186         }
187         if (which & CTRL_MUTE) {
188                 set_mute_controllable (boost::shared_ptr<AutomationControl>());
189         }
190         if (which & CTRL_SOLO) {
191                 set_solo_controllable (boost::shared_ptr<AutomationControl>());
192         }
193         if (which & CTRL_REC) {
194                 set_rec_controllable (boost::shared_ptr<AutomationControl>());
195         }
196         if (which & CTRL_PAN) {
197                 set_pan_controllable (boost::shared_ptr<AutomationControl>());
198         }
199         if (which & CTRL_SELECT) {
200                 set_select_controllable (boost::shared_ptr<AutomationControl>());
201                 select_button ().set_color (0xffffffff);
202                 select_button ().set_active (false);
203                 select_button ().set_blinking (false);
204         }
205         if (which & CTRL_TEXT0) {
206                 set_text_line (0, "");
207         }
208         if (which & CTRL_TEXT1) {
209                 set_text_line (1, "");
210         }
211         if (which & CTRL_TEXT2) {
212                 set_text_line (2, "");
213         }
214         if (which & CTRL_TEXT3) {
215                 set_text_line (3, "");
216         }
217         set_bar_mode (4); // Off
218 }
219
220 void
221 FP8Strip::set_strip_name ()
222 {
223         size_t lb = _base.show_meters () ? 6 : 9;
224         set_text_line (0, _stripable_name.substr (0, lb));
225         set_text_line (1, _stripable_name.length() > lb ? _stripable_name.substr (lb) : "");
226 }
227
228 void
229 FP8Strip::set_stripable (boost::shared_ptr<Stripable> s, bool panmode)
230 {
231         assert (s);
232
233         if (_base.show_meters () && _base.show_panner ()) {
234                 set_strip_mode (5, true);
235         } else if (_base.show_meters ()) {
236                 set_strip_mode (4, true);
237         } else {
238                 set_strip_mode (0, true);
239         }
240         if (!_base.show_panner ()) {
241                 set_bar_mode (4, true); // Off
242         }
243
244         if (panmode) {
245                 set_fader_controllable (s->pan_azimuth_control ());
246         } else {
247                 set_fader_controllable (s->gain_control ());
248         }
249         set_pan_controllable (s->pan_azimuth_control ());
250
251         if (s->is_monitor ()) {
252                 set_mute_controllable (boost::shared_ptr<AutomationControl>());
253         } else {
254                 set_mute_controllable (s->mute_control ());
255         }
256         set_solo_controllable (s->solo_control ());
257
258         if (boost::dynamic_pointer_cast<Track> (s)) {
259                 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
260                 set_rec_controllable (t->rec_enable_control ());
261                 recarm_button ().set_color (0xff0000ff);
262         } else {
263                 set_rec_controllable (boost::shared_ptr<AutomationControl>());
264                 recarm_button ().set_color (0xffffffff);
265                 recarm_button ().set_active (false);
266         }
267         _peak_meter = s->peak_meter ();
268         _redux_ctrl = s->comp_redux_controllable ();
269
270         set_select_controllable (boost::shared_ptr<AutomationControl>());
271         select_button ().set_active (s->is_selected ());
272         select_button ().set_color (s->presentation_info ().color());
273         //select_button ().set_blinking (false);
274
275         _stripable_name = s->name ();
276
277         if (_base.twolinetext ()) {
278                 set_strip_name ();
279         } else {
280                 set_text_line (0, s->name ());
281                 set_text_line (1, _pan_ctrl ? _pan_ctrl->get_user_string () : "");
282         }
283         set_text_line (2, "");
284         set_text_line (3, "");
285 }
286
287 /* *****************************************************************************
288  * Parse Strip Specifig MIDI Events
289  */
290
291 bool
292 FP8Strip::midi_touch (bool t)
293 {
294         _touching = t;
295         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
296         if (!ac) {
297                 return false;
298         }
299         if (t) {
300                 ac->start_touch (ac->session().transport_frame());
301         } else {
302                 ac->stop_touch (ac->session().transport_frame());
303         }
304         return true;
305 }
306
307 bool
308 FP8Strip::midi_fader (float val)
309 {
310         assert (val >= 0.f && val <= 1.f);
311         if (!_touching) {
312                 return false;
313         }
314         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
315         if (!ac) {
316                 return false;
317         }
318         ac->start_touch (ac->session().transport_frame());
319         ac->set_value (ac->interface_to_internal (val), group_mode ());
320         return true;
321 }
322
323 /* *****************************************************************************
324  * Actions from Controller, Update Model
325  */
326
327 PBD::Controllable::GroupControlDisposition
328 FP8Strip::group_mode () const
329 {
330         if (_base.shift_mod ()) {
331                 return PBD::Controllable::InverseGroup;
332         } else {
333                 return PBD::Controllable::UseGroup;
334         }
335 }
336
337 void
338 FP8Strip::set_mute (bool on)
339 {
340         if (!_mute_ctrl) {
341                 return;
342         }
343         _mute_ctrl->start_touch (_mute_ctrl->session().transport_frame());
344         _mute_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
345 }
346
347 void
348 FP8Strip::set_solo (bool on)
349 {
350         if (!_solo_ctrl) {
351                 return;
352         }
353         _solo_ctrl->start_touch (_solo_ctrl->session().transport_frame());
354         _solo_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
355 }
356
357 void
358 FP8Strip::set_recarm ()
359 {
360         if (!_rec_ctrl) {
361                 return;
362         }
363         const bool on = !recarm_button ().is_active();
364         _rec_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
365 }
366
367 void
368 FP8Strip::set_select ()
369 {
370         if (!_select_plugin_functor.empty ()) {
371                 assert (!_x_select_ctrl);
372                 _select_plugin_functor ();
373         } else if (_x_select_ctrl) {
374                 _x_select_ctrl->start_touch (_x_select_ctrl->session().transport_frame());
375                 const bool on = !select_button ().is_active();
376                 _x_select_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
377         }
378 }
379
380 /* *****************************************************************************
381  * Callbacks from Stripable, Update View
382  */
383
384 void
385 FP8Strip::notify_fader_changed ()
386 {
387         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
388         if (_touching) {
389                 return;
390         }
391         float val = 0;
392         if (ac) {
393                 val = ac->internal_to_interface (ac->get_value());
394                 val = std::max (0.f, std::min (1.f, val)) * 16368.f; /* 16 * 1023 */
395         }
396         unsigned short mv = lrintf (val);
397         if (mv == _last_fader) {
398                 return;
399         }
400         _last_fader = mv;
401         _base.tx_midi3 (0xe0 + _id, (mv & 0x7f), (mv >> 7) & 0x7f);
402 }
403
404 void
405 FP8Strip::notify_solo_changed ()
406 {
407         if (_solo_ctrl) {
408                 boost::shared_ptr<SoloControl> sc = boost::dynamic_pointer_cast<SoloControl> (_solo_ctrl);
409                 if (sc) {
410                         _solo.set_blinking (sc->soloed_by_others () && !sc->self_soloed ());
411                         _solo.set_active (sc->self_soloed ());
412                 } else {
413                         _solo.set_blinking (false);
414                         _solo.set_active (_solo_ctrl->get_value () > 0);
415                 }
416         } else {
417                 _solo.set_blinking (false);
418                 _solo.set_active (false);
419         }
420 }
421
422 void
423 FP8Strip::notify_mute_changed ()
424 {
425         if (_mute_ctrl) {
426                 _mute.set_active (_mute_ctrl->get_value () > 0);
427         } else {
428                 _mute.set_active (false);
429         }
430 }
431
432 void
433 FP8Strip::notify_rec_changed ()
434 {
435         if (_rec_ctrl) {
436                 recarm_button ().set_active (_rec_ctrl->get_value() > 0.);
437         } else {
438                 recarm_button ().set_active (false);
439         }
440 }
441
442 void
443 FP8Strip::notify_pan_changed ()
444 {
445         // display only
446 }
447
448 void
449 FP8Strip::notify_x_select_changed ()
450 {
451         if (!_select_plugin_functor.empty ()) {
452                 assert (!_x_select_ctrl);
453                 return;
454         }
455
456         if (_x_select_ctrl) {
457                 assert (_select_plugin_functor.empty ());
458                 select_button ().set_active (_x_select_ctrl->get_value() > 0.);
459                 select_button ().set_color (0xffff00ff);
460                 select_button ().set_blinking (false);
461         }
462 }
463
464 /* *****************************************************************************
465  * Periodic View Updates 
466  */
467
468 void
469 FP8Strip::periodic_update_fader ()
470 {
471         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
472         if (!ac || _touching) {
473                 return;
474         }
475
476         if (!ac->automation_playback ()) {
477                 return;
478         }
479         notify_fader_changed ();
480 }
481
482 void
483 FP8Strip::set_periodic_display_mode (DisplayMode m) {
484         _displaymode = m;
485         if (_displaymode == SendDisplay || _displaymode == PluginParam) {
486                 // need to change to 4 lines before calling set_text()
487                 set_strip_mode (2); // 4 lines of small text
488         }
489 }
490
491 void
492 FP8Strip::periodic_update_meter ()
493 {
494         bool show_meters = _base.show_meters ();
495         bool have_meter = false;
496         bool have_panner = false;
497
498         if (_peak_meter && show_meters) {
499                 have_meter = true;
500                 float dB = _peak_meter->meter_level (0, MeterMCP);
501                 // TODO: deflect meter
502                 int val = std::min (127.f, std::max (0.f, 2.f * dB + 127.f));
503                 if (val != _last_meter || val > 0) {
504                         _base.tx_midi2 (0xd0 + _id, val & 0x7f); // falls off automatically
505                         _last_meter = val;
506                 }
507
508         } else if (show_meters) {
509                 if (0 != _last_meter) {
510                         _base.tx_midi2 (0xd0 + _id, 0);
511                         _last_meter = 0;
512                 }
513         }
514
515         // show redux only if there's a meter, too  (strip display mode 5)
516         if (_peak_meter && _redux_ctrl && show_meters) {
517                 float rx = (1.f - _redux_ctrl->get_parameter ()) * 127.f;
518                 // TODO: deflect redux
519                 int val = std::min (127.f, std::max (0.f, rx));
520                 if (val != _last_redux) {
521                         _base.tx_midi2 (0xd8 + _id, val & 0x7f);
522                         _last_redux = val;
523                 }
524         } else if (show_meters) {
525                 if (0 != _last_redux) {
526                         _base.tx_midi2 (0xd8 + _id, 0);
527                         _last_redux = 0;
528                 }
529         }
530
531         if (_displaymode == PluginParam) {
532                 if (_fader_ctrl) {
533                         set_bar_mode (2); // Fill
534                         set_text_line (2, value_as_string(_fader_ctrl->desc(), _fader_ctrl->get_value()));
535                         float barpos = _fader_ctrl->internal_to_interface (_fader_ctrl->get_value());
536                         int val = std::min (127.f, std::max (0.f, barpos * 128.f));
537                         if (val != _last_barpos) {
538                                 _base.tx_midi3 (0xb0, 0x30 + _id, val & 0x7f);
539                                 _last_barpos = val;
540                         }
541                 } else {
542                         set_bar_mode (4); // Off
543                         set_text_line (2, "");
544                 }
545         }
546         else if (_displaymode == PluginSelect) {
547                 set_bar_mode (4); // Off
548         }
549         else if (_displaymode == SendDisplay) {
550                 set_bar_mode (4); // Off
551                 if (_fader_ctrl) {
552                         set_text_line (1, value_as_string(_fader_ctrl->desc(), _fader_ctrl->get_value()));
553                 } else {
554                         set_text_line (1, "");
555                 }
556         } else if (_pan_ctrl) {
557                 have_panner = _base.show_panner ();
558                 float panpos = _pan_ctrl->internal_to_interface (_pan_ctrl->get_value());
559                 int val = std::min (127.f, std::max (0.f, panpos * 128.f));
560                 set_bar_mode (have_panner ? 1 : 4); // Bipolar or Off
561                 if (val != _last_barpos && have_panner) {
562                         _base.tx_midi3 (0xb0, 0x30 + _id, val & 0x7f);
563                         _last_barpos = val;
564                 }
565                 if (_base.twolinetext ()) {
566                         set_strip_name ();
567                 } else {
568                         set_text_line (1, _pan_ctrl->get_user_string ());
569                 }
570         } else {
571                 set_bar_mode (4); // Off
572                 if (_base.twolinetext ()) {
573                         set_strip_name ();
574                 } else {
575                         set_text_line (1, "");
576                 }
577         }
578
579         if (_displaymode == SendDisplay || _displaymode == PluginParam) {
580                 set_strip_mode (2); // 4 lines of small text + value-bar
581         }
582         else if (have_meter && have_panner) {
583                 set_strip_mode (5); // small meters + 3 lines of text (3rd is large)  + value-bar
584         }
585         else if (have_meter) {
586                 set_strip_mode (4); // big meters + 3 lines of text (3rd line is large)
587         }
588         else if (have_panner) {
589                 set_strip_mode (0); // 3 lines of text (3rd line is large + long) + value-bar
590         } else {
591                 set_strip_mode (0); // 3 lines of text (3rd line is large + long) + value-bar
592         }
593 }
594
595 void
596 FP8Strip::set_strip_mode (uint8_t strip_mode, bool clear)
597 {
598         if (strip_mode == _strip_mode && !clear) {
599                 return;
600         }
601
602         _strip_mode = strip_mode;
603         _base.tx_sysex (3, 0x13, _id, (_strip_mode & 0x07) | (clear ? 0x10 : 0));
604
605         if (clear) {
606                 /* work-around, when swiching modes, the FP8 may not
607                  * properly redraw long lines. Only update lines 0, 1
608                  * (line 2 is timecode, line 3 may be inverted)
609                  */
610                 _base.tx_text (_id, 0, 0x00, _last_line[0]);
611                 _base.tx_text (_id, 1, 0x00, _last_line[1]);
612         }
613 }
614
615 void
616 FP8Strip::set_bar_mode (uint8_t bar_mode, bool force)
617 {
618         if (bar_mode == _bar_mode && !force) {
619                 return;
620         }
621
622         if (bar_mode == 4) {
623                 _base.tx_midi3 (0xb0, 0x30 + _id, 0);
624                 _last_barpos = 0xff;
625         }
626
627         _bar_mode = bar_mode;
628         _base.tx_midi3 (0xb0, 0x38 + _id, bar_mode);
629 }
630
631 void
632 FP8Strip::set_text_line (uint8_t line, std::string const& txt, bool inv)
633 {
634         assert (line < 4);
635         if (_last_line[line] == txt) {
636                 return;
637         }
638         _base.tx_text (_id, line, inv ? 0x04 : 0x00, txt);
639         _last_line[line] = txt;
640 }
641
642 void
643 FP8Strip::periodic_update_timecode (uint32_t m)
644 {
645         if (m == 0) {
646                 return;
647         }
648         if (m == 3) {
649                 bool mc = _id >= 4;
650                 std::string const& tc = mc ? _base.musical_time () : _base.timecode();
651                 std::string t;
652                 if (tc.size () == 12) {
653                         t = tc.substr (1 + (_id - (mc ? 4 : 0)) * 3, 2);
654                 }
655                 set_text_line (2, t);
656         } else if (_id >= 2 && _id < 6) {
657                 std::string const& tc = (m == 2) ? _base.musical_time () : _base.timecode();
658                 //" HH:MM:SS:FF" or " BR|BT|TI|CK"
659                 std::string t;
660                 if (tc.size () == 12) {
661                         t = tc.substr (1 + (_id - 2) * 3, 2);
662                 }
663                 set_text_line (2, t);
664         } else {
665                 set_text_line (2, "");
666         }
667 }
668
669 void
670 FP8Strip::periodic ()
671 {
672         periodic_update_fader ();
673         periodic_update_meter ();
674         if (_displaymode != PluginSelect && _displaymode != PluginParam) {
675                 periodic_update_timecode (_base.clock_mode ());
676         }
677 }