cb6239728df90916e01103c8bf44fded81293eab
[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/utils.h"
44 #include "ardour/vca_manager.h"
45
46 #include "gtkmm2ext/colors.h"
47 #include "canvas/line.h"
48 #include "canvas/rectangle.h"
49 #include "canvas/text.h"
50
51 #include "gtkmm2ext/gui_thread.h"
52
53 #include "canvas.h"
54 #include "knob.h"
55 #include "level_meter.h"
56 #include "mix.h"
57 #include "push2.h"
58 #include "utils.h"
59
60 #include "pbd/i18n.h"
61
62 #ifdef __APPLE__
63 #define Rect ArdourCanvas::Rect
64 #endif
65
66 using namespace ARDOUR;
67 using namespace std;
68 using namespace PBD;
69 using namespace Glib;
70 using namespace ArdourSurface;
71 using namespace Gtkmm2ext;
72 using namespace ArdourCanvas;
73
74 MixLayout::MixLayout (Push2& p, Session & s, std::string const & name)
75         : Push2Layout (p, s, name)
76         , bank_start (0)
77         , vpot_mode (Volume)
78 {
79         /* background */
80
81         bg = new ArdourCanvas::Rectangle (this);
82         bg->set (Rect (0, 0, display_width(), display_height()));
83         bg->set_fill_color (p2.get_color (Push2::DarkBackground));
84
85         /* upper line */
86
87         upper_line = new Line (this);
88         upper_line->set (Duple (0, 22.5), Duple (display_width(), 22.5));
89         upper_line->set_outline_color (p2.get_color (Push2::LightBackground));
90
91         Pango::FontDescription fd2 ("Sans 10");
92
93         for (int n = 0; n < 8; ++n) {
94
95                 /* background for text labels for knob function */
96
97                 ArdourCanvas::Rectangle* r = new ArdourCanvas::Rectangle (this);
98                 Coord x0 = 10 + (n*Push2Canvas::inter_button_spacing()) - 5;
99                 r->set (Rect (x0, 2, x0 + Push2Canvas::inter_button_spacing(), 2 + 21));
100                 upper_backgrounds.push_back (r);
101
102                 r = new ArdourCanvas::Rectangle (this);
103                 r->set (Rect (x0, 137, x0 + Push2Canvas::inter_button_spacing(), 137 + 21));
104                 lower_backgrounds.push_back (r);
105
106                 /* text labels for knob function*/
107
108                 Text* t = new Text (this);
109                 t->set_font_description (fd2);
110                 t->set_color (p2.get_color (Push2::ParameterName));
111                 t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 5));
112
113                 string txt;
114                 switch (n) {
115                 case 0:
116                         txt = _("Volumes");
117                         break;
118                 case 1:
119                         txt = _("Pans");
120                         break;
121                 case 2:
122                         txt = _("Pan Widths");
123                         break;
124                 case 3:
125                         txt = _("A Sends");
126                         break;
127                 case 4:
128                         txt = _("B Sends");
129                         break;
130                 case 5:
131                         txt = _("C Sends");
132                         break;
133                 case 6:
134                         txt = _("D Sends");
135                         break;
136                 case 7:
137                         txt = _("E Sends");
138                         break;
139                 }
140                 t->set (txt);
141                 upper_text.push_back (t);
142
143                 /* GainMeters */
144
145                 gain_meter[n] = new GainMeter (this, p2);
146                 gain_meter[n]->set_position (Duple (40 + (n * Push2Canvas::inter_button_spacing()), 95));
147
148                 /* stripable names */
149
150                 t = new Text (this);
151                 t->set_font_description (fd2);
152                 t->set_color (p2.get_color (Push2::ParameterName));
153                 t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 140));
154                 lower_text.push_back (t);
155
156         }
157
158         mode_button = p2.button_by_id (Push2::Upper1);
159
160         session.RouteAdded.connect (session_connections, invalidator(*this), boost::bind (&MixLayout::stripables_added, this), &p2);
161         session.vca_manager().VCAAdded.connect (session_connections, invalidator (*this), boost::bind (&MixLayout::stripables_added, this), &p2);
162 }
163
164 MixLayout::~MixLayout ()
165 {
166         // Item destructor deletes all children
167 }
168
169 void
170 MixLayout::show ()
171 {
172         Push2::ButtonID upper_buttons[] = { Push2::Upper1, Push2::Upper2, Push2::Upper3, Push2::Upper4,
173                                             Push2::Upper5, Push2::Upper6, Push2::Upper7, Push2::Upper8 };
174
175
176         for (size_t n = 0; n < sizeof (upper_buttons) / sizeof (upper_buttons[0]); ++n) {
177                 Push2::Button* b = p2.button_by_id (upper_buttons[n]);
178
179                 if (b != mode_button) {
180                         b->set_color (Push2::LED::DarkGray);
181                 } else {
182                         b->set_color (Push2::LED::White);
183                 }
184                 b->set_state (Push2::LED::OneShot24th);
185                 p2.write (b->state_msg());
186         }
187
188         switch_bank (bank_start);
189
190         Container::show ();
191 }
192
193 void
194 MixLayout::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
195 {
196         Container::render (area, context);
197 }
198
199 void
200 MixLayout::button_upper (uint32_t n)
201 {
202         Push2::Button* b;
203         switch (n) {
204         case 0:
205                 vpot_mode = Volume;
206                 b = p2.button_by_id (Push2::Upper1);
207                 break;
208         case 1:
209                 vpot_mode = PanAzimuth;
210                 b = p2.button_by_id (Push2::Upper2);
211                 break;
212         case 2:
213                 vpot_mode = PanWidth;
214                 b = p2.button_by_id (Push2::Upper3);
215                 break;
216         case 3:
217                 vpot_mode = Send1;
218                 b = p2.button_by_id (Push2::Upper4);
219                 break;
220         case 4:
221                 vpot_mode = Send2;
222                 b = p2.button_by_id (Push2::Upper5);
223                 break;
224         case 5:
225                 vpot_mode = Send3;
226                 b = p2.button_by_id (Push2::Upper6);
227                 break;
228         case 6:
229                 vpot_mode = Send4;
230                 b = p2.button_by_id (Push2::Upper7);
231                 break;
232         case 7:
233                 vpot_mode = Send5;
234                 b = p2.button_by_id (Push2::Upper8);
235                 break;
236         }
237
238         if (b != mode_button) {
239                 mode_button->set_color (Push2::LED::Black);
240                 mode_button->set_state (Push2::LED::OneShot24th);
241                 p2.write (mode_button->state_msg());
242         }
243
244         mode_button = b;
245
246         show_vpot_mode ();
247 }
248
249 void
250 MixLayout::show_vpot_mode ()
251 {
252         mode_button->set_color (Push2::LED::White);
253         mode_button->set_state (Push2::LED::OneShot24th);
254         p2.write (mode_button->state_msg());
255
256         for (int s = 0; s < 8; ++s) {
257                 upper_backgrounds[s]->hide ();
258                 upper_text[s]->set_color (p2.get_color (Push2::ParameterName));
259         }
260
261         uint32_t n = 0;
262
263         boost::shared_ptr<AutomationControl> ac;
264         switch (vpot_mode) {
265         case Volume:
266                 for (int s = 0; s < 8; ++s) {
267                         if (stripable[s]) {
268                                 gain_meter[s]->knob->set_controllable (stripable[s]->gain_control());
269                                 boost::shared_ptr<PeakMeter> pm = stripable[s]->peak_meter(); 
270                                 if (pm) {
271                                         gain_meter[s]->meter->set_meter (pm.get());
272                                 } else {
273                                         gain_meter[s]->meter->set_meter (0);
274                                 }
275                         } else {
276                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
277                                 gain_meter[s]->meter->set_meter (0);
278                         }
279                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
280                         gain_meter[s]->meter->show ();
281                 }
282                 n = 0;
283                 break;
284         case PanAzimuth:
285                 for (int s = 0; s < 8; ++s) {
286                         if (stripable[s]) {
287                                 gain_meter[s]->knob->set_controllable (stripable[s]->pan_azimuth_control());
288                                 gain_meter[s]->knob->add_flag (Push2Knob::ArcToZero);
289                         } else {
290                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
291
292                         }
293                         gain_meter[s]->meter->hide ();
294                 }
295                 n = 1;
296                 break;
297         case PanWidth:
298                 for (int s = 0; s < 8; ++s) {
299                         if (stripable[s]) {
300                                 gain_meter[s]->knob->set_controllable (stripable[s]->pan_width_control());
301                         } else {
302                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
303
304                         }
305                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
306                         gain_meter[s]->meter->hide ();
307                 }
308                 n = 2;
309                 break;
310         case Send1:
311                 for (int s = 0; s < 8; ++s) {
312                         if (stripable[s]) {
313                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (0));
314                         } else {
315                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
316
317                         }
318                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
319                         gain_meter[s]->meter->hide ();
320                 }
321                 n = 3;
322                 break;
323         case Send2:
324                 for (int s = 0; s < 8; ++s) {
325                         if (stripable[s]) {
326                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (1));
327                         } else {
328                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
329
330                         }
331                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
332                         gain_meter[s]->meter->hide ();
333                 }
334                 n = 4;
335                 break;
336         case Send3:
337                 for (int s = 0; s < 8; ++s) {
338                         if (stripable[s]) {
339                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (2));
340                         } else {
341                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
342
343                         }
344                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
345                         gain_meter[s]->meter->hide ();
346                 }
347                 n = 5;
348                 break;
349         case Send4:
350                 for (int s = 0; s < 8; ++s) {
351                         if (stripable[s]) {
352                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (3));
353                         } else {
354                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
355
356                         }
357                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
358                         gain_meter[s]->meter->hide ();
359                 }
360                 n = 6;
361                 break;
362         case Send5:
363                 for (int s = 0; s < 8; ++s) {
364                         if (stripable[s]) {
365                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (4));
366                         } else {
367                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
368
369                         }
370                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
371                         gain_meter[s]->meter->hide ();
372                 }
373                 n = 7;
374                 break;
375         default:
376                 break;
377         }
378
379         upper_backgrounds[n]->set_fill_color (p2.get_color (Push2::ParameterName));
380         upper_backgrounds[n]->set_outline_color (p2.get_color (Push2::ParameterName));
381         upper_backgrounds[n]->show ();
382         upper_text[n]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
383 }
384
385 void
386 MixLayout::button_mute ()
387 {
388         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
389         if (s) {
390                 boost::shared_ptr<AutomationControl> ac = s->mute_control();
391                 if (ac) {
392                         ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
393                 }
394         }
395 }
396
397 void
398 MixLayout::button_solo ()
399 {
400         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
401         if (s) {
402                 boost::shared_ptr<AutomationControl> ac = s->solo_control();
403                 if (ac) {
404                         session.set_control (ac, !ac->get_value(), PBD::Controllable::UseGroup);
405                 }
406         }
407 }
408
409 void
410 MixLayout::button_lower (uint32_t n)
411 {
412         if (!stripable[n]) {
413                 return;
414         }
415
416         ControlProtocol::SetStripableSelection (stripable[n]);
417 }
418
419 void
420 MixLayout::strip_vpot (int n, int delta)
421 {
422         boost::shared_ptr<Controllable> ac = gain_meter[n]->knob->controllable();
423
424         if (ac) {
425                 ac->set_value (ac->interface_to_internal (
426                                        min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
427                                PBD::Controllable::UseGroup);
428         }
429 }
430
431 void
432 MixLayout::strip_vpot_touch (int n, bool touching)
433 {
434         if (stripable[n]) {
435                 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
436                 if (ac) {
437                         if (touching) {
438                                 ac->start_touch (session.audible_sample());
439                         } else {
440                                 ac->stop_touch (session.audible_sample());
441                         }
442                 }
443         }
444 }
445
446 void
447 MixLayout::stripable_property_change (PropertyChange const& what_changed, uint32_t which)
448 {
449         if (what_changed.contains (Properties::color)) {
450                 lower_backgrounds[which]->set_fill_color (stripable[which]->presentation_info().color());
451
452                 if (stripable[which]->is_selected()) {
453                         lower_text[which]->set_fill_color (contrasting_text_color (stripable[which]->presentation_info().color()));
454                         /* might not be a MIDI track, in which case this will
455                            do nothing
456                         */
457                         p2.update_selection_color ();
458                 }
459         }
460
461         if (what_changed.contains (Properties::hidden)) {
462                 switch_bank (bank_start);
463         }
464
465         if (what_changed.contains (Properties::selected)) {
466
467                 if (!stripable[which]) {
468                         return;
469                 }
470
471                 if (stripable[which]->is_selected()) {
472                         show_selection (which);
473                 } else {
474                         hide_selection (which);
475                 }
476         }
477
478 }
479
480 void
481 MixLayout::show_selection (uint32_t n)
482 {
483         lower_backgrounds[n]->show ();
484         lower_backgrounds[n]->set_fill_color (stripable[n]->presentation_info().color());
485         lower_text[n]->set_color (contrasting_text_color (lower_backgrounds[n]->fill_color()));
486 }
487
488 void
489 MixLayout::hide_selection (uint32_t n)
490 {
491         lower_backgrounds[n]->hide ();
492         if (stripable[n]) {
493                 lower_text[n]->set_color (stripable[n]->presentation_info().color());
494         }
495 }
496
497 void
498 MixLayout::solo_changed (uint32_t n)
499 {
500         solo_mute_changed (n);
501 }
502
503 void
504 MixLayout::mute_changed (uint32_t n)
505 {
506         solo_mute_changed (n);
507 }
508
509 void
510 MixLayout::solo_mute_changed (uint32_t n)
511 {
512         string shortname = short_version (stripable[n]->name(), 10);
513         string text;
514         boost::shared_ptr<AutomationControl> ac;
515         ac = stripable[n]->solo_control();
516         if (ac && ac->get_value()) {
517                 text += "* ";
518         }
519         boost::shared_ptr<MuteControl> mc;
520         mc = stripable[n]->mute_control ();
521         if (mc) {
522                 if (mc->muted_by_self_or_masters()) {
523                         text += "! ";
524                 } else if (mc->muted_by_others_soloing()) {
525                         text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
526                 }
527         }
528         text += shortname;
529         lower_text[n]->set (text);
530 }
531
532 void
533 MixLayout::switch_bank (uint32_t base)
534 {
535         stripable_connections.drop_connections ();
536
537         /* work backwards so we can tell if we should actually switch banks */
538
539         boost::shared_ptr<Stripable> s[8];
540         uint32_t different = 0;
541
542         for (int n = 0; n < 8; ++n) {
543                 s[n] = session.get_remote_nth_stripable (base+n, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
544                 if (s[n] != stripable[n]) {
545                         different++;
546                 }
547         }
548
549         if (!s[0]) {
550                 /* not even the first stripable exists, do nothing */
551                 for (int n = 0; n < 8; ++n) {
552                         stripable[n].reset ();
553                         gain_meter[n]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
554                         gain_meter[n]->meter->set_meter (0);
555                 }
556                 return;
557         }
558
559         for (int n = 0; n < 8; ++n) {
560                 stripable[n] = s[n];
561         }
562
563         /* at least one stripable in this bank */
564
565         bank_start = base;
566
567         for (int n = 0; n < 8; ++n) {
568
569                 if (!stripable[n]) {
570                         lower_text[n]->hide ();
571                         hide_selection (n);
572                         gain_meter[n]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
573                         gain_meter[n]->meter->set_meter (0);
574                 } else {
575
576                         lower_text[n]->show ();
577
578                         /* stripable goes away? refill the bank, starting at the same point */
579
580                         stripable[n]->DropReferences.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
581                         stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
582                         stripable[n]->solo_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::solo_changed, this, n), &p2);
583                         stripable[n]->mute_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::mute_changed, this, n), &p2);
584
585                         if (stripable[n]->is_selected()) {
586                                 show_selection (n);
587                         } else {
588                                 hide_selection (n);
589                         }
590
591                         /* this will set lower text to the correct value (basically
592                            the stripable name)
593                         */
594
595                         solo_mute_changed (n);
596
597                         gain_meter[n]->knob->set_text_color (stripable[n]->presentation_info().color());
598                         gain_meter[n]->knob->set_arc_start_color (stripable[n]->presentation_info().color());
599                         gain_meter[n]->knob->set_arc_end_color (stripable[n]->presentation_info().color());
600                 }
601
602
603                 Push2::Button* b;
604
605                 switch (n) {
606                 case 0:
607                         b = p2.button_by_id (Push2::Lower1);
608                         break;
609                 case 1:
610                         b = p2.button_by_id (Push2::Lower2);
611                         break;
612                 case 2:
613                         b = p2.button_by_id (Push2::Lower3);
614                         break;
615                 case 3:
616                         b = p2.button_by_id (Push2::Lower4);
617                         break;
618                 case 4:
619                         b = p2.button_by_id (Push2::Lower5);
620                         break;
621                 case 5:
622                         b = p2.button_by_id (Push2::Lower6);
623                         break;
624                 case 6:
625                         b = p2.button_by_id (Push2::Lower7);
626                         break;
627                 case 7:
628                         b = p2.button_by_id (Push2::Lower8);
629                         break;
630                 }
631
632                 if (stripable[n]) {
633                         b->set_color (p2.get_color_index (stripable[n]->presentation_info().color()));
634                 } else {
635                         b->set_color (Push2::LED::Black);
636                 }
637
638                 b->set_state (Push2::LED::OneShot24th);
639                 p2.write (b->state_msg());
640         }
641
642         show_vpot_mode ();
643 }
644
645 void
646 MixLayout::button_right ()
647 {
648         switch_bank (max (0, bank_start + 8));
649 }
650
651 void
652 MixLayout::button_left ()
653 {
654         switch_bank (max (0, bank_start - 8));
655 }
656
657
658 void
659 MixLayout::button_select_press ()
660 {
661 }
662
663 void
664 MixLayout::button_select_release ()
665 {
666         if (!(p2.modifier_state() & Push2::ModSelect)) {
667                 /* somebody else used us as a modifier */
668                 return;
669         }
670
671         int selected = -1;
672
673         for (int n = 0; n < 8; ++n) {
674                 if (stripable[n]) {
675                         if (stripable[n]->is_selected()) {
676                                         selected = n;
677                                         break;
678                         }
679                 }
680         }
681
682         if (selected < 0) {
683
684                 /* no visible track selected, select first (if any) */
685
686                 if (stripable[0]) {
687                         ControlProtocol::SetStripableSelection (stripable[0]);
688                 }
689
690         } else {
691
692                 if (p2.modifier_state() & Push2::ModShift) {
693                         /* select prev */
694
695                         if (selected == 0) {
696                                 /* current selected is leftmost ... cancel selection,
697                                    switch banks by one, and select leftmost
698                                 */
699                                 if (bank_start != 0) {
700                                         ControlProtocol::ClearStripableSelection ();
701                                         switch_bank (bank_start-1);
702                                         if (stripable[0]) {
703                                                 ControlProtocol::SetStripableSelection (stripable[0]);
704                                         }
705                                 }
706                         } else {
707                                 /* select prev, if any */
708                                 int n = selected - 1;
709                                 while (n >= 0 && !stripable[n]) {
710                                         --n;
711                                 }
712                                 if (n >= 0) {
713                                         ControlProtocol::SetStripableSelection (stripable[n]);
714                                 }
715                         }
716
717                 } else {
718
719                         /* select next */
720
721                         if (selected == 7) {
722                                 /* current selected is rightmost ... cancel selection,
723                                    switch banks by one, and select righmost
724                                 */
725                                 ControlProtocol::ToggleStripableSelection (stripable[selected]);
726                                 switch_bank (bank_start+1);
727                                 if (stripable[7]) {
728                                         ControlProtocol::SetStripableSelection (stripable[7]);
729                                 }
730                         } else {
731                                 /* select next, if any */
732                                 int n = selected + 1;
733                                 while (n < 8 && !stripable[n]) {
734                                         ++n;
735                                 }
736
737                                 if (n != 8) {
738                                         ControlProtocol::SetStripableSelection (stripable[n]);
739                                 }
740                         }
741                 }
742         }
743 }
744
745 void
746 MixLayout::stripables_added ()
747 {
748         /* reload current bank */
749         switch_bank (bank_start);
750 }
751
752 void
753 MixLayout::button_down ()
754 {
755         p2.scroll_dn_1_track ();
756 }
757
758 void
759 MixLayout::button_up ()
760 {
761         p2.scroll_up_1_track ();
762 }
763
764 void
765 MixLayout::update_meters ()
766 {
767         if (vpot_mode != Volume) {
768                 return;
769         }
770
771         for (uint32_t n = 0; n < 8; ++n) {
772                 gain_meter[n]->meter->update_meters ();
773         }
774 }
775
776 MixLayout::GainMeter::GainMeter (Item* parent, Push2& p2)
777         : Container (parent)
778 {
779         knob = new Push2Knob (p2, this);
780         knob->set_radius (25);
781         /* leave position at (0,0) */
782
783         meter = new LevelMeter (p2, this, 90, ArdourCanvas::Meter::Vertical);
784         meter->set_position (Duple (40, -60));
785 }