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"
42 #include "ardour/vca_manager.h"
44 #include "canvas/colors.h"
53 using namespace ARDOUR;
57 using namespace ArdourSurface;
59 MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
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);
79 txt = _("Pan Widths");
97 upper_layout[n]->set_text (txt);
100 Pango::FontDescription fd3 ("Sans 10");
101 for (int n = 0; n < 8; ++n) {
102 lower_layout[n] = Pango::Layout::create (context);
103 lower_layout[n]->set_font_description (fd3);
106 for (int n = 0; n < 8; ++n) {
107 knobs[n] = new Push2Knob (p2, context);
108 knobs[n]->set_position (60 + (n*120), 95);
109 knobs[n]->set_radius (25);
112 mode_button = p2.button_by_id (Push2::Upper1);
114 session.RouteAdded.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripables_added, this), &p2);
115 session.vca_manager().VCAAdded.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripables_added, this), &p2);
118 MixLayout::~MixLayout ()
120 for (int n = 0; n < 8; ++n) {
126 MixLayout::on_show ()
128 mode_button->set_color (Push2::LED::White);
129 mode_button->set_state (Push2::LED::OneShot24th);
130 p2.write (mode_button->state_msg());
132 switch_bank (bank_start);
136 MixLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
138 bool children_dirty = false;
140 for (int n = 0; n < 8; ++n) {
141 if (knobs[n]->dirty()) {
142 children_dirty = true;
147 for (int n = 0; n < 8; ++n) {
150 string shortname = short_version (stripable[n]->name(), 10);
152 boost::shared_ptr<AutomationControl> ac;
153 ac = stripable[n]->solo_control();
154 if (ac && ac->get_value()) {
157 boost::shared_ptr<MuteControl> mc;
158 mc = stripable[n]->mute_control ();
160 if (mc->muted_by_self_or_masters()) {
162 } else if (mc->muted_by_others_soloing()) {
163 text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
168 if (text != lower_layout[n]->get_text()) {
169 lower_layout[n]->set_text (text);
170 children_dirty = true;
175 if (!children_dirty && !_dirty) {
179 set_source_rgb (context, p2.get_color (Push2::DarkBackground));
180 context->rectangle (0, 0, p2.cols, p2.rows);
183 set_source_rgb (context, p2.get_color (Push2::ParameterName));
185 for (int n = 0; n < 8; ++n) {
187 if (upper_layout[n]->get_text().empty()) {
191 /* Draw highlight box */
193 uint32_t color = p2.get_color (Push2::ParameterName);
195 if (n == (int) vpot_mode) {
196 set_source_rgb (context, color);
197 context->rectangle (10 + (n*120) - 5, 2, 120, 21);
199 set_source_rgb (context, ArdourCanvas::contrasting_text_color (color));
201 set_source_rgb (context, color);
204 context->move_to (10 + (n*120), 2);
205 upper_layout[n]->update_from_cairo_context (context);
206 upper_layout[n]->show_in_cairo_context (context);
209 context->move_to (0, 22.5);
210 context->line_to (p2.cols, 22.5);
211 context->set_line_width (1.0);
214 for (int n = 0; n < 8; ++n) {
215 knobs[n]->redraw (context);
218 for (int n = 0; n < 8; ++n) {
220 if (lower_layout[n]->get_text().empty()) {
225 uint32_t color = stripable[n]->presentation_info().color();
227 if (stripable[n]->presentation_info().selected()) {
228 set_source_rgb (context, color);
229 context->rectangle (10 + (n*120) - 5, 137, 120, 21);
231 set_source_rgb (context, ArdourCanvas::contrasting_text_color (color));
233 set_source_rgb (context, color);
236 context->move_to (10 + (n*120), 140);
237 lower_layout[n]->update_from_cairo_context (context);
238 lower_layout[n]->show_in_cairo_context (context);
248 MixLayout::button_upper (uint32_t n)
254 b = p2.button_by_id (Push2::Upper1);
257 vpot_mode = PanAzimuth;
258 b = p2.button_by_id (Push2::Upper2);
261 vpot_mode = PanWidth;
262 b = p2.button_by_id (Push2::Upper3);
266 b = p2.button_by_id (Push2::Upper4);
270 b = p2.button_by_id (Push2::Upper5);
274 b = p2.button_by_id (Push2::Upper6);
278 b = p2.button_by_id (Push2::Upper7);
282 b = p2.button_by_id (Push2::Upper8);
286 if (b != mode_button) {
287 mode_button->set_color (Push2::LED::Black);
288 mode_button->set_state (Push2::LED::OneShot24th);
289 p2.write (mode_button->state_msg());
298 MixLayout::show_vpot_mode ()
300 mode_button->set_color (Push2::LED::White);
301 mode_button->set_state (Push2::LED::OneShot24th);
302 p2.write (mode_button->state_msg());
304 boost::shared_ptr<AutomationControl> ac;
307 for (int s = 0; s < 8; ++s) {
309 knobs[s]->set_controllable (stripable[s]->gain_control());
311 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
313 knobs[s]->remove_flag (Push2Knob::ArcToZero);
317 for (int s = 0; s < 8; ++s) {
319 knobs[s]->set_controllable (stripable[s]->pan_azimuth_control());
320 knobs[s]->add_flag (Push2Knob::ArcToZero);
322 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
328 for (int s = 0; s < 8; ++s) {
330 knobs[s]->set_controllable (stripable[s]->pan_width_control());
332 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
335 knobs[s]->remove_flag (Push2Knob::ArcToZero);
339 for (int s = 0; s < 8; ++s) {
341 knobs[s]->set_controllable (stripable[s]->send_level_controllable (0));
343 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
346 knobs[s]->remove_flag (Push2Knob::ArcToZero);
350 for (int s = 0; s < 8; ++s) {
352 knobs[s]->set_controllable (stripable[s]->send_level_controllable (1));
354 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
357 knobs[s]->remove_flag (Push2Knob::ArcToZero);
361 for (int s = 0; s < 8; ++s) {
363 knobs[s]->set_controllable (stripable[s]->send_level_controllable (2));
365 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
368 knobs[s]->remove_flag (Push2Knob::ArcToZero);
372 for (int s = 0; s < 8; ++s) {
374 knobs[s]->set_controllable (stripable[s]->send_level_controllable (3));
376 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
379 knobs[s]->remove_flag (Push2Knob::ArcToZero);
383 for (int s = 0; s < 8; ++s) {
385 knobs[s]->set_controllable (stripable[s]->send_level_controllable (4));
387 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
390 knobs[s]->remove_flag (Push2Knob::ArcToZero);
401 MixLayout::button_mute ()
403 boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
405 boost::shared_ptr<AutomationControl> ac = s->mute_control();
407 ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
413 MixLayout::button_solo ()
415 boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
417 boost::shared_ptr<AutomationControl> ac = s->solo_control();
419 ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
425 MixLayout::button_lower (uint32_t n)
431 ControlProtocol::SetStripableSelection (stripable[n]);
435 MixLayout::strip_vpot (int n, int delta)
437 boost::shared_ptr<Controllable> ac = knobs[n]->controllable();
440 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
445 MixLayout::strip_vpot_touch (int n, bool touching)
448 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
451 ac->start_touch (session.audible_frame());
453 ac->stop_touch (true, session.audible_frame());
460 MixLayout::stripable_property_change (PropertyChange const& what_changed, int which)
462 if (what_changed.contains (Properties::selected)) {
463 if (!stripable[which]) {
467 /* cancel string, which will cause a redraw on the next update
468 * cycle. The redraw will reflect selected status
471 lower_layout[which]->set_text (string());
476 MixLayout::switch_bank (uint32_t base)
478 stripable_connections.drop_connections ();
480 /* work backwards so we can tell if we should actually switch banks */
482 boost::shared_ptr<Stripable> s[8];
483 uint32_t old_empty = 0;
484 uint32_t new_empty = 0;
486 for (int n = 0; n < 8; ++n) {
490 s[n] = session.get_remote_nth_stripable (base+n, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
496 if ((new_empty != 0) && (new_empty >= old_empty)) {
497 /* some missing strips; new bank the same or more empty stripables than the old one, do
498 nothing since we had already reached the end.
503 for (int n = 0; n < 8; ++n) {
507 /* at least one stripable in this bank */
511 for (int n = 0; n < 8; ++n) {
516 /* stripable goes away? refill the bank, starting at the same point */
518 stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
519 stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
525 b = p2.button_by_id (Push2::Lower1);
528 b = p2.button_by_id (Push2::Lower2);
531 b = p2.button_by_id (Push2::Lower3);
534 b = p2.button_by_id (Push2::Lower4);
537 b = p2.button_by_id (Push2::Lower5);
540 b = p2.button_by_id (Push2::Lower6);
543 b = p2.button_by_id (Push2::Lower7);
546 b = p2.button_by_id (Push2::Lower8);
550 b->set_color (p2.get_color_index (stripable[n]->presentation_info().color()));
551 b->set_state (Push2::LED::OneShot24th);
552 p2.write (b->state_msg());
554 knobs[n]->set_text_color (stripable[n]->presentation_info().color());
555 knobs[n]->set_arc_start_color (stripable[n]->presentation_info().color());
556 knobs[n]->set_arc_end_color (stripable[n]->presentation_info().color());
563 MixLayout::button_right ()
565 switch_bank (max (0, bank_start + 8));
569 MixLayout::button_left ()
571 switch_bank (max (0, bank_start - 8));
576 MixLayout::button_select_press ()
581 MixLayout::button_select_release ()
583 if (!(p2.modifier_state() & Push2::ModSelect)) {
584 /* somebody else used us as a modifier */
590 for (int n = 0; n < 8; ++n) {
592 if (stripable[n]->presentation_info().selected()) {
601 /* no visible track selected, select first (if any) */
604 ControlProtocol::SetStripableSelection (stripable[0]);
609 if (p2.modifier_state() & Push2::ModShift) {
610 std::cerr << "select prev\n";
614 /* current selected is leftmost ... cancel selection,
615 switch banks by one, and select leftmost
617 if (bank_start != 0) {
618 ControlProtocol::ClearStripableSelection ();
619 switch_bank (bank_start-1);
621 ControlProtocol::SetStripableSelection (stripable[0]);
625 /* select prev, if any */
626 int n = selected - 1;
627 while (n >= 0 && !stripable[n]) {
631 ControlProtocol::SetStripableSelection (stripable[n]);
637 std::cerr << "select next\n";
641 /* current selected is rightmost ... cancel selection,
642 switch banks by one, and select righmost
644 ControlProtocol::ToggleStripableSelection (stripable[selected]);
645 switch_bank (bank_start+1);
647 ControlProtocol::SetStripableSelection (stripable[7]);
650 /* select next, if any */
651 int n = selected + 1;
652 while (n < 8 && !stripable[n]) {
657 ControlProtocol::SetStripableSelection (stripable[n]);
665 MixLayout::stripables_added ()
667 /* reload current bank */
668 switch_bank (bank_start);