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