2 Copyright (C) 2016 Paul Davis
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.
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.
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.
19 #include <pangomm/layout.h>
21 #include "pbd/compose.h"
22 #include "pbd/convert.h"
23 #include "pbd/debug.h"
24 #include "pbd/failed_constructor.h"
25 #include "pbd/file_utils.h"
26 #include "pbd/search_path.h"
27 #include "pbd/enumwriter.h"
29 #include "midi++/parser.h"
30 #include "timecode/time.h"
31 #include "timecode/bbt_time.h"
33 #include "ardour/async_midi_port.h"
34 #include "ardour/audioengine.h"
35 #include "ardour/debug.h"
36 #include "ardour/filesystem_paths.h"
37 #include "ardour/midiport_manager.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/midi_port.h"
40 #include "ardour/session.h"
41 #include "ardour/tempo.h"
43 #include "canvas/colors.h"
52 using namespace ARDOUR;
56 using namespace ArdourSurface;
58 MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
62 tc_clock_layout = Pango::Layout::create (context);
63 bbt_clock_layout = Pango::Layout::create (context);
65 Pango::FontDescription fd ("Sans Bold 24");
66 tc_clock_layout->set_font_description (fd);
67 bbt_clock_layout->set_font_description (fd);
69 Pango::FontDescription fd2 ("Sans 10");
70 for (int n = 0; n < 8; ++n) {
71 upper_layout[n] = Pango::Layout::create (context);
72 upper_layout[n]->set_font_description (fd2);
83 txt = _("Pan Widths");
101 upper_layout[n]->set_text (txt);
104 Pango::FontDescription fd3 ("Sans 10");
105 for (int n = 0; n < 8; ++n) {
106 lower_layout[n] = Pango::Layout::create (context);
107 lower_layout[n]->set_font_description (fd3);
110 for (int n = 0; n < 8; ++n) {
111 knobs[n] = new Push2Knob (p2);
112 knobs[n]->set_position (60 + (n*120), 95);
113 knobs[n]->set_radius (25);
119 MixLayout::~MixLayout ()
124 MixLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
126 framepos_t audible = session.audible_frame();
128 bool negative = false;
129 string tc_clock_text;
130 string bbt_clock_text;
137 session.timecode_time (audible, TC);
139 TC.negative = TC.negative || negative;
141 tc_clock_text = Timecode::timecode_format_time(TC);
143 Timecode::BBT_Time bbt = session.tempo_map().bbt_at_frame (audible);
146 #define BBT_BAR_CHAR "|"
149 snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
150 bbt.bars, bbt.beats, bbt.ticks);
152 snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
153 bbt.bars, bbt.beats, bbt.ticks);
156 bbt_clock_text = buf;
158 bool children_dirty = false;
160 if (tc_clock_text != tc_clock_layout->get_text()) {
161 children_dirty = true;
162 tc_clock_layout->set_text (tc_clock_text);
165 if (bbt_clock_text != tc_clock_layout->get_text()) {
166 children_dirty = true;
167 bbt_clock_layout->set_text (bbt_clock_text);
170 for (int n = 0; n < 8; ++n) {
171 if (knobs[n]->dirty()) {
172 children_dirty = true;
177 for (int n = 0; n < 8; ++n) {
179 string name = short_version (stripable[n]->name(), 10);
180 if (name != lower_layout[n]->get_text()) {
181 lower_layout[n]->set_text (name);
182 children_dirty = true;
187 if (!children_dirty) {
191 set_source_rgb (context, p2.get_color (Push2::DarkBackground));
192 context->rectangle (0, 0, p2.cols, p2.rows);
197 set_source_rgb (context, ArdourCanvas::contrasting_text_color (p2.get_color (Push2::DarkBackground)));
198 context->move_to (650, 30);
199 tc_clock_layout->update_from_cairo_context (context);
200 tc_clock_layout->show_in_cairo_context (context);
201 context->move_to (650, 65);
202 bbt_clock_layout->update_from_cairo_context (context);
203 bbt_clock_layout->show_in_cairo_context (context);
205 set_source_rgb (context, p2.get_color (Push2::ParameterName));
207 for (int n = 0; n < 8; ++n) {
208 if (upper_layout[n]->get_text().empty()) {
211 context->move_to (10 + (n*120), 2);
212 upper_layout[n]->update_from_cairo_context (context);
213 upper_layout[n]->show_in_cairo_context (context);
216 context->move_to (0, 22.5);
217 context->line_to (p2.cols, 22.5);
218 context->set_line_width (1.0);
221 for (int n = 0; n < 8; ++n) {
222 knobs[n]->redraw (context);
225 for (int n = 0; n < 8; ++n) {
227 if (lower_layout[n]->get_text().empty()) {
232 if (stripable[n]->presentation_info().selected()) {
233 set_source_rgb (context, stripable[n]->presentation_info().color());
234 context->rectangle (10 + (n*120) - 5, 137, 120, 22);
236 set_source_rgb (context, ArdourCanvas::contrasting_text_color (stripable[n]->presentation_info().color()));
238 set_source_rgb (context, stripable[n]->presentation_info().color());
241 /* XXX what color is used here? text should be empty anyway... no stripable */
242 context->move_to (10 + (n*120), 140);
243 lower_layout[n]->update_from_cairo_context (context);
244 lower_layout[n]->show_in_cairo_context (context);
251 MixLayout::button_upper (uint32_t n)
257 if (p2.modifier_state() & Push2::ModShift) {
258 boost::shared_ptr<AutomationControl> sc = stripable[n]->rec_enable_control ();
260 sc->set_value (!sc->get_value(), PBD::Controllable::UseGroup);
263 boost::shared_ptr<SoloControl> sc = stripable[n]->solo_control ();
265 sc->set_value (!sc->self_soloed(), PBD::Controllable::UseGroup);
271 MixLayout::button_lower (uint32_t n)
277 ControlProtocol::SetStripableSelection (stripable[n]);
281 MixLayout::strip_vpot (int n, int delta)
284 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
286 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
292 MixLayout::strip_vpot_touch (int n, bool touching)
295 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
298 ac->start_touch (session.audible_frame());
300 ac->stop_touch (true, session.audible_frame());
307 MixLayout::stripable_property_change (PropertyChange const& what_changed, int which)
309 if (what_changed.contains (Properties::selected)) {
310 if (!stripable[which]) {
314 /* cancel string, which will cause a redraw on the next update
315 * cycle. The redraw will reflect selected status
318 lower_layout[which]->set_text (string());
323 MixLayout::solo_change (int n)
356 boost::shared_ptr<SoloControl> ac = stripable[n]->solo_control ();
361 Push2::Button* b = p2.button_by_id (bid);
364 b->set_color (Push2::LED::Green);
366 b->set_color (Push2::LED::Black);
369 if (ac->soloed_by_others_upstream() || ac->soloed_by_others_downstream()) {
370 b->set_state (Push2::LED::Blinking4th);
372 b->set_state (Push2::LED::OneShot24th);
375 p2.write (b->state_msg());
379 MixLayout::mute_change (int n)
387 cerr << "Mute changed on " << n << ' ' << stripable[n]->name() << endl;
418 boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
424 Push2::Button* b = p2.button_by_id (bid);
426 if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
428 if (mc->muted_by_self ()) {
430 b->set_color (Push2::LED::Blue);
431 b->set_state (Push2::LED::OneShot24th);
432 cerr << "FULL MUTE1\n";
433 } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
434 /* this will reflect both solo mutes AND master mutes */
435 b->set_color (Push2::LED::Blue);
436 b->set_state (Push2::LED::Blinking4th);
437 cerr << "OTHER MUTE1\n";
440 b->set_color (Push2::LED::Black);
441 b->set_state (Push2::LED::OneShot24th);
442 cerr << "NO MUTE1\n";
447 if (mc->muted_by_self()) {
449 b->set_color (Push2::LED::Blue);
450 b->set_state (Push2::LED::OneShot24th);
451 cerr << "FULL MUTE2\n";
452 } else if (mc->muted_by_masters ()) {
453 /* this shows only master mutes, not mute-by-others-soloing */
454 b->set_color (Push2::LED::Blue);
455 b->set_state (Push2::LED::Blinking4th);
456 cerr << "OTHER MUTE1\n";
459 b->set_color (Push2::LED::Black);
460 b->set_state (Push2::LED::OneShot24th);
461 cerr << "NO MUTE2\n";
465 p2.write (b->state_msg());
469 MixLayout::switch_bank (uint32_t base)
471 stripable_connections.drop_connections ();
473 /* try to get the first stripable for the requested bank */
475 stripable[0] = session.get_remote_nth_stripable (base, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
481 /* at least one stripable in this bank */
484 stripable[1] = session.get_remote_nth_stripable (base+1, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
485 stripable[2] = session.get_remote_nth_stripable (base+2, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
486 stripable[3] = session.get_remote_nth_stripable (base+3, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
487 stripable[4] = session.get_remote_nth_stripable (base+4, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
488 stripable[5] = session.get_remote_nth_stripable (base+5, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
489 stripable[6] = session.get_remote_nth_stripable (base+6, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
490 stripable[7] = session.get_remote_nth_stripable (base+7, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
493 for (int n = 0; n < 8; ++n) {
498 /* stripable goes away? refill the bank, starting at the same point */
500 stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
501 boost::shared_ptr<AutomationControl> sc = stripable[n]->solo_control();
503 sc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::solo_change, this, n), &p2);
506 boost::shared_ptr<AutomationControl> mc = stripable[n]->mute_control();
508 mc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::mute_change, this, n), &p2);
511 stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
520 b = p2.button_by_id (Push2::Lower1);
523 b = p2.button_by_id (Push2::Lower2);
526 b = p2.button_by_id (Push2::Lower3);
529 b = p2.button_by_id (Push2::Lower4);
532 b = p2.button_by_id (Push2::Lower5);
535 b = p2.button_by_id (Push2::Lower6);
538 b = p2.button_by_id (Push2::Lower7);
541 b = p2.button_by_id (Push2::Lower8);
545 b->set_color (p2.get_color_index (stripable[n]->presentation_info().color()));
546 b->set_state (Push2::LED::OneShot24th);
547 p2.write (b->state_msg());
552 MixLayout::button_right ()
554 switch_bank (max (0, bank_start + 8));
558 MixLayout::button_left ()
560 switch_bank (max (0, bank_start - 8));
565 MixLayout::button_select_press ()
570 MixLayout::button_select_release ()
572 if (!(p2.modifier_state() & Push2::ModSelect)) {
573 /* somebody else used us as a modifier */
579 for (int n = 0; n < 8; ++n) {
581 if (stripable[n]->presentation_info().selected()) {
590 /* no visible track selected, select first (if any) */
593 ControlProtocol::SetStripableSelection (stripable[0]);
598 if (p2.modifier_state() & Push2::ModShift) {
599 std::cerr << "select prev\n";
603 /* current selected is leftmost ... cancel selection,
604 switch banks by one, and select leftmost
606 if (bank_start != 0) {
607 ControlProtocol::ClearStripableSelection ();
608 switch_bank (bank_start-1);
610 ControlProtocol::SetStripableSelection (stripable[0]);
614 /* select prev, if any */
615 int n = selected - 1;
616 while (n >= 0 && !stripable[n]) {
620 ControlProtocol::SetStripableSelection (stripable[n]);
626 std::cerr << "select next\n";
630 /* current selected is rightmost ... cancel selection,
631 switch banks by one, and select righmost
633 ControlProtocol::ToggleStripableSelection (stripable[selected]);
634 switch_bank (bank_start+1);
636 ControlProtocol::SetStripableSelection (stripable[7]);
639 /* select next, if any */
640 int n = selected + 1;
641 while (n < 8 && !stripable[n]) {
646 ControlProtocol::SetStripableSelection (stripable[n]);