FP8: allow to re-initialize the Strips w/o restart
[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         _mute.set_active (false);
95
96         /* reset momentary button state */
97         _mute.reset ();
98         _solo.reset ();
99
100         drop_automation_controls ();
101
102         select_button ().set_color (0xffffffff);
103         select_button ().set_active (false);
104         select_button ().set_blinking (false);
105
106         recarm_button ().set_active (false);
107         recarm_button ().set_color (0xffffffff);
108
109         set_strip_mode (0, true);
110
111         // force unset txt
112         _last_line[0].clear ();
113         _last_line[1].clear ();
114         _last_line[2].clear ();
115         _last_line[3].clear ();
116         _base.tx_sysex (4, 0x12, _id, 0x00, 0x00);
117         _base.tx_sysex (4, 0x12, _id, 0x01, 0x00);
118         _base.tx_sysex (4, 0x12, _id, 0x02, 0x00);
119         _base.tx_sysex (4, 0x12, _id, 0x03, 0x00);
120
121         set_bar_mode (4); // off
122
123         _base.tx_midi2 (0xd0 + _id, 0); // reset meter
124         _base.tx_midi2 (0xd8 + _id, 0); // reset redux
125
126         _base.tx_midi3 (0xe0 + _id, 0, 0); // fader
127
128         /* clear cached values */
129         _last_fader = 65535;
130         _last_meter = _last_redux = _last_barpos = 0xff;
131 }
132
133
134 #define GENERATE_SET_CTRL_FUNCTION(NAME)                                            \
135 void                                                                                \
136 FP8Strip::set_ ##NAME##_controllable (boost::shared_ptr<AutomationControl> ac)      \
137 {                                                                                   \
138   if (_##NAME##_ctrl == ac) {                                                       \
139     return;                                                                         \
140   }                                                                                 \
141   _##NAME##_connection.disconnect();                                                \
142   _##NAME##_ctrl = ac;                                                              \
143                                                                                     \
144   if (ac) {                                                                         \
145     ac->Changed.connect (_##NAME##_connection, MISSING_INVALIDATOR,                 \
146       boost::bind (&FP8Strip::notify_##NAME##_changed, this), fp8_context());       \
147   }                                                                                 \
148   notify_##NAME##_changed ();                                                       \
149 }
150
151
152 GENERATE_SET_CTRL_FUNCTION (fader)
153 GENERATE_SET_CTRL_FUNCTION (mute)
154 GENERATE_SET_CTRL_FUNCTION (solo)
155 GENERATE_SET_CTRL_FUNCTION (rec)
156 GENERATE_SET_CTRL_FUNCTION (pan)
157 GENERATE_SET_CTRL_FUNCTION (x_select)
158
159 #undef GENERATE_SET_CTRL_FUNCTION
160
161 // special case -- w/_select_plugin_functor
162 void
163 FP8Strip::set_select_controllable (boost::shared_ptr<AutomationControl> ac)
164 {
165         _select_plugin_functor.clear ();
166         set_x_select_controllable (ac);
167 }
168
169 void
170 FP8Strip::set_select_cb (boost::function<void ()>& functor)
171 {
172         set_select_controllable (boost::shared_ptr<AutomationControl>());
173         _select_plugin_functor = functor;
174 }
175
176 void
177 FP8Strip::unset_controllables (int which)
178 {
179         _peak_meter = boost::shared_ptr<ARDOUR::PeakMeter>();
180         _redux_ctrl = boost::shared_ptr<ARDOUR::ReadOnlyControl>();
181
182         if (which & CTRL_FADER) {
183                 set_fader_controllable (boost::shared_ptr<AutomationControl>());
184         }
185         if (which & CTRL_MUTE) {
186                 set_mute_controllable (boost::shared_ptr<AutomationControl>());
187         }
188         if (which & CTRL_SOLO) {
189                 set_solo_controllable (boost::shared_ptr<AutomationControl>());
190         }
191         if (which & CTRL_REC) {
192                 set_rec_controllable (boost::shared_ptr<AutomationControl>());
193         }
194         if (which & CTRL_PAN) {
195                 set_pan_controllable (boost::shared_ptr<AutomationControl>());
196         }
197         if (which & CTRL_SELECT) {
198                 set_select_controllable (boost::shared_ptr<AutomationControl>());
199                 select_button ().set_color (0xffffffff);
200                 select_button ().set_active (false);
201                 select_button ().set_blinking (false);
202         }
203         if (which & CTRL_TEXT0) {
204                 set_text_line (0x00, "");
205         }
206         if (which & CTRL_TEXT1) {
207                 set_text_line (0x01, "");
208         }
209         if (which & CTRL_TEXT2) {
210                 set_text_line (0x02, "");
211         }
212         if (which & CTRL_TEXT3) {
213                 set_text_line (0x03, "");
214         }
215         set_bar_mode (4); // Off
216 }
217
218 void
219 FP8Strip::set_stripable (boost::shared_ptr<Stripable> s, bool panmode)
220 {
221         assert (s);
222
223         if (panmode) {
224                 set_fader_controllable (s->pan_azimuth_control ());
225         } else {
226                 set_fader_controllable (s->gain_control ());
227         }
228         set_pan_controllable (s->pan_azimuth_control ());
229
230         if (s->is_monitor ()) {
231                 set_mute_controllable (boost::shared_ptr<AutomationControl>());
232         } else {
233                 set_mute_controllable (s->mute_control ());
234         }
235         set_solo_controllable (s->solo_control ());
236
237         if (boost::dynamic_pointer_cast<Track> (s)) {
238                 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
239                 set_rec_controllable (t->rec_enable_control ());
240                 recarm_button ().set_color (0xff0000ff);
241         } else {
242                 set_rec_controllable (boost::shared_ptr<AutomationControl>());
243                 recarm_button ().set_color (0xffffffff);
244                 recarm_button ().set_active (false);
245         }
246         _peak_meter = s->peak_meter ();
247         _redux_ctrl = s->comp_redux_controllable ();
248
249         set_select_controllable (boost::shared_ptr<AutomationControl>());
250         select_button ().set_active (s->is_selected ());
251         select_button ().set_color (s->presentation_info ().color());
252         //select_button ().set_blinking (false);
253
254         set_strip_mode (0x05);
255         set_text_line (0x00, s->name ());
256         set_text_line (0x01, _pan_ctrl ? _pan_ctrl->get_user_string () : "");
257         set_text_line (0x02, "");
258         set_text_line (0x03, "");
259 }
260
261 /* *****************************************************************************
262  * Parse Strip Specifig MIDI Events
263  */
264
265 bool
266 FP8Strip::midi_touch (bool t)
267 {
268         _touching = t;
269         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
270         if (!ac) {
271                 return false;
272         }
273         if (t) {
274                 if (!ac->touching ()) {
275                         ac->start_touch (ac->session().transport_frame());
276                 }
277         } else {
278                 ac->stop_touch (true, ac->session().transport_frame());
279         }
280         return true;
281 }
282
283 bool
284 FP8Strip::midi_fader (float val)
285 {
286         assert (val >= 0.f && val <= 1.f);
287         if (!_touching) {
288                 return false;
289         }
290         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
291         if (!ac) {
292                 return false;
293         }
294         if (ac->automation_state() == Touch && !ac->touching ()) {
295                 ac->start_touch (ac->session().transport_frame());
296         }
297         ac->set_value (ac->interface_to_internal (val), group_mode ());
298         return true;
299 }
300
301 /* *****************************************************************************
302  * Actions from Controller, Update Model
303  */
304
305 PBD::Controllable::GroupControlDisposition
306 FP8Strip::group_mode () const
307 {
308         if (_base.shift_mod ()) {
309                 return PBD::Controllable::InverseGroup;
310         } else {
311                 return PBD::Controllable::UseGroup;
312         }
313 }
314
315 void
316 FP8Strip::set_mute (bool on)
317 {
318         if (_mute_ctrl) {
319                 if (_mute_ctrl->automation_state() == Touch && !_mute_ctrl->touching ()) {
320                         _mute_ctrl->start_touch (_mute_ctrl->session().transport_frame());
321                 }
322                 _mute_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
323         }
324 }
325
326 void
327 FP8Strip::set_solo (bool on)
328 {
329         if (_solo_ctrl) {
330                 if (_solo_ctrl->automation_state() == Touch && !_solo_ctrl->touching ()) {
331                         _solo_ctrl->start_touch (_solo_ctrl->session().transport_frame());
332                 }
333                 _solo_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
334         }
335 }
336
337 void
338 FP8Strip::set_recarm ()
339 {
340         if (_rec_ctrl) {
341                 const bool on = !recarm_button ().is_active();
342                 _rec_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
343         }
344 }
345
346 void
347 FP8Strip::set_select ()
348 {
349         if (!_select_plugin_functor.empty ()) {
350                 assert (!_x_select_ctrl);
351                 _select_plugin_functor ();
352         } else if (_x_select_ctrl) {
353                 if (_x_select_ctrl->automation_state() == Touch && !_x_select_ctrl->touching ()) {
354                         _x_select_ctrl->start_touch (_x_select_ctrl->session().transport_frame());
355                 }
356                 const bool on = !select_button ().is_active();
357                 _x_select_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
358         }
359 }
360
361 /* *****************************************************************************
362  * Callbacks from Stripable, Update View
363  */
364
365 void
366 FP8Strip::notify_fader_changed ()
367 {
368         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
369         if (_touching) {
370                 return;
371         }
372         float val = 0;
373         if (ac) {
374                 val = ac->internal_to_interface (ac->get_value());
375                 val = std::max (0.f, std::min (1.f, val)) * 16368.f; /* 16 * 1023 */
376         }
377         unsigned short mv = lrintf (val);
378         if (mv == _last_fader) {
379                 return;
380         }
381         _last_fader = mv;
382         _base.tx_midi3 (0xe0 + _id, (mv & 0x7f), (mv >> 7) & 0x7f);
383 }
384
385 void
386 FP8Strip::notify_solo_changed ()
387 {
388         if (_solo_ctrl) {
389                 _solo.set_active (_solo_ctrl->get_value () > 0);
390         } else {
391                 _solo.set_active (false);
392         }
393 }
394
395 void
396 FP8Strip::notify_mute_changed ()
397 {
398         if (_mute_ctrl) {
399                 _mute.set_active (_mute_ctrl->get_value () > 0);
400         } else {
401                 _mute.set_active (false);
402         }
403 }
404
405 void
406 FP8Strip::notify_rec_changed ()
407 {
408         if (_rec_ctrl) {
409                 recarm_button ().set_active (_rec_ctrl->get_value() > 0.);
410         } else {
411                 recarm_button ().set_active (false);
412         }
413 }
414
415 void
416 FP8Strip::notify_pan_changed ()
417 {
418         // display only
419 }
420
421 void
422 FP8Strip::notify_x_select_changed ()
423 {
424         if (!_select_plugin_functor.empty ()) {
425                 assert (!_x_select_ctrl);
426                 return;
427         }
428
429         if (_x_select_ctrl) {
430                 assert (_select_plugin_functor.empty ());
431                 select_button ().set_active (_x_select_ctrl->get_value() > 0.);
432                 select_button ().set_color (0xffff00ff);
433                 select_button ().set_blinking (false);
434         } else {
435                 ; // leave alone.
436         }
437 }
438
439 /* *****************************************************************************
440  * Periodic View Updates 
441  */
442
443 void
444 FP8Strip::periodic_update_fader ()
445 {
446         boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
447         if (!ac || _touching) {
448                 return;
449         }
450
451         ARDOUR::AutoState state = ac->automation_state();
452         if (state == Touch || state == Play) {
453                 notify_fader_changed ();
454         }
455 }
456
457 void
458 FP8Strip::set_periodic_display_mode (DisplayMode m) {
459         _displaymode = m;
460         if (_displaymode == SendDisplay || _displaymode == PluginParam) {
461                 // need to change to 4 lines before calling set_text()
462                 set_strip_mode (2); // 4 lines of small text
463         }
464 }
465
466 void
467 FP8Strip::periodic_update_meter ()
468 {
469         bool have_meter = false;
470         bool have_panner = false;
471
472         if (_peak_meter) {
473                 have_meter = true;
474                 float dB = _peak_meter->meter_level (0, MeterMCP);
475                 // TODO: deflect meter
476                 int val = std::min (127.f, std::max (0.f, 2.f * dB + 127.f));
477                 if (val != _last_meter || val > 0) {
478                         _base.tx_midi2 (0xd0 + _id, val & 0x7f); // falls off automatically
479                         _last_meter = val;
480                 }
481
482         } else {
483                 if (0 != _last_meter) {
484                         _base.tx_midi2 (0xd0 + _id, 0);
485                         _last_meter = 0;
486                 }
487         }
488
489         // show redux only if there's a meter, too  (strip display mode 5)
490         if (_peak_meter && _redux_ctrl) {
491                 float rx = (1.f - _redux_ctrl->get_parameter ()) * 127.f;
492                 // TODO: deflect redux
493                 int val = std::min (127.f, std::max (0.f, rx));
494                 if (val != _last_redux) {
495                         _base.tx_midi2 (0xd8 + _id, val & 0x7f);
496                         _last_redux = val;
497                 }
498         } else {
499                 if (0 != _last_redux) {
500                         _base.tx_midi2 (0xd8 + _id, 0);
501                         _last_redux = 0;
502                 }
503         }
504
505         if (_displaymode == PluginParam) {
506                 if (_fader_ctrl) {
507                         set_bar_mode (2); // Fill
508                         set_text_line (0x01, value_as_string(_fader_ctrl->desc(), _fader_ctrl->get_value()));
509                         float barpos = _fader_ctrl->internal_to_interface (_fader_ctrl->get_value());
510                         int val = std::min (127.f, std::max (0.f, barpos * 128.f));
511                         if (val != _last_barpos) {
512                                 _base.tx_midi3 (0xb0, 0x30 + _id, val & 0x7f);
513                                 _last_barpos = val;
514                         }
515                 } else {
516                         set_bar_mode (4); // Off
517                         set_text_line (0x01, "");
518                 }
519         }
520         else if (_displaymode == SendDisplay) {
521                 set_bar_mode (4); // Off
522                 if (_fader_ctrl) {
523                         set_text_line (0x01, value_as_string(_fader_ctrl->desc(), _fader_ctrl->get_value()));
524                 } else {
525                         set_text_line (0x01, "");
526                 }
527         } else if (_pan_ctrl) {
528                 have_panner = true;
529                 float panpos = _pan_ctrl->internal_to_interface (_pan_ctrl->get_value());
530                 int val = std::min (127.f, std::max (0.f, panpos * 128.f));
531                 set_bar_mode (1); // Bipolar
532                 if (val != _last_barpos) {
533                         _base.tx_midi3 (0xb0, 0x30 + _id, val & 0x7f);
534                         _last_barpos = val;
535                 }
536                 set_text_line (0x01, _pan_ctrl->get_user_string ());
537         } else {
538                 set_bar_mode (4); // Off
539         }
540
541         if (_displaymode == SendDisplay || _displaymode == PluginParam) {
542                 set_strip_mode (2); // 4 lines of small text + value-bar
543         }
544         else if (have_meter && have_panner) {
545                 set_strip_mode (5); // small meters + 3 lines of text (3rd is large)  + value-bar
546         }
547         else if (have_meter) {
548                 set_strip_mode (4); // big meters + 3 lines of text (3rd line is large)
549         }
550         else if (have_panner) {
551                 set_strip_mode (0); // 3 lines of text (3rd line is large) + value-bar
552         } else {
553                 set_strip_mode (0); // 3 lines of text (3rd line is large) + value-bar
554         }
555 }
556
557 void
558 FP8Strip::set_strip_mode (uint8_t strip_mode, bool clear)
559 {
560         if (strip_mode == _strip_mode && !clear) {
561                 return;
562         }
563         _strip_mode = strip_mode;
564         _base.tx_sysex (3, 0x13, _id, (_strip_mode & 0x07) | (clear ? 0x10 : 0));
565         //_base.tx_midi3 (0xb0, 0x38 + _id, _bar_mode);
566 }
567
568 void
569 FP8Strip::set_bar_mode (uint8_t bar_mode)
570 {
571         if (bar_mode == _bar_mode) {
572                 return;
573         }
574         _bar_mode = bar_mode;
575         _base.tx_midi3 (0xb0, 0x38 + _id, bar_mode);
576 }
577
578 void
579 FP8Strip::set_text_line (uint8_t line, std::string const& txt, bool inv)
580 {
581         assert (line < 4);
582         if (_last_line[line] == txt) {
583                 return;
584         }
585         _base.tx_text (_id, line, inv ? 0x04 : 0x00, txt);
586         _last_line[line] = txt;
587 }
588
589 void
590 FP8Strip::periodic_update_timecode ()
591 {
592         if (_id >= 2 && _id < 6) {
593                 std::string const& tc = _base.timecode();
594                 //" HH:MM:SS:FF"
595                 std::string t;
596                 if (tc.size () == 12) {
597                         t = tc.substr (1 + (_id - 2) * 3, 2);
598                 }
599                 set_text_line (0x02, t);
600         }
601 }
602
603 void
604 FP8Strip::periodic ()
605 {
606         periodic_update_fader ();
607         periodic_update_meter ();
608         if (_displaymode != PluginSelect) {
609                 periodic_update_timecode ();
610         }
611 }