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)
64 tc_clock_layout = Pango::Layout::create (context);
65 bbt_clock_layout = Pango::Layout::create (context);
67 Pango::FontDescription fd ("Sans Bold 24");
68 tc_clock_layout->set_font_description (fd);
69 bbt_clock_layout->set_font_description (fd);
71 Pango::FontDescription fd2 ("Sans 10");
72 for (int n = 0; n < 8; ++n) {
73 upper_layout[n] = Pango::Layout::create (context);
74 upper_layout[n]->set_font_description (fd2);
85 txt = _("Pan Widths");
103 upper_layout[n]->set_text (txt);
106 Pango::FontDescription fd3 ("Sans 10");
107 for (int n = 0; n < 8; ++n) {
108 lower_layout[n] = Pango::Layout::create (context);
109 lower_layout[n]->set_font_description (fd3);
112 for (int n = 0; n < 8; ++n) {
113 knobs[n] = new Push2Knob (p2, context);
114 knobs[n]->set_position (60 + (n*120), 95);
115 knobs[n]->set_radius (25);
118 mode_button = p2.button_by_id (Push2::Upper1);
121 MixLayout::~MixLayout ()
123 for (int n = 0; n < 8; ++n) {
129 MixLayout::on_show ()
131 mode_button->set_color (Push2::LED::White);
132 mode_button->set_state (Push2::LED::OneShot24th);
133 p2.write (mode_button->state_msg());
135 switch_bank (bank_start);
140 MixLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
142 framepos_t audible = session.audible_frame();
144 bool negative = false;
145 string tc_clock_text;
146 string bbt_clock_text;
153 session.timecode_time (audible, TC);
155 TC.negative = TC.negative || negative;
157 tc_clock_text = Timecode::timecode_format_time(TC);
159 Timecode::BBT_Time bbt = session.tempo_map().bbt_at_frame (audible);
162 #define BBT_BAR_CHAR "|"
165 snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
166 bbt.bars, bbt.beats, bbt.ticks);
168 snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
169 bbt.bars, bbt.beats, bbt.ticks);
172 bbt_clock_text = buf;
174 bool children_dirty = false;
176 if (tc_clock_text != tc_clock_layout->get_text()) {
177 children_dirty = true;
178 tc_clock_layout->set_text (tc_clock_text);
181 if (bbt_clock_text != bbt_clock_layout->get_text()) {
182 children_dirty = true;
183 bbt_clock_layout->set_text (bbt_clock_text);
186 for (int n = 0; n < 8; ++n) {
187 if (knobs[n]->dirty()) {
188 children_dirty = true;
193 for (int n = 0; n < 8; ++n) {
195 string shortname = short_version (stripable[n]->name(), 10);
197 boost::shared_ptr<AutomationControl> ac;
198 ac = stripable[n]->solo_control();
199 if (ac && ac->get_value()) {
202 boost::shared_ptr<MuteControl> mc;
203 mc = stripable[n]->mute_control ();
205 if (mc->muted_by_self_or_masters()) {
207 } else if (mc->muted_by_others_soloing()) {
208 text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
213 if (text != lower_layout[n]->get_text()) {
214 lower_layout[n]->set_text (text);
215 children_dirty = true;
220 if (!children_dirty && !_dirty) {
224 set_source_rgb (context, p2.get_color (Push2::DarkBackground));
225 context->rectangle (0, 0, p2.cols, p2.rows);
230 set_source_rgb (context, ArdourCanvas::contrasting_text_color (p2.get_color (Push2::DarkBackground)));
231 context->move_to (650, 30);
232 tc_clock_layout->update_from_cairo_context (context);
233 tc_clock_layout->show_in_cairo_context (context);
234 context->move_to (650, 65);
235 bbt_clock_layout->update_from_cairo_context (context);
236 bbt_clock_layout->show_in_cairo_context (context);
238 set_source_rgb (context, p2.get_color (Push2::ParameterName));
240 for (int n = 0; n < 8; ++n) {
242 if (upper_layout[n]->get_text().empty()) {
246 /* Draw highlight box */
248 uint32_t color = p2.get_color (Push2::ParameterName);
250 if (n == (int) vpot_mode) {
251 set_source_rgb (context, color);
252 context->rectangle (10 + (n*120) - 5, 2, 120, 21);
254 set_source_rgb (context, ArdourCanvas::contrasting_text_color (color));
256 set_source_rgb (context, color);
259 context->move_to (10 + (n*120), 2);
260 upper_layout[n]->update_from_cairo_context (context);
261 upper_layout[n]->show_in_cairo_context (context);
264 context->move_to (0, 22.5);
265 context->line_to (p2.cols, 22.5);
266 context->set_line_width (1.0);
269 for (int n = 0; n < 8; ++n) {
270 knobs[n]->redraw (context);
273 for (int n = 0; n < 8; ++n) {
275 if (lower_layout[n]->get_text().empty()) {
280 uint32_t color = stripable[n]->presentation_info().color();
282 if (stripable[n]->presentation_info().selected()) {
283 set_source_rgb (context, color);
284 context->rectangle (10 + (n*120) - 5, 137, 120, 21);
286 set_source_rgb (context, ArdourCanvas::contrasting_text_color (color));
288 set_source_rgb (context, color);
291 context->move_to (10 + (n*120), 140);
292 lower_layout[n]->update_from_cairo_context (context);
293 lower_layout[n]->show_in_cairo_context (context);
303 MixLayout::button_upper (uint32_t n)
309 b = p2.button_by_id (Push2::Upper1);
312 vpot_mode = PanAzimuth;
313 b = p2.button_by_id (Push2::Upper2);
316 vpot_mode = PanWidth;
317 b = p2.button_by_id (Push2::Upper3);
321 b = p2.button_by_id (Push2::Upper4);
325 b = p2.button_by_id (Push2::Upper5);
329 b = p2.button_by_id (Push2::Upper6);
333 b = p2.button_by_id (Push2::Upper7);
337 b = p2.button_by_id (Push2::Upper8);
341 if (b != mode_button) {
342 mode_button->set_color (Push2::LED::Black);
343 mode_button->set_state (Push2::LED::OneShot24th);
344 p2.write (mode_button->state_msg());
353 MixLayout::show_vpot_mode ()
355 mode_button->set_color (Push2::LED::White);
356 mode_button->set_state (Push2::LED::OneShot24th);
357 p2.write (mode_button->state_msg());
359 boost::shared_ptr<AutomationControl> ac;
362 for (int s = 0; s < 8; ++s) {
364 knobs[s]->set_controllable (stripable[s]->gain_control());
366 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]->pan_azimuth_control());
375 knobs[s]->add_flag (Push2Knob::ArcToZero);
377 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
383 for (int s = 0; s < 8; ++s) {
385 knobs[s]->set_controllable (stripable[s]->pan_width_control());
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::solo_change (int n)
481 MixLayout::mute_change (int n)
486 MixLayout::switch_bank (uint32_t base)
488 stripable_connections.drop_connections ();
490 /* try to get the first stripable for the requested bank */
492 stripable[0] = session.get_remote_nth_stripable (base, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
498 /* at least one stripable in this bank */
501 stripable[1] = session.get_remote_nth_stripable (base+1, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
502 stripable[2] = session.get_remote_nth_stripable (base+2, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
503 stripable[3] = session.get_remote_nth_stripable (base+3, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
504 stripable[4] = session.get_remote_nth_stripable (base+4, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
505 stripable[5] = session.get_remote_nth_stripable (base+5, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
506 stripable[6] = session.get_remote_nth_stripable (base+6, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
507 stripable[7] = session.get_remote_nth_stripable (base+7, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
510 for (int n = 0; n < 8; ++n) {
515 /* stripable goes away? refill the bank, starting at the same point */
517 stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
518 stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
527 b = p2.button_by_id (Push2::Lower1);
530 b = p2.button_by_id (Push2::Lower2);
533 b = p2.button_by_id (Push2::Lower3);
536 b = p2.button_by_id (Push2::Lower4);
539 b = p2.button_by_id (Push2::Lower5);
542 b = p2.button_by_id (Push2::Lower6);
545 b = p2.button_by_id (Push2::Lower7);
548 b = p2.button_by_id (Push2::Lower8);
552 b->set_color (p2.get_color_index (stripable[n]->presentation_info().color()));
553 b->set_state (Push2::LED::OneShot24th);
554 p2.write (b->state_msg());
556 knobs[n]->set_text_color (stripable[n]->presentation_info().color());
561 MixLayout::button_right ()
563 switch_bank (max (0, bank_start + 8));
567 MixLayout::button_left ()
569 switch_bank (max (0, bank_start - 8));
574 MixLayout::button_select_press ()
579 MixLayout::button_select_release ()
581 if (!(p2.modifier_state() & Push2::ModSelect)) {
582 /* somebody else used us as a modifier */
588 for (int n = 0; n < 8; ++n) {
590 if (stripable[n]->presentation_info().selected()) {
599 /* no visible track selected, select first (if any) */
602 ControlProtocol::SetStripableSelection (stripable[0]);
607 if (p2.modifier_state() & Push2::ModShift) {
608 std::cerr << "select prev\n";
612 /* current selected is leftmost ... cancel selection,
613 switch banks by one, and select leftmost
615 if (bank_start != 0) {
616 ControlProtocol::ClearStripableSelection ();
617 switch_bank (bank_start-1);
619 ControlProtocol::SetStripableSelection (stripable[0]);
623 /* select prev, if any */
624 int n = selected - 1;
625 while (n >= 0 && !stripable[n]) {
629 ControlProtocol::SetStripableSelection (stripable[n]);
635 std::cerr << "select next\n";
639 /* current selected is rightmost ... cancel selection,
640 switch banks by one, and select righmost
642 ControlProtocol::ToggleStripableSelection (stripable[selected]);
643 switch_bank (bank_start+1);
645 ControlProtocol::SetStripableSelection (stripable[7]);
648 /* select next, if any */
649 int n = selected + 1;
650 while (n < 8 && !stripable[n]) {
655 ControlProtocol::SetStripableSelection (stripable[n]);