LCXL: fix off by one cause by fader8master
[ardour.git] / libs / surfaces / push2 / track_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
32 #include "temporal/time.h"
33 #include "temporal/bbt_time.h"
34
35 #include "ardour/async_midi_port.h"
36 #include "ardour/audioengine.h"
37 #include "ardour/debug.h"
38 #include "ardour/dsp_filter.h"
39 #include "ardour/filesystem_paths.h"
40 #include "ardour/midiport_manager.h"
41 #include "ardour/midi_track.h"
42 #include "ardour/midi_port.h"
43 #include "ardour/monitor_control.h"
44 #include "ardour/meter.h"
45 #include "ardour/session.h"
46 #include "ardour/solo_isolate_control.h"
47 #include "ardour/solo_safe_control.h"
48 #include "ardour/tempo.h"
49
50 #include "gtkmm2ext/gui_thread.h"
51 #include "gtkmm2ext/rgb_macros.h"
52
53 #include "canvas/box.h"
54 #include "canvas/line.h"
55 #include "canvas/meter.h"
56 #include "canvas/rectangle.h"
57 #include "canvas/text.h"
58 #include "canvas/types.h"
59
60 #include "canvas.h"
61 #include "knob.h"
62 #include "level_meter.h"
63 #include "menu.h"
64 #include "push2.h"
65 #include "track_mix.h"
66 #include "utils.h"
67
68 #include "pbd/i18n.h"
69
70 #ifdef __APPLE__
71 #define Rect ArdourCanvas::Rect
72 #endif
73
74 using namespace ARDOUR;
75 using namespace std;
76 using namespace PBD;
77 using namespace Glib;
78 using namespace ArdourSurface;
79 using namespace ArdourCanvas;
80
81 TrackMixLayout::TrackMixLayout (Push2& p, Session & s, std::string const & name)
82         : Push2Layout (p, s, name)
83 {
84         Pango::FontDescription fd ("Sans 10");
85
86         bg = new ArdourCanvas::Rectangle (this);
87         bg->set (Rect (0, 0, display_width(), display_height()));
88         bg->set_fill_color (p2.get_color (Push2::DarkBackground));
89
90         upper_line = new Line (this);
91         upper_line->set (Duple (0, 22.5), Duple (display_width(), 22.5));
92         upper_line->set_outline_color (p2.get_color (Push2::LightBackground));
93
94         for (int n = 0; n < 8; ++n) {
95                 Text* t;
96
97                 if (n < 4) {
98                         t = new Text (this);
99                         t->set_font_description (fd);
100                         t->set_color (p2.get_color (Push2::ParameterName));
101                         t->set_position ( Duple (10 + (n*Push2Canvas::inter_button_spacing()), 2));
102                         upper_text.push_back (t);
103                 }
104
105                 t = new Text (this);
106                 t->set_font_description (fd);
107                 t->set_color (p2.get_color (Push2::ParameterName));
108                 t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 140));
109
110                 lower_text.push_back (t);
111
112                 switch (n) {
113                 case 0:
114                         upper_text[n]->set (_("Track Volume"));
115                         lower_text[n]->set (_("Mute"));
116                         break;
117                 case 1:
118                         upper_text[n]->set (_("Track Pan"));
119                         lower_text[n]->set (_("Solo"));
120                         break;
121                 case 2:
122                         upper_text[n]->set (_("Track Width"));
123                         lower_text[n]->set (_("Rec-enable"));
124                         break;
125                 case 3:
126                         upper_text[n]->set (_("Track Trim"));
127                         lower_text[n]->set (_("In"));
128                         break;
129                 case 4:
130                         lower_text[n]->set (_("Disk"));
131                         break;
132                 case 5:
133                         lower_text[n]->set (_("Solo Iso"));
134                         break;
135                 case 6:
136                         lower_text[n]->set (_("Solo Lock"));
137                         break;
138                 case 7:
139                         lower_text[n]->set (_(""));
140                         break;
141                 }
142
143                 knobs[n] = new Push2Knob (p2, this);
144                 knobs[n]->set_position (Duple (60 + (Push2Canvas::inter_button_spacing()*n), 95));
145                 knobs[n]->set_radius (25);
146         }
147
148         name_text = new Text (this);
149         name_text->set_font_description (fd);
150         name_text->set_position (Duple (10 + (4*Push2Canvas::inter_button_spacing()), 2));
151
152         meter = new LevelMeter (p2, this, 300, ArdourCanvas::Meter::Horizontal);
153         meter->set_position (Duple (10 + (4 * Push2Canvas::inter_button_spacing()), 30));
154
155         Pango::FontDescription fd2 ("Sans 18");
156         bbt_text = new Text (this);
157         bbt_text->set_font_description (fd2);
158         bbt_text->set_color (p2.get_color (Push2::LightBackground));
159         bbt_text->set_position (Duple (10 + (4 * Push2Canvas::inter_button_spacing()), 60));
160
161         minsec_text = new Text (this);
162         minsec_text->set_font_description (fd2);
163         minsec_text->set_color (p2.get_color (Push2::LightBackground));
164         minsec_text->set_position (Duple (10 + (4 * Push2Canvas::inter_button_spacing()), 90));
165 }
166
167 TrackMixLayout::~TrackMixLayout ()
168 {
169         for (int n = 0; n < 8; ++n) {
170                 delete knobs[n];
171         }
172 }
173
174 void
175 TrackMixLayout::show ()
176 {
177         Push2::ButtonID lower_buttons[] = { Push2::Lower1, Push2::Lower2, Push2::Lower3, Push2::Lower4,
178                                             Push2::Lower5, Push2::Lower6, Push2::Lower7, Push2::Lower8 };
179
180         for (size_t n = 0; n < sizeof (lower_buttons) / sizeof (lower_buttons[0]); ++n) {
181                 boost::shared_ptr<Push2::Button> b = p2.button_by_id (lower_buttons[n]);
182                 b->set_color (Push2::LED::DarkGray);
183                 b->set_state (Push2::LED::OneShot24th);
184                 p2.write (b->state_msg());
185         }
186
187         show_state ();
188
189         Container::show ();
190 }
191
192 void
193 TrackMixLayout::hide ()
194 {
195
196 }
197
198 void
199 TrackMixLayout::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
200 {
201         Container::render (area, context);
202 }
203
204 void
205 TrackMixLayout::button_upper (uint32_t n)
206 {
207 }
208
209 void
210 TrackMixLayout::button_lower (uint32_t n)
211 {
212         if (!stripable) {
213                 return;
214         }
215
216         MonitorChoice mc;
217
218         switch (n) {
219         case 0:
220                 if (stripable->mute_control()) {
221                         stripable->mute_control()->set_value (!stripable->mute_control()->get_value(), PBD::Controllable::UseGroup);
222                 }
223                 break;
224         case 1:
225                 if (stripable->solo_control()) {
226                         stripable->solo_control()->set_value (!stripable->solo_control()->get_value(), PBD::Controllable::UseGroup);
227                 }
228                 break;
229         case 2:
230                 if (stripable->rec_enable_control()) {
231                         stripable->rec_enable_control()->set_value (!stripable->rec_enable_control()->get_value(), PBD::Controllable::UseGroup);
232                 }
233                 break;
234         case 3:
235                 if (stripable->monitor_control()) {
236                         mc = stripable->monitoring_control()->monitoring_choice();
237                         switch (mc) {
238                         case MonitorInput:
239                                 stripable->monitoring_control()->set_value (MonitorAuto, PBD::Controllable::UseGroup);
240                                 break;
241                         default:
242                                 stripable->monitoring_control()->set_value (MonitorInput, PBD::Controllable::UseGroup);
243                                 break;
244                         }
245                 }
246                 break;
247         case 4:
248                 mc = stripable->monitoring_control()->monitoring_choice();
249                 switch (mc) {
250                 case MonitorDisk:
251                         stripable->monitoring_control()->set_value (MonitorAuto, PBD::Controllable::UseGroup);
252                         break;
253                 default:
254                         stripable->monitoring_control()->set_value (MonitorDisk, PBD::Controllable::UseGroup);
255                         break;
256                 }
257                 break;
258         case 5:
259                 if (stripable->solo_isolate_control()) {
260                         stripable->solo_isolate_control()->set_value (!stripable->solo_isolate_control()->get_value(), PBD::Controllable::UseGroup);
261                 }
262                 break;
263         case 6:
264                 if (stripable->solo_safe_control()) {
265                         stripable->solo_safe_control()->set_value (!stripable->solo_safe_control()->get_value(), PBD::Controllable::UseGroup);
266                 }
267                 break;
268         case 7:
269                 /* nothing here */
270                 break;
271         }
272 }
273
274 void
275 TrackMixLayout::button_left ()
276 {
277         p2.access_action ("Editor/select-prev-route");
278 }
279
280 void
281 TrackMixLayout::button_right ()
282 {
283         p2.access_action ("Editor/select-next-route");
284 }
285
286 void
287 TrackMixLayout::simple_control_change (boost::shared_ptr<AutomationControl> ac, Push2::ButtonID bid)
288 {
289         if (!ac || !parent()) {
290                 return;
291         }
292
293         boost::shared_ptr<Push2::Button> b = p2.button_by_id (bid);
294
295         if (!b) {
296                 return;
297         }
298
299         if (ac->get_value()) {
300                 b->set_color (selection_color);
301         } else {
302                 b->set_color (Push2::LED::DarkGray);
303         }
304         b->set_state (Push2::LED::OneShot24th);
305         p2.write (b->state_msg());
306 }
307
308 void
309 TrackMixLayout::solo_mute_change ()
310 {
311         if (!stripable) {
312                 return;
313         }
314
315         boost::shared_ptr<Push2::Button> b = p2.button_by_id (Push2::Lower2);
316
317         if (b) {
318                 boost::shared_ptr<SoloControl> sc = stripable->solo_control();
319
320                 if (sc) {
321                         if (sc->soloed_by_self_or_masters()) {
322                                 b->set_color (selection_color);
323                                 b->set_state (Push2::LED::OneShot24th);
324                         } else if (sc->soloed_by_others_upstream() || sc->soloed_by_others_downstream()) {
325                                 b->set_color (selection_color);
326                                 b->set_state (Push2::LED::Blinking8th);
327                         } else {
328                                 b->set_color (Push2::LED::DarkGray);
329                                 b->set_state (Push2::LED::OneShot24th);
330                         }
331                 } else {
332                         b->set_color (Push2::LED::DarkGray);
333                         b->set_state (Push2::LED::OneShot24th);
334                 }
335
336                 p2.write (b->state_msg());
337         }
338
339         b = p2.button_by_id (Push2::Lower1);
340
341         if (b) {
342                 boost::shared_ptr<MuteControl> mc = stripable->mute_control();
343
344                 if (mc) {
345                         if (mc->muted_by_self_or_masters()) {
346                                 b->set_color (selection_color);
347                                 b->set_state (Push2::LED::OneShot24th);
348                         } else if (mc->muted_by_others_soloing()) {
349                                 b->set_color (selection_color);
350                                 b->set_state (Push2::LED::Blinking8th);
351                         } else {
352                                 b->set_color (Push2::LED::DarkGray);
353                                 b->set_state (Push2::LED::OneShot24th);
354                         }
355
356                 } else {
357                         b->set_color (Push2::LED::DarkGray);
358                         b->set_state (Push2::LED::OneShot24th);
359                 }
360
361                 p2.write (b->state_msg());
362         }
363
364 }
365
366 void
367 TrackMixLayout::rec_enable_change ()
368 {
369         if (!stripable) {
370                 return;
371         }
372
373         simple_control_change (stripable->rec_enable_control(), Push2::Lower3);
374 }
375
376 void
377 TrackMixLayout::solo_iso_change ()
378 {
379         if (!stripable) {
380                 return;
381         }
382
383         simple_control_change (stripable->solo_isolate_control(), Push2::Lower6);
384 }
385 void
386 TrackMixLayout::solo_safe_change ()
387 {
388         if (!stripable) {
389                 return;
390         }
391
392         simple_control_change (stripable->solo_safe_control(), Push2::Lower7);
393 }
394
395 void
396 TrackMixLayout::monitoring_change ()
397 {
398         if (!stripable) {
399                 return;
400         }
401
402         if (!stripable->monitoring_control()) {
403                 return;
404         }
405
406         boost::shared_ptr<Push2::Button> b1 = p2.button_by_id (Push2::Lower4);
407         boost::shared_ptr<Push2::Button> b2 = p2.button_by_id (Push2::Lower5);
408         uint8_t b1_color;
409         uint8_t b2_color;
410
411         MonitorChoice mc = stripable->monitoring_control()->monitoring_choice ();
412
413         switch (mc) {
414         case MonitorAuto:
415                 b1_color = Push2::LED::DarkGray;
416                 b2_color = Push2::LED::DarkGray;
417                 break;
418         case MonitorInput:
419                 b1_color = selection_color;
420                 b2_color = Push2::LED::DarkGray;
421                 break;
422         case MonitorDisk:
423                 b1_color = Push2::LED::DarkGray;
424                 b2_color = selection_color;
425                 break;
426         case MonitorCue:
427                 b1_color = selection_color;
428                 b2_color = selection_color;
429                 break;
430         }
431
432         b1->set_color (b1_color);
433         b1->set_state (Push2::LED::OneShot24th);
434         p2.write (b1->state_msg());
435
436         b2->set_color (b2_color);
437         b2->set_state (Push2::LED::OneShot24th);
438         p2.write (b2->state_msg());
439 }
440
441 void
442 TrackMixLayout::show_state ()
443 {
444         if (!parent()) {
445                 return;
446         }
447
448         if (stripable) {
449                 name_changed ();
450                 color_changed ();
451                 solo_mute_change ();
452                 rec_enable_change ();
453                 solo_iso_change ();
454                 solo_safe_change ();
455                 monitoring_change ();
456
457                 meter->set_meter (stripable->peak_meter ().get());
458         } else {
459                 meter->set_meter (0);
460         }
461 }
462
463 void
464 TrackMixLayout::set_stripable (boost::shared_ptr<Stripable> s)
465 {
466         stripable_connections.drop_connections ();
467
468         stripable = s;
469
470         if (stripable) {
471
472                 stripable->DropReferences.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::drop_stripable, this), &p2);
473
474                 stripable->PropertyChanged.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::stripable_property_change, this, _1), &p2);
475                 stripable->presentation_info().PropertyChanged.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::stripable_property_change, this, _1), &p2);
476
477                 stripable->solo_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::solo_mute_change, this), &p2);
478                 stripable->mute_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::solo_mute_change, this), &p2);
479                 stripable->solo_isolate_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::solo_iso_change, this), &p2);
480                 stripable->solo_safe_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::solo_safe_change, this), &p2);
481
482                 if (stripable->rec_enable_control()) {
483                         stripable->rec_enable_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::rec_enable_change, this), &p2);
484                 }
485
486                 if (stripable->monitoring_control()) {
487                         stripable->monitoring_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&TrackMixLayout::monitoring_change, this), &p2);
488                 }
489
490                 knobs[0]->set_controllable (stripable->gain_control());
491                 knobs[1]->set_controllable (stripable->pan_azimuth_control());
492                 knobs[1]->add_flag (Push2Knob::ArcToZero);
493                 knobs[2]->set_controllable (stripable->pan_width_control());
494                 knobs[3]->set_controllable (stripable->trim_control());
495                 knobs[3]->add_flag (Push2Knob::ArcToZero);
496                 knobs[4]->set_controllable (boost::shared_ptr<AutomationControl>());
497                 knobs[5]->set_controllable (boost::shared_ptr<AutomationControl>());
498                 knobs[6]->set_controllable (boost::shared_ptr<AutomationControl>());
499                 knobs[7]->set_controllable (boost::shared_ptr<AutomationControl>());
500         }
501
502         show_state ();
503 }
504
505 void
506 TrackMixLayout::drop_stripable ()
507 {
508         stripable_connections.drop_connections ();
509         stripable.reset ();
510 }
511
512 void
513 TrackMixLayout::name_changed ()
514 {
515         if (stripable) {
516
517                 name_text->set (stripable->name());
518
519                 /* right justify */
520
521                 Duple pos;
522                 pos.y = name_text->position().y;
523                 pos.x = display_width() - 10 - name_text->width();
524
525                 name_text->set_position (pos);
526         }
527 }
528
529 void
530 TrackMixLayout::color_changed ()
531 {
532         if (!parent()) {
533                 return;
534         }
535
536         Gtkmm2ext::Color rgba = stripable->presentation_info().color();
537         selection_color = p2.get_color_index (rgba);
538
539         name_text->set_color (rgba);
540
541         for (int n = 0; n < 8; ++n) {
542                 knobs[n]->set_text_color (rgba);
543                 knobs[n]->set_arc_start_color (rgba);
544                 knobs[n]->set_arc_end_color (rgba);
545         }
546 }
547
548 void
549 TrackMixLayout::stripable_property_change (PropertyChange const& what_changed)
550 {
551         if (what_changed.contains (Properties::color)) {
552                 color_changed ();
553         }
554         if (what_changed.contains (Properties::name)) {
555                 name_changed ();
556         }
557 }
558
559 void
560 TrackMixLayout::strip_vpot (int n, int delta)
561 {
562         boost::shared_ptr<Controllable> ac = knobs[n]->controllable();
563
564         if (ac) {
565                 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
566         }
567 }
568
569 void
570 TrackMixLayout::strip_vpot_touch (int n, bool touching)
571 {
572         boost::shared_ptr<AutomationControl> ac = knobs[n]->controllable();
573         if (ac) {
574                 if (touching) {
575                         ac->start_touch (session.audible_sample());
576                 } else {
577                         ac->stop_touch (session.audible_sample());
578                 }
579         }
580 }
581
582 void
583 TrackMixLayout::update_meters ()
584 {
585         if (!stripable) {
586                 return;
587         }
588
589         meter->update_meters ();
590 }
591
592 void
593 TrackMixLayout::update_clocks ()
594 {
595         samplepos_t pos = session.audible_sample();
596         bool negative = false;
597
598         if (pos < 0) {
599                 pos = -pos;
600                 negative = true;
601         }
602
603         char buf[16];
604         Timecode::BBT_Time BBT = session.tempo_map().bbt_at_sample (pos);
605
606 #define BBT_BAR_CHAR "|"
607
608         if (negative) {
609                 snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
610                           BBT.bars, BBT.beats, BBT.ticks);
611         } else {
612                 snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
613                           BBT.bars, BBT.beats, BBT.ticks);
614         }
615
616         bbt_text->set (buf);
617
618         samplecnt_t left;
619         int hrs;
620         int mins;
621         int secs;
622         int millisecs;
623
624         const double sample_rate = session.sample_rate ();
625
626         left = pos;
627         hrs = (int) floor (left / (sample_rate * 60.0f * 60.0f));
628         left -= (samplecnt_t) floor (hrs * sample_rate * 60.0f * 60.0f);
629         mins = (int) floor (left / (sample_rate * 60.0f));
630         left -= (samplecnt_t) floor (mins * sample_rate * 60.0f);
631         secs = (int) floor (left / (float) sample_rate);
632         left -= (samplecnt_t) floor ((double)(secs * sample_rate));
633         millisecs = floor (left * 1000.0 / (float) sample_rate);
634
635         if (negative) {
636                 snprintf (buf, sizeof (buf), "-%02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs);
637         } else {
638                 snprintf (buf, sizeof (buf), " %02" PRId32 ":%02" PRId32 ":%02" PRId32 ".%03" PRId32, hrs, mins, secs, millisecs);
639         }
640
641         minsec_text->set (buf);
642 }