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"
48 using namespace ARDOUR;
52 using namespace ArdourSurface;
54 MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
58 tc_clock_layout = Pango::Layout::create (context);
59 bbt_clock_layout = Pango::Layout::create (context);
61 Pango::FontDescription fd ("Sans Bold 24");
62 tc_clock_layout->set_font_description (fd);
63 bbt_clock_layout->set_font_description (fd);
65 Pango::FontDescription fd2 ("Sans 10");
66 for (int n = 0; n < 8; ++n) {
67 upper_layout[n] = Pango::Layout::create (context);
68 upper_layout[n]->set_font_description (fd2);
69 upper_layout[n]->set_text ("solo");
70 lower_layout[n] = Pango::Layout::create (context);
71 lower_layout[n]->set_font_description (fd2);
72 lower_layout[n]->set_text ("mute");
75 Pango::FontDescription fd3 ("Sans Bold 10");
76 for (int n = 0; n < 8; ++n) {
77 mid_layout[n] = Pango::Layout::create (context);
78 mid_layout[n]->set_font_description (fd3);
84 MixLayout::~MixLayout ()
89 MixLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
91 framepos_t audible = session.audible_frame();
93 bool negative = false;
95 string bbt_clock_text;
102 session.timecode_time (audible, TC);
104 TC.negative = TC.negative || negative;
106 tc_clock_text = Timecode::timecode_format_time(TC);
108 Timecode::BBT_Time bbt = session.tempo_map().bbt_at_frame (audible);
111 #define BBT_BAR_CHAR "|"
114 snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
115 bbt.bars, bbt.beats, bbt.ticks);
117 snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
118 bbt.bars, bbt.beats, bbt.ticks);
121 bbt_clock_text = buf;
125 if (tc_clock_text != tc_clock_layout->get_text()) {
127 tc_clock_layout->set_text (tc_clock_text);
130 if (bbt_clock_text != tc_clock_layout->get_text()) {
132 bbt_clock_layout->set_text (bbt_clock_text);
137 for (int n = 0; n < 8; ++n) {
139 mid_text = short_version (stripable[n]->name(), 10);
140 if (mid_text != mid_layout[n]->get_text()) {
141 mid_layout[n]->set_text (mid_text);
151 context->set_source_rgb (0.764, 0.882, 0.882);
152 context->rectangle (0, 0, 960, 160);
157 context->set_source_rgb (0.23, 0.0, 0.349);
158 context->move_to (650, 25);
159 tc_clock_layout->update_from_cairo_context (context);
160 tc_clock_layout->show_in_cairo_context (context);
161 context->move_to (650, 60);
162 bbt_clock_layout->update_from_cairo_context (context);
163 bbt_clock_layout->show_in_cairo_context (context);
165 for (int n = 0; n < 8; ++n) {
166 context->move_to (10 + (n*120), 2);
167 upper_layout[n]->update_from_cairo_context (context);
168 upper_layout[n]->show_in_cairo_context (context);
171 for (int n = 0; n < 8; ++n) {
172 context->move_to (10 + (n*120), 140);
173 lower_layout[n]->update_from_cairo_context (context);
174 lower_layout[n]->show_in_cairo_context (context);
177 for (int n = 0; n < 8; ++n) {
178 if (stripable[n] && stripable[n]->presentation_info().selected()) {
179 context->rectangle (10 + (n*120) - 5, 115, 120, 22);
180 context->set_source_rgb (1.0, 0.737, 0.172);
183 context->set_source_rgb (0.0, 0.0, 0.0);
184 context->move_to (10 + (n*120), 120);
185 mid_layout[n]->update_from_cairo_context (context);
186 mid_layout[n]->show_in_cairo_context (context);
193 MixLayout::button_upper (uint32_t n)
199 if (p2.modifier_state() & Push2::ModShift) {
200 boost::shared_ptr<AutomationControl> sc = stripable[n]->rec_enable_control ();
202 sc->set_value (!sc->get_value(), PBD::Controllable::UseGroup);
205 boost::shared_ptr<SoloControl> sc = stripable[n]->solo_control ();
207 sc->set_value (!sc->self_soloed(), PBD::Controllable::UseGroup);
213 MixLayout::button_lower (uint32_t n)
219 if (p2.modifier_state() & Push2::ModSelect) {
220 ControlProtocol::SetStripableSelection (stripable[n]);
222 boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
225 mc->set_value (!mc->muted_by_self(), PBD::Controllable::UseGroup);
231 MixLayout::strip_vpot (int n, int delta)
234 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
236 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
242 MixLayout::strip_vpot_touch (int n, bool touching)
245 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
248 ac->start_touch (session.audible_frame());
250 ac->stop_touch (true, session.audible_frame());
257 MixLayout::stripable_property_change (PropertyChange const& what_changed, int which)
259 if (what_changed.contains (Properties::selected)) {
260 if (!stripable[which]) {
264 /* cancel string, which will cause a redraw on the next update
265 * cycle. The redraw will reflect selected status
268 mid_layout[which]->set_text (string());
274 MixLayout::solo_change (int n)
307 boost::shared_ptr<SoloControl> ac = stripable[n]->solo_control ();
312 Push2::Button* b = p2.button_by_id (bid);
315 b->set_color (Push2::LED::Green);
317 b->set_color (Push2::LED::Black);
320 if (ac->soloed_by_others_upstream() || ac->soloed_by_others_downstream()) {
321 b->set_state (Push2::LED::Blinking4th);
323 b->set_state (Push2::LED::OneShot24th);
326 p2.write (b->state_msg());
330 MixLayout::mute_change (int n)
338 cerr << "Mute changed on " << n << ' ' << stripable[n]->name() << endl;
369 boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
375 Push2::Button* b = p2.button_by_id (bid);
377 if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
379 if (mc->muted_by_self ()) {
381 b->set_color (Push2::LED::Blue);
382 b->set_state (Push2::LED::OneShot24th);
383 cerr << "FULL MUTE1\n";
384 } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
385 /* this will reflect both solo mutes AND master mutes */
386 b->set_color (Push2::LED::Blue);
387 b->set_state (Push2::LED::Blinking4th);
388 cerr << "OTHER MUTE1\n";
391 b->set_color (Push2::LED::Black);
392 b->set_state (Push2::LED::OneShot24th);
393 cerr << "NO MUTE1\n";
398 if (mc->muted_by_self()) {
400 b->set_color (Push2::LED::Blue);
401 b->set_state (Push2::LED::OneShot24th);
402 cerr << "FULL MUTE2\n";
403 } else if (mc->muted_by_masters ()) {
404 /* this shows only master mutes, not mute-by-others-soloing */
405 b->set_color (Push2::LED::Blue);
406 b->set_state (Push2::LED::Blinking4th);
407 cerr << "OTHER MUTE1\n";
410 b->set_color (Push2::LED::Black);
411 b->set_state (Push2::LED::OneShot24th);
412 cerr << "NO MUTE2\n";
416 p2.write (b->state_msg());
420 MixLayout::switch_bank (uint32_t base)
422 stripable_connections.drop_connections ();
424 /* try to get the first stripable for the requested bank */
426 stripable[0] = session.get_remote_nth_stripable (base, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
432 /* at least one stripable in this bank */
435 stripable[1] = session.get_remote_nth_stripable (base+1, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
436 stripable[2] = session.get_remote_nth_stripable (base+2, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
437 stripable[3] = session.get_remote_nth_stripable (base+3, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
438 stripable[4] = session.get_remote_nth_stripable (base+4, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
439 stripable[5] = session.get_remote_nth_stripable (base+5, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
440 stripable[6] = session.get_remote_nth_stripable (base+6, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
441 stripable[7] = session.get_remote_nth_stripable (base+7, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
444 for (int n = 0; n < 8; ++n) {
449 /* stripable goes away? refill the bank, starting at the same point */
451 stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
452 boost::shared_ptr<AutomationControl> sc = stripable[n]->solo_control();
454 sc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::solo_change, this, n), &p2);
457 boost::shared_ptr<AutomationControl> mc = stripable[n]->mute_control();
459 mc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::mute_change, this, n), &p2);
462 stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
471 MixLayout::button_right ()
473 switch_bank (max (0, bank_start + 8));
477 MixLayout::button_left ()
479 switch_bank (max (0, bank_start - 8));
483 MixLayout::button_select_press ()
488 MixLayout::button_select_release ()
490 if (!(p2.modifier_state() & Push2::ModSelect)) {
491 /* somebody else used us as a modifier */
497 for (int n = 0; n < 8; ++n) {
499 if (stripable[n]->presentation_info().selected()) {
508 /* no visible track selected, select first (if any) */
511 ControlProtocol::SetStripableSelection (stripable[0]);
516 if (p2.modifier_state() & Push2::ModShift) {
517 std::cerr << "select prev\n";
521 /* current selected is leftmost ... cancel selection,
522 switch banks by one, and select leftmost
524 if (bank_start != 0) {
525 ControlProtocol::ClearStripableSelection ();
526 switch_bank (bank_start-1);
528 ControlProtocol::SetStripableSelection (stripable[0]);
532 /* select prev, if any */
533 int n = selected - 1;
534 while (n >= 0 && !stripable[n]) {
538 ControlProtocol::SetStripableSelection (stripable[n]);
544 std::cerr << "select next\n";
548 /* current selected is rightmost ... cancel selection,
549 switch banks by one, and select righmost
551 ControlProtocol::ToggleStripableSelection (stripable[selected]);
552 switch_bank (bank_start+1);
554 ControlProtocol::SetStripableSelection (stripable[7]);
557 /* select next, if any */
558 int n = selected + 1;
559 while (n < 8 && !stripable[n]) {
564 ControlProtocol::SetStripableSelection (stripable[n]);