a slew of unconnected mostly minor tweaks to get Push2 support back to where it was...
[ardour.git] / libs / surfaces / push2 / mix.cc
1 /*
2   Copyright (C) 2016 Paul Davis
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (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., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <cairomm/region.h>
20 #include <pangomm/layout.h>
21
22 #include "pbd/compose.h"
23 #include "pbd/convert.h"
24 #include "pbd/debug.h"
25 #include "pbd/failed_constructor.h"
26 #include "pbd/file_utils.h"
27 #include "pbd/search_path.h"
28 #include "pbd/enumwriter.h"
29
30 #include "midi++/parser.h"
31 #include "timecode/time.h"
32 #include "timecode/bbt_time.h"
33
34 #include "ardour/async_midi_port.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/debug.h"
37 #include "ardour/filesystem_paths.h"
38 #include "ardour/midiport_manager.h"
39 #include "ardour/midi_track.h"
40 #include "ardour/midi_port.h"
41 #include "ardour/session.h"
42 #include "ardour/tempo.h"
43 #include "ardour/vca_manager.h"
44
45 #include "canvas/colors.h"
46 #include "canvas/rectangle.h"
47
48 #include "gtkmm2ext/gui_thread.h"
49
50 #include "canvas.h"
51 #include "mix.h"
52 #include "knob.h"
53 #include "push2.h"
54 #include "utils.h"
55
56 #include "pbd/i18n.h"
57
58 using namespace ARDOUR;
59 using namespace std;
60 using namespace PBD;
61 using namespace Glib;
62 using namespace ArdourSurface;
63 using namespace ArdourCanvas;
64
65 MixLayout::MixLayout (Push2& p, Session& s)
66         : Push2Layout (p, s)
67         , bank_start (0)
68         , vpot_mode (Volume)
69 {
70         selection_bg = new Rectangle (this);
71         selection_bg->hide ();
72
73         Pango::FontDescription fd2 ("Sans 10");
74         for (int n = 0; n < 8; ++n) {
75
76                 /* background for text labels for knob function */
77
78                 Rectangle* r = new Rectangle (this);
79                 Coord x0 = 10 + (n*Push2Canvas::inter_button_spacing()) - 5;
80                 r->set (Rect (x0, 2, x0 + Push2Canvas::inter_button_spacing(), 2 + 21));
81                 backgrounds.push_back (r);
82
83                 /* text labels for knob function*/
84
85                 Text* t = new Text (this);
86                 t->set_font_description (fd2);
87                 t->set_color (p2.get_color (Push2::ParameterName));
88                 t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 5));
89
90                 string txt;
91                 switch (n) {
92                 case 0:
93                         txt = _("Volumes");
94                         break;
95                 case 1:
96                         txt = _("Pans");
97                         break;
98                 case 2:
99                         txt = _("Pan Widths");
100                         break;
101                 case 3:
102                         txt = _("A Sends");
103                         break;
104                 case 4:
105                         txt = _("B Sends");
106                         break;
107                 case 5:
108                         txt = _("C Sends");
109                         break;
110                 case 6:
111                         txt = _("D Sends");
112                         break;
113                 case 7:
114                         txt = _("E Sends");
115                         break;
116                 }
117                 t->set (txt);
118                 upper_text.push_back (t);
119
120                 /* knobs */
121
122                 knobs[n] = new Push2Knob (p2, this);
123                 knobs[n]->set_position (Duple (60 + (n*Push2Canvas::inter_button_spacing()), 95));
124                 knobs[n]->set_radius (25);
125
126                 /* stripable names */
127
128                 t = new Text (this);
129                 t->set_font_description (fd2);
130                 t->set_color (p2.get_color (Push2::ParameterName));
131                 t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 140));
132                 lower_text.push_back (t);
133
134         }
135
136         mode_button = p2.button_by_id (Push2::Upper1);
137
138         session.RouteAdded.connect (session_connections, invalidator(*this), boost::bind (&MixLayout::stripables_added, this), &p2);
139         session.vca_manager().VCAAdded.connect (session_connections, invalidator (*this), boost::bind (&MixLayout::stripables_added, this), &p2);
140 }
141
142 MixLayout::~MixLayout ()
143 {
144         // Item destructor deletes all children
145 }
146
147 void
148 MixLayout::show ()
149 {
150         Container::show ();
151
152         mode_button->set_color (Push2::LED::White);
153         mode_button->set_state (Push2::LED::OneShot24th);
154         p2.write (mode_button->state_msg());
155
156         switch_bank (bank_start);
157 }
158
159 void
160 MixLayout::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
161 {
162         DEBUG_TRACE (DEBUG::Push2, string_compose ("mix render %1\n", area));
163
164         /* draw background */
165
166         set_source_rgb (context, p2.get_color (Push2::DarkBackground));
167         context->rectangle (0, 0, display_width(), display_height());
168         context->fill ();
169
170         /* draw line across top (below labels) */
171
172         context->move_to (0, 22.5);
173         context->line_to (display_width(), 22.5);
174         context->set_line_width (1.0);
175         context->stroke ();
176
177         /* show the kids ... */
178
179         render_children (area, context);
180 }
181
182 void
183 MixLayout::button_upper (uint32_t n)
184 {
185         Push2::Button* b;
186         switch (n) {
187         case 0:
188                 vpot_mode = Volume;
189                 b = p2.button_by_id (Push2::Upper1);
190                 break;
191         case 1:
192                 vpot_mode = PanAzimuth;
193                 b = p2.button_by_id (Push2::Upper2);
194                 break;
195         case 2:
196                 vpot_mode = PanWidth;
197                 b = p2.button_by_id (Push2::Upper3);
198                 break;
199         case 3:
200                 vpot_mode = Send1;
201                 b = p2.button_by_id (Push2::Upper4);
202                 break;
203         case 4:
204                 vpot_mode = Send2;
205                 b = p2.button_by_id (Push2::Upper5);
206                 break;
207         case 5:
208                 vpot_mode = Send3;
209                 b = p2.button_by_id (Push2::Upper6);
210                 break;
211         case 6:
212                 vpot_mode = Send4;
213                 b = p2.button_by_id (Push2::Upper7);
214                 break;
215         case 7:
216                 vpot_mode = Send5;
217                 b = p2.button_by_id (Push2::Upper8);
218                 break;
219         }
220
221         if (b != mode_button) {
222                 mode_button->set_color (Push2::LED::Black);
223                 mode_button->set_state (Push2::LED::OneShot24th);
224                 p2.write (mode_button->state_msg());
225         }
226
227         mode_button = b;
228
229         show_vpot_mode ();
230 }
231
232 void
233 MixLayout::show_vpot_mode ()
234 {
235         mode_button->set_color (Push2::LED::White);
236         mode_button->set_state (Push2::LED::OneShot24th);
237         p2.write (mode_button->state_msg());
238
239         for (int s = 0; s < 8; ++s) {
240                 backgrounds[s]->hide ();
241                 upper_text[s]->set_color (p2.get_color (Push2::ParameterName));
242         }
243
244         boost::shared_ptr<AutomationControl> ac;
245         switch (vpot_mode) {
246         case Volume:
247                 for (int s = 0; s < 8; ++s) {
248                         if (stripable[s]) {
249                                 knobs[s]->set_controllable (stripable[s]->gain_control());
250                         } else {
251                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
252                         }
253                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
254                 }
255                 backgrounds[0]->set_fill_color (p2.get_color (Push2::ParameterName));
256                 backgrounds[0]->show ();
257                 upper_text[0]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
258                 break;
259         case PanAzimuth:
260                 for (int s = 0; s < 8; ++s) {
261                         if (stripable[s]) {
262                                 knobs[s]->set_controllable (stripable[s]->pan_azimuth_control());
263                                 knobs[s]->add_flag (Push2Knob::ArcToZero);
264                         } else {
265                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
266
267                         }
268                 }
269                 backgrounds[1]->set_fill_color (p2.get_color (Push2::ParameterName));
270                 backgrounds[1]->show ();
271                 upper_text[1]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
272                 break;
273         case PanWidth:
274                 for (int s = 0; s < 8; ++s) {
275                         if (stripable[s]) {
276                                 knobs[s]->set_controllable (stripable[s]->pan_width_control());
277                         } else {
278                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
279
280                         }
281                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
282                 }
283                 backgrounds[2]->set_fill_color (p2.get_color (Push2::ParameterName));
284                 backgrounds[2]->show ();
285                 upper_text[2]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
286                 break;
287         case Send1:
288                 for (int s = 0; s < 8; ++s) {
289                         if (stripable[s]) {
290                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (0));
291                         } else {
292                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
293
294                         }
295                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
296                 }
297                 backgrounds[3]->set_fill_color (p2.get_color (Push2::ParameterName));
298                 backgrounds[3]->show ();
299                 upper_text[3]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
300                 break;
301         case Send2:
302                 for (int s = 0; s < 8; ++s) {
303                         if (stripable[s]) {
304                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (1));
305                         } else {
306                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
307
308                         }
309                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
310                 }
311                 backgrounds[4]->set_fill_color (p2.get_color (Push2::ParameterName));
312                 backgrounds[4]->show ();
313                 upper_text[4]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
314                 break;
315         case Send3:
316                 for (int s = 0; s < 8; ++s) {
317                         if (stripable[s]) {
318                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (2));
319                         } else {
320                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
321
322                         }
323                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
324                 }
325                 backgrounds[5]->set_fill_color (p2.get_color (Push2::ParameterName));
326                 backgrounds[5]->show ();
327                 upper_text[5]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
328                 break;
329         case Send4:
330                 for (int s = 0; s < 8; ++s) {
331                         if (stripable[s]) {
332                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (3));
333                         } else {
334                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
335
336                         }
337                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
338                 }
339                 backgrounds[6]->set_fill_color (p2.get_color (Push2::ParameterName));
340                 backgrounds[6]->show ();
341                 upper_text[6]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
342                 break;
343         case Send5:
344                 for (int s = 0; s < 8; ++s) {
345                         if (stripable[s]) {
346                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (4));
347                         } else {
348                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
349
350                         }
351                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
352                 }
353                 backgrounds[7]->set_fill_color (p2.get_color (Push2::ParameterName));
354                 backgrounds[7]->show ();
355                 upper_text[7]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
356                 break;
357         default:
358                 break;
359         }
360 }
361
362 void
363 MixLayout::button_mute ()
364 {
365         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
366         if (s) {
367                 boost::shared_ptr<AutomationControl> ac = s->mute_control();
368                 if (ac) {
369                         ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
370                 }
371         }
372 }
373
374 void
375 MixLayout::button_solo ()
376 {
377         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
378         if (s) {
379                 boost::shared_ptr<AutomationControl> ac = s->solo_control();
380                 if (ac) {
381                         ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
382                 }
383         }
384 }
385
386 void
387 MixLayout::button_lower (uint32_t n)
388 {
389         if (!stripable[n]) {
390                 return;
391         }
392
393         ControlProtocol::SetStripableSelection (stripable[n]);
394 }
395
396 void
397 MixLayout::strip_vpot (int n, int delta)
398 {
399         boost::shared_ptr<Controllable> ac = knobs[n]->controllable();
400
401         if (ac) {
402                 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
403         }
404 }
405
406 void
407 MixLayout::strip_vpot_touch (int n, bool touching)
408 {
409         if (stripable[n]) {
410                 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
411                 if (ac) {
412                         if (touching) {
413                                 ac->start_touch (session.audible_frame());
414                         } else {
415                                 ac->stop_touch (true, session.audible_frame());
416                         }
417                 }
418         }
419 }
420
421 void
422 MixLayout::stripable_property_change (PropertyChange const& what_changed, uint32_t which)
423 {
424         if (what_changed.contains (Properties::hidden)) {
425                 switch_bank (bank_start);
426         }
427
428         if (what_changed.contains (Properties::selected)) {
429
430                 if (!stripable[which]) {
431                         return;
432                 }
433
434                 if (stripable[which]->presentation_info().selected()) {
435                         show_selection (which);
436                 } else {
437                         hide_selection (which);
438                 }
439         }
440
441 }
442
443 void
444 MixLayout::show_selection (uint32_t n)
445 {
446         selection_bg->show ();
447         selection_bg->set_fill_color (stripable[n]->presentation_info().color());
448         const Coord x0 = 10  + (n * Push2Canvas::inter_button_spacing()) - 5;
449         selection_bg->set (Rect (x0, 137, x0 + Push2Canvas::inter_button_spacing(), 137 + 21));
450         lower_text[n]->set_color (ArdourCanvas::contrasting_text_color (selection_bg->fill_color()));
451 }
452
453 void
454 MixLayout::hide_selection (uint32_t n)
455 {
456         selection_bg->hide ();
457         if (stripable[n]) {
458                 lower_text[n]->set_color (stripable[n]->presentation_info().color());
459         }
460 }
461
462 void
463 MixLayout::solo_changed (uint32_t n)
464 {
465         solo_mute_changed (n);
466 }
467
468 void
469 MixLayout::mute_changed (uint32_t n)
470 {
471         solo_mute_changed (n);
472 }
473
474 void
475 MixLayout::solo_mute_changed (uint32_t n)
476 {
477         string shortname = short_version (stripable[n]->name(), 10);
478         string text;
479         boost::shared_ptr<AutomationControl> ac;
480         ac = stripable[n]->solo_control();
481         if (ac && ac->get_value()) {
482                 text += "* ";
483         }
484         boost::shared_ptr<MuteControl> mc;
485         mc = stripable[n]->mute_control ();
486         if (mc) {
487                 if (mc->muted_by_self_or_masters()) {
488                         text += "! ";
489                 } else if (mc->muted_by_others_soloing()) {
490                         text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
491                 }
492         }
493         text += shortname;
494         lower_text[n]->set (text);
495 }
496
497 void
498 MixLayout::switch_bank (uint32_t base)
499 {
500         stripable_connections.drop_connections ();
501
502         /* work backwards so we can tell if we should actually switch banks */
503
504         boost::shared_ptr<Stripable> s[8];
505         uint32_t different = 0;
506
507         for (int n = 0; n < 8; ++n) {
508                 s[n] = session.get_remote_nth_stripable (base+n, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
509                 if (s[n] != stripable[n]) {
510                         different++;
511                 }
512         }
513
514         if (!different) {
515                 /* some missing strips; new bank the same or more empty stripables than the old one, do
516                    nothing since we had already reached the end.
517                 */
518                 return;
519         }
520
521         if (!s[0]) {
522                 /* not even the first stripable exists, do nothing */
523                 return;
524         }
525
526         for (int n = 0; n < 8; ++n) {
527                 stripable[n] = s[n];
528         }
529
530         /* at least one stripable in this bank */
531
532         bank_start = base;
533
534         for (int n = 0; n < 8; ++n) {
535                 if (!stripable[n]) {
536                         lower_text[n]->hide ();
537                         hide_selection (n);
538                         continue;
539                 }
540
541                 lower_text[n]->show ();
542
543                 /* stripable goes away? refill the bank, starting at the same point */
544
545                 stripable[n]->DropReferences.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
546                 stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
547                 stripable[n]->solo_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::solo_changed, this, n), &p2);
548                 stripable[n]->mute_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::mute_changed, this, n), &p2);
549
550                 if (stripable[n]->presentation_info().selected()) {
551                         show_selection (n);
552                 } else {
553                         hide_selection (n);
554                 }
555
556                 /* this will set lower text to the correct value (basically
557                    the stripable name)
558                 */
559
560                 solo_mute_changed (n);
561
562                 Push2::Button* b;
563
564                 switch (n) {
565                 case 0:
566                         b = p2.button_by_id (Push2::Lower1);
567                         break;
568                 case 1:
569                         b = p2.button_by_id (Push2::Lower2);
570                         break;
571                 case 2:
572                         b = p2.button_by_id (Push2::Lower3);
573                         break;
574                 case 3:
575                         b = p2.button_by_id (Push2::Lower4);
576                         break;
577                 case 4:
578                         b = p2.button_by_id (Push2::Lower5);
579                         break;
580                 case 5:
581                         b = p2.button_by_id (Push2::Lower6);
582                         break;
583                 case 6:
584                         b = p2.button_by_id (Push2::Lower7);
585                         break;
586                 case 7:
587                         b = p2.button_by_id (Push2::Lower8);
588                         break;
589                 }
590
591                 b->set_color (p2.get_color_index (stripable[n]->presentation_info().color()));
592                 b->set_state (Push2::LED::OneShot24th);
593                 p2.write (b->state_msg());
594
595                 knobs[n]->set_text_color (stripable[n]->presentation_info().color());
596                 knobs[n]->set_arc_start_color (stripable[n]->presentation_info().color());
597                 knobs[n]->set_arc_end_color (stripable[n]->presentation_info().color());
598         }
599
600         show_vpot_mode ();
601 }
602
603 void
604 MixLayout::button_right ()
605 {
606         switch_bank (max (0, bank_start + 8));
607 }
608
609 void
610 MixLayout::button_left ()
611 {
612         switch_bank (max (0, bank_start - 8));
613 }
614
615
616 void
617 MixLayout::button_select_press ()
618 {
619 }
620
621 void
622 MixLayout::button_select_release ()
623 {
624         if (!(p2.modifier_state() & Push2::ModSelect)) {
625                 /* somebody else used us as a modifier */
626                 return;
627         }
628
629         int selected = -1;
630
631         for (int n = 0; n < 8; ++n) {
632                 if (stripable[n]) {
633                         if (stripable[n]->presentation_info().selected()) {
634                                         selected = n;
635                                         break;
636                         }
637                 }
638         }
639
640         if (selected < 0) {
641
642                 /* no visible track selected, select first (if any) */
643
644                 if (stripable[0]) {
645                         ControlProtocol::SetStripableSelection (stripable[0]);
646                 }
647
648         } else {
649
650                 if (p2.modifier_state() & Push2::ModShift) {
651                         std::cerr << "select prev\n";
652                         /* select prev */
653
654                         if (selected == 0) {
655                                 /* current selected is leftmost ... cancel selection,
656                                    switch banks by one, and select leftmost
657                                 */
658                                 if (bank_start != 0) {
659                                         ControlProtocol::ClearStripableSelection ();
660                                         switch_bank (bank_start-1);
661                                         if (stripable[0]) {
662                                                 ControlProtocol::SetStripableSelection (stripable[0]);
663                                         }
664                                 }
665                         } else {
666                                 /* select prev, if any */
667                                 int n = selected - 1;
668                                 while (n >= 0 && !stripable[n]) {
669                                         --n;
670                                 }
671                                 if (n >= 0) {
672                                         ControlProtocol::SetStripableSelection (stripable[n]);
673                                 }
674                         }
675
676                 } else {
677
678                         std::cerr << "select next\n";
679                         /* select next */
680
681                         if (selected == 7) {
682                                 /* current selected is rightmost ... cancel selection,
683                                    switch banks by one, and select righmost
684                                 */
685                                 ControlProtocol::ToggleStripableSelection (stripable[selected]);
686                                 switch_bank (bank_start+1);
687                                 if (stripable[7]) {
688                                         ControlProtocol::SetStripableSelection (stripable[7]);
689                                 }
690                         } else {
691                                 /* select next, if any */
692                                 int n = selected + 1;
693                                 while (n < 8 && !stripable[n]) {
694                                         ++n;
695                                 }
696
697                                 if (n != 8) {
698                                         ControlProtocol::SetStripableSelection (stripable[n]);
699                                 }
700                         }
701                 }
702         }
703 }
704
705 void
706 MixLayout::stripables_added ()
707 {
708         /* reload current bank */
709         switch_bank (bank_start);
710 }