summaryrefslogtreecommitdiff
path: root/libs/surfaces/push2
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2016-07-10 10:49:39 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2016-09-27 14:59:30 -0500
commit14d80ec5f86c06b6327ccd4a08582afd1f95b4ce (patch)
treea4729042681cf33188d3563febb3e487d884ca48 /libs/surfaces/push2
parent86578ea0cc1daef464953fdb80820d40a8167f78 (diff)
refactor Push2 GUI into modular "layouts" that encapsulate a given screen, its drawing and event handling
Diffstat (limited to 'libs/surfaces/push2')
-rw-r--r--libs/surfaces/push2/buttons.cc157
-rw-r--r--libs/surfaces/push2/layout.cc38
-rw-r--r--libs/surfaces/push2/mix.cc570
-rw-r--r--libs/surfaces/push2/push2.cc706
-rw-r--r--libs/surfaces/push2/push2.h306
-rw-r--r--libs/surfaces/push2/scale.cc159
-rw-r--r--libs/surfaces/push2/wscript3
7 files changed, 1117 insertions, 822 deletions
diff --git a/libs/surfaces/push2/buttons.cc b/libs/surfaces/push2/buttons.cc
index 8e916d126f..fe9e3322f4 100644
--- a/libs/surfaces/push2/buttons.cc
+++ b/libs/surfaces/push2/buttons.cc
@@ -213,7 +213,7 @@ Push2::button_play ()
return;
}
- if (modifier_state & ModShift) {
+ if (_modifier_state & ModShift) {
goto_start (session->transport_rolling());
return;
}
@@ -258,13 +258,13 @@ Push2::button_page_left ()
void
Push2::button_right ()
{
- switch_bank (max (0, bank_start + 8));
+ _current_layout->button_right ();
}
void
Push2::button_left ()
{
- switch_bank (max (0, bank_start - 8));
+ _current_layout->button_left ();
}
void
@@ -331,45 +331,19 @@ Push2::button_clip ()
void
Push2::button_upper (uint32_t n)
{
- if (!stripable[n]) {
- return;
- }
-
- if (modifier_state & ModShift) {
- boost::shared_ptr<AutomationControl> sc = stripable[n]->rec_enable_control ();
- if (sc) {
- sc->set_value (!sc->get_value(), PBD::Controllable::UseGroup);
- }
- } else {
- boost::shared_ptr<SoloControl> sc = stripable[n]->solo_control ();
- if (sc) {
- sc->set_value (!sc->self_soloed(), PBD::Controllable::UseGroup);
- }
- }
+ _current_layout->button_upper (n);
}
void
Push2::button_lower (uint32_t n)
{
- if (!stripable[n]) {
- return;
- }
-
- if (modifier_state & ModSelect) {
- SetStripableSelection (stripable[n]);
- } else {
- boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
-
- if (mc) {
- mc->set_value (!mc->muted_by_self(), PBD::Controllable::UseGroup);
- }
- }
+ _current_layout->button_lower (n);
}
void
Push2::button_undo ()
{
- if (modifier_state & ModShift) {
+ if (_modifier_state & ModShift) {
ControlProtocol::Redo ();
} else {
ControlProtocol::Undo ();
@@ -379,56 +353,56 @@ Push2::button_undo ()
void
Push2::button_fwd32t ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (0+n);
}
void
Push2::button_fwd32 ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (1+n);
}
void
Push2::button_fwd16t ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (2+n);
}
void
Push2::button_fwd16 ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (3+n);
}
void
Push2::button_fwd8t ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (4+n);
}
void
Push2::button_fwd8 ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (5+n);
}
void
Push2::button_fwd4t ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (6+n);
}
void
Push2::button_fwd4 ()
{
- const int n = (modifier_state & ModShift) ? 8 : 0;
+ const int n = (_modifier_state & ModShift) ? 8 : 0;
goto_nth_marker (7+n);
}
@@ -466,93 +440,30 @@ Push2::button_shift_long_press ()
void
Push2::button_select_press ()
{
- start_select ();
+ cerr << "start select\n";
+ _modifier_state = ModifierState (_modifier_state | ModSelect);
+ Button* b = id_button_map[Select];
+ b->set_color (Push2::LED::White);
+ b->set_state (Push2::LED::Blinking16th);
+ write (b->state_msg());
+
+ _current_layout->button_select_press ();
}
void
Push2::button_select_release ()
{
- if (!(modifier_state & ModSelect)) {
- /* somebody else used us as a modifier */
- return;
- }
-
- end_select ();
-
- int selected = -1;
-
- for (int n = 0; n < 8; ++n) {
- if (stripable[n]) {
- if (stripable[n]->presentation_info().selected()) {
- selected = n;
- break;
- }
- }
+ if (_modifier_state & ModSelect) {
+ cerr << "end select\n";
+ _modifier_state = ModifierState (_modifier_state & ~(ModSelect));
+ Button* b = id_button_map[Select];
+ b->timeout_connection.disconnect ();
+ b->set_color (Push2::LED::White);
+ b->set_state (Push2::LED::OneShot24th);
+ write (b->state_msg());
}
- if (selected < 0) {
-
- /* no visible track selected, select first (if any) */
-
- if (stripable[0]) {
- SetStripableSelection (stripable[0]);
- }
-
- } else {
-
- if (modifier_state & ModShift) {
- std::cerr << "select prev\n";
- /* select prev */
-
- if (selected == 0) {
- /* current selected is leftmost ... cancel selection,
- switch banks by one, and select leftmost
- */
- if (bank_start != 0) {
- ClearStripableSelection ();
- switch_bank (bank_start-1);
- if (stripable[0]) {
- SetStripableSelection (stripable[0]);
- }
- }
- } else {
- /* select prev, if any */
- int n = selected - 1;
- while (n >= 0 && !stripable[n]) {
- --n;
- }
- if (n >= 0) {
- SetStripableSelection (stripable[n]);
- }
- }
-
- } else {
-
- std::cerr << "select next\n";
- /* select next */
-
- if (selected == 7) {
- /* current selected is rightmost ... cancel selection,
- switch banks by one, and select righmost
- */
- ToggleStripableSelection (stripable[selected]);
- switch_bank (bank_start+1);
- if (stripable[7]) {
- SetStripableSelection (stripable[7]);
- }
- } else {
- /* select next, if any */
- int n = selected + 1;
- while (n < 8 && !stripable[n]) {
- ++n;
- }
-
- if (n != 8) {
- SetStripableSelection (stripable[n]);
- }
- }
- }
- }
+ _current_layout->button_select_release ();
}
void
@@ -622,9 +533,9 @@ Push2::button_layout_press ()
void
Push2::button_scale_press ()
{
- if (current_menu != scale_menu) {
- show_scale_menu ();
+ if (_current_layout != scale_layout) {
+ _current_layout = scale_layout;
} else {
- cancel_menu ();
+ _current_layout = mix_layout;
}
}
diff --git a/libs/surfaces/push2/layout.cc b/libs/surfaces/push2/layout.cc
new file mode 100644
index 0000000000..e27ea0fe55
--- /dev/null
+++ b/libs/surfaces/push2/layout.cc
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2016 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "push2.h"
+
+using namespace ARDOUR;
+using namespace ArdourSurface;
+
+Push2Layout::Push2Layout (Push2& p, Session& s)
+ : p2 (p)
+ , session (s)
+{
+}
+
+Push2Layout::~Push2Layout ()
+{
+}
+
+bool
+Push2Layout::mapped () const
+{
+ return p2.current_layout() == this;
+}
diff --git a/libs/surfaces/push2/mix.cc b/libs/surfaces/push2/mix.cc
new file mode 100644
index 0000000000..a4bebf463a
--- /dev/null
+++ b/libs/surfaces/push2/mix.cc
@@ -0,0 +1,570 @@
+/*
+ Copyright (C) 2016 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <pangomm/layout.h>
+
+#include "pbd/compose.h"
+#include "pbd/convert.h"
+#include "pbd/debug.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
+#include "pbd/enumwriter.h"
+
+#include "midi++/parser.h"
+#include "timecode/time.h"
+#include "timecode/bbt_time.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/debug.h"
+#include "ardour/filesystem_paths.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/midi_track.h"
+#include "ardour/midi_port.h"
+#include "ardour/session.h"
+#include "ardour/tempo.h"
+
+#include "push2.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+using namespace Glib;
+using namespace ArdourSurface;
+
+MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
+ : Push2Layout (p, s)
+ , bank_start (0)
+{
+ tc_clock_layout = Pango::Layout::create (context);
+ bbt_clock_layout = Pango::Layout::create (context);
+
+ Pango::FontDescription fd ("Sans Bold 24");
+ tc_clock_layout->set_font_description (fd);
+ bbt_clock_layout->set_font_description (fd);
+
+ Pango::FontDescription fd2 ("Sans 10");
+ for (int n = 0; n < 8; ++n) {
+ upper_layout[n] = Pango::Layout::create (context);
+ upper_layout[n]->set_font_description (fd2);
+ upper_layout[n]->set_text ("solo");
+ lower_layout[n] = Pango::Layout::create (context);
+ lower_layout[n]->set_font_description (fd2);
+ lower_layout[n]->set_text ("mute");
+ }
+
+ Pango::FontDescription fd3 ("Sans Bold 10");
+ for (int n = 0; n < 8; ++n) {
+ mid_layout[n] = Pango::Layout::create (context);
+ mid_layout[n]->set_font_description (fd3);
+ }
+
+ switch_bank (0);
+}
+
+MixLayout::~MixLayout ()
+{
+}
+
+bool
+MixLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
+{
+ framepos_t audible = session.audible_frame();
+ Timecode::Time TC;
+ bool negative = false;
+ string tc_clock_text;
+ string bbt_clock_text;
+
+ if (audible < 0) {
+ audible = -audible;
+ negative = true;
+ }
+
+ session.timecode_time (audible, TC);
+
+ TC.negative = TC.negative || negative;
+
+ tc_clock_text = Timecode::timecode_format_time(TC);
+
+ Timecode::BBT_Time bbt = session.tempo_map().bbt_at_frame (audible);
+ char buf[16];
+
+#define BBT_BAR_CHAR "|"
+
+ if (negative) {
+ snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
+ bbt.bars, bbt.beats, bbt.ticks);
+ } else {
+ snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
+ bbt.bars, bbt.beats, bbt.ticks);
+ }
+
+ bbt_clock_text = buf;
+
+ bool dirty = false;
+
+ if (tc_clock_text != tc_clock_layout->get_text()) {
+ dirty = true;
+ tc_clock_layout->set_text (tc_clock_text);
+ }
+
+ if (bbt_clock_text != tc_clock_layout->get_text()) {
+ dirty = true;
+ bbt_clock_layout->set_text (bbt_clock_text);
+ }
+
+ string mid_text;
+
+ for (int n = 0; n < 8; ++n) {
+ if (stripable[n]) {
+ mid_text = short_version (stripable[n]->name(), 10);
+ if (mid_text != mid_layout[n]->get_text()) {
+ mid_layout[n]->set_text (mid_text);
+ dirty = true;
+ }
+ }
+ }
+
+ if (!dirty) {
+ return false;
+ }
+
+ context->set_source_rgb (0.764, 0.882, 0.882);
+ context->rectangle (0, 0, 960, 160);
+ context->fill ();
+
+ /* clocks */
+
+ context->set_source_rgb (0.23, 0.0, 0.349);
+ context->move_to (650, 25);
+ tc_clock_layout->update_from_cairo_context (context);
+ tc_clock_layout->show_in_cairo_context (context);
+ context->move_to (650, 60);
+ bbt_clock_layout->update_from_cairo_context (context);
+ bbt_clock_layout->show_in_cairo_context (context);
+
+ for (int n = 0; n < 8; ++n) {
+ context->move_to (10 + (n*120), 2);
+ upper_layout[n]->update_from_cairo_context (context);
+ upper_layout[n]->show_in_cairo_context (context);
+ }
+
+ for (int n = 0; n < 8; ++n) {
+ context->move_to (10 + (n*120), 140);
+ lower_layout[n]->update_from_cairo_context (context);
+ lower_layout[n]->show_in_cairo_context (context);
+ }
+
+ for (int n = 0; n < 8; ++n) {
+ if (stripable[n] && stripable[n]->presentation_info().selected()) {
+ context->rectangle (10 + (n*120) - 5, 115, 120, 22);
+ context->set_source_rgb (1.0, 0.737, 0.172);
+ context->fill();
+ }
+ context->set_source_rgb (0.0, 0.0, 0.0);
+ context->move_to (10 + (n*120), 120);
+ mid_layout[n]->update_from_cairo_context (context);
+ mid_layout[n]->show_in_cairo_context (context);
+ }
+
+ return true;
+}
+
+void
+MixLayout::button_upper (uint32_t n)
+{
+ if (!stripable[n]) {
+ return;
+ }
+
+ if (p2.modifier_state() & Push2::ModShift) {
+ boost::shared_ptr<AutomationControl> sc = stripable[n]->rec_enable_control ();
+ if (sc) {
+ sc->set_value (!sc->get_value(), PBD::Controllable::UseGroup);
+ }
+ } else {
+ boost::shared_ptr<SoloControl> sc = stripable[n]->solo_control ();
+ if (sc) {
+ sc->set_value (!sc->self_soloed(), PBD::Controllable::UseGroup);
+ }
+ }
+}
+
+void
+MixLayout::button_lower (uint32_t n)
+{
+ if (!stripable[n]) {
+ return;
+ }
+
+ if (p2.modifier_state() & Push2::ModSelect) {
+ ControlProtocol::SetStripableSelection (stripable[n]);
+ } else {
+ boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
+
+ if (mc) {
+ mc->set_value (!mc->muted_by_self(), PBD::Controllable::UseGroup);
+ }
+ }
+}
+
+void
+MixLayout::strip_vpot (int n, int delta)
+{
+ if (stripable[n]) {
+ boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
+ if (ac) {
+ ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
+ }
+ }
+}
+
+void
+MixLayout::strip_vpot_touch (int n, bool touching)
+{
+ if (stripable[n]) {
+ boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
+ if (ac) {
+ if (touching) {
+ ac->start_touch (session.audible_frame());
+ } else {
+ ac->stop_touch (true, session.audible_frame());
+ }
+ }
+ }
+}
+
+void
+MixLayout::stripable_property_change (PropertyChange const& what_changed, int which)
+{
+ if (what_changed.contains (Properties::selected)) {
+ if (!stripable[which]) {
+ return;
+ }
+
+ /* cancel string, which will cause a redraw on the next update
+ * cycle. The redraw will reflect selected status
+ */
+
+ mid_layout[which]->set_text (string());
+ }
+}
+
+
+void
+MixLayout::solo_change (int n)
+{
+ Push2::ButtonID bid;
+
+ switch (n) {
+ case 0:
+ bid = Push2::Upper1;
+ break;
+ case 1:
+ bid = Push2::Upper2;
+ break;
+ case 2:
+ bid = Push2::Upper3;
+ break;
+ case 3:
+ bid = Push2::Upper4;
+ break;
+ case 4:
+ bid = Push2::Upper5;
+ break;
+ case 5:
+ bid = Push2::Upper6;
+ break;
+ case 6:
+ bid = Push2::Upper7;
+ break;
+ case 7:
+ bid = Push2::Upper8;
+ break;
+ default:
+ return;
+ }
+
+ boost::shared_ptr<SoloControl> ac = stripable[n]->solo_control ();
+ if (!ac) {
+ return;
+ }
+
+ Push2::Button* b = p2.button_by_id (bid);
+
+ if (ac->soloed()) {
+ b->set_color (Push2::LED::Green);
+ } else {
+ b->set_color (Push2::LED::Black);
+ }
+
+ if (ac->soloed_by_others_upstream() || ac->soloed_by_others_downstream()) {
+ b->set_state (Push2::LED::Blinking4th);
+ } else {
+ b->set_state (Push2::LED::OneShot24th);
+ }
+
+ p2.write (b->state_msg());
+}
+
+void
+MixLayout::mute_change (int n)
+{
+ Push2::ButtonID bid;
+
+ if (!stripable[n]) {
+ return;
+ }
+
+ cerr << "Mute changed on " << n << ' ' << stripable[n]->name() << endl;
+
+ switch (n) {
+ case 0:
+ bid = Push2::Lower1;
+ break;
+ case 1:
+ bid = Push2::Lower2;
+ break;
+ case 2:
+ bid = Push2::Lower3;
+ break;
+ case 3:
+ bid = Push2::Lower4;
+ break;
+ case 4:
+ bid = Push2::Lower5;
+ break;
+ case 5:
+ bid = Push2::Lower6;
+ break;
+ case 6:
+ bid = Push2::Lower7;
+ break;
+ case 7:
+ bid = Push2::Lower8;
+ break;
+ default:
+ return;
+ }
+
+ boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
+
+ if (!mc) {
+ return;
+ }
+
+ Push2::Button* b = p2.button_by_id (bid);
+
+ if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
+
+ if (mc->muted_by_self ()) {
+ /* full mute */
+ b->set_color (Push2::LED::Blue);
+ b->set_state (Push2::LED::OneShot24th);
+ cerr << "FULL MUTE1\n";
+ } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
+ /* this will reflect both solo mutes AND master mutes */
+ b->set_color (Push2::LED::Blue);
+ b->set_state (Push2::LED::Blinking4th);
+ cerr << "OTHER MUTE1\n";
+ } else {
+ /* no mute at all */
+ b->set_color (Push2::LED::Black);
+ b->set_state (Push2::LED::OneShot24th);
+ cerr << "NO MUTE1\n";
+ }
+
+ } else {
+
+ if (mc->muted_by_self()) {
+ /* full mute */
+ b->set_color (Push2::LED::Blue);
+ b->set_state (Push2::LED::OneShot24th);
+ cerr << "FULL MUTE2\n";
+ } else if (mc->muted_by_masters ()) {
+ /* this shows only master mutes, not mute-by-others-soloing */
+ b->set_color (Push2::LED::Blue);
+ b->set_state (Push2::LED::Blinking4th);
+ cerr << "OTHER MUTE1\n";
+ } else {
+ /* no mute at all */
+ b->set_color (Push2::LED::Black);
+ b->set_state (Push2::LED::OneShot24th);
+ cerr << "NO MUTE2\n";
+ }
+ }
+
+ p2.write (b->state_msg());
+}
+
+void
+MixLayout::switch_bank (uint32_t base)
+{
+ stripable_connections.drop_connections ();
+
+ /* try to get the first stripable for the requested bank */
+
+ stripable[0] = session.get_remote_nth_stripable (base, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+
+ if (!stripable[0]) {
+ return;
+ }
+
+ /* at least one stripable in this bank */
+ bank_start = base;
+
+ stripable[1] = session.get_remote_nth_stripable (base+1, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+ stripable[2] = session.get_remote_nth_stripable (base+2, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+ stripable[3] = session.get_remote_nth_stripable (base+3, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+ stripable[4] = session.get_remote_nth_stripable (base+4, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+ stripable[5] = session.get_remote_nth_stripable (base+5, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+ stripable[6] = session.get_remote_nth_stripable (base+6, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+ stripable[7] = session.get_remote_nth_stripable (base+7, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+
+
+ for (int n = 0; n < 8; ++n) {
+ if (!stripable[n]) {
+ continue;
+ }
+
+ /* stripable goes away? refill the bank, starting at the same point */
+
+ stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
+ boost::shared_ptr<AutomationControl> sc = stripable[n]->solo_control();
+ if (sc) {
+ sc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::solo_change, this, n), &p2);
+ }
+
+ boost::shared_ptr<AutomationControl> mc = stripable[n]->mute_control();
+ if (mc) {
+ mc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::mute_change, this, n), &p2);
+ }
+
+ stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
+
+ solo_change (n);
+ mute_change (n);
+
+ }
+}
+
+void
+MixLayout::button_right ()
+{
+ switch_bank (max (0, bank_start + 8));
+}
+
+void
+MixLayout::button_left ()
+{
+ switch_bank (max (0, bank_start - 8));
+}
+
+void
+MixLayout::button_select_press ()
+{
+}
+
+void
+MixLayout::button_select_release ()
+{
+ if (!(p2.modifier_state() & Push2::ModSelect)) {
+ /* somebody else used us as a modifier */
+ return;
+ }
+
+ int selected = -1;
+
+ for (int n = 0; n < 8; ++n) {
+ if (stripable[n]) {
+ if (stripable[n]->presentation_info().selected()) {
+ selected = n;
+ break;
+ }
+ }
+ }
+
+ if (selected < 0) {
+
+ /* no visible track selected, select first (if any) */
+
+ if (stripable[0]) {
+ ControlProtocol::SetStripableSelection (stripable[0]);
+ }
+
+ } else {
+
+ if (p2.modifier_state() & Push2::ModShift) {
+ std::cerr << "select prev\n";
+ /* select prev */
+
+ if (selected == 0) {
+ /* current selected is leftmost ... cancel selection,
+ switch banks by one, and select leftmost
+ */
+ if (bank_start != 0) {
+ ControlProtocol::ClearStripableSelection ();
+ switch_bank (bank_start-1);
+ if (stripable[0]) {
+ ControlProtocol::SetStripableSelection (stripable[0]);
+ }
+ }
+ } else {
+ /* select prev, if any */
+ int n = selected - 1;
+ while (n >= 0 && !stripable[n]) {
+ --n;
+ }
+ if (n >= 0) {
+ ControlProtocol::SetStripableSelection (stripable[n]);
+ }
+ }
+
+ } else {
+
+ std::cerr << "select next\n";
+ /* select next */
+
+ if (selected == 7) {
+ /* current selected is rightmost ... cancel selection,
+ switch banks by one, and select righmost
+ */
+ ControlProtocol::ToggleStripableSelection (stripable[selected]);
+ switch_bank (bank_start+1);
+ if (stripable[7]) {
+ ControlProtocol::SetStripableSelection (stripable[7]);
+ }
+ } else {
+ /* select next, if any */
+ int n = selected + 1;
+ while (n < 8 && !stripable[n]) {
+ ++n;
+ }
+
+ if (n != 8) {
+ ControlProtocol::SetStripableSelection (stripable[n]);
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/libs/surfaces/push2/push2.cc b/libs/surfaces/push2/push2.cc
index d2e4be3fde..0a00ba8d95 100644
--- a/libs/surfaces/push2/push2.cc
+++ b/libs/surfaces/push2/push2.cc
@@ -1,25 +1,21 @@
/*
- Copyright (C) 2016 Paul Davis
+ Copyright (C) 2016 Paul Davis
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <cairomm/context.h>
-#include <cairomm/surface.h>
-#include <pangomm/layout.h>
-
#include "pbd/compose.h"
#include "pbd/convert.h"
#include "pbd/debug.h"
@@ -46,14 +42,14 @@
#include "gui.h"
#include "menu.h"
+#include "i18n.h"
+
using namespace ARDOUR;
using namespace std;
using namespace PBD;
using namespace Glib;
using namespace ArdourSurface;
-#include "i18n.h"
-
#include "pbd/abstract_ui.cc" // instantiate template
const int Push2::cols = 960;
@@ -122,9 +118,10 @@ Push2::Push2 (ARDOUR::Session& s)
, handle (0)
, device_buffer (0)
, frame_buffer (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, cols, rows))
- , modifier_state (None)
+ , _modifier_state (None)
, splash_start (0)
- , bank_start (0)
+ , _current_layout (0)
+ , drawn_layout (0)
, connection_state (ConnectionState (0))
, gui (0)
, _mode (MusicalMode::IonianMajor)
@@ -133,42 +130,26 @@ Push2::Push2 (ARDOUR::Session& s)
, _in_key (true)
, octave_shift (0)
, percussion (false)
- , current_menu (0)
- , drawn_menu (0)
{
context = Cairo::Context::create (frame_buffer);
- tc_clock_layout = Pango::Layout::create (context);
- bbt_clock_layout = Pango::Layout::create (context);
-
- Pango::FontDescription fd ("Sans Bold 24");
- tc_clock_layout->set_font_description (fd);
- bbt_clock_layout->set_font_description (fd);
-
- Pango::FontDescription fd2 ("Sans 10");
- for (int n = 0; n < 8; ++n) {
- upper_layout[n] = Pango::Layout::create (context);
- upper_layout[n]->set_font_description (fd2);
- upper_layout[n]->set_text ("solo");
- lower_layout[n] = Pango::Layout::create (context);
- lower_layout[n]->set_font_description (fd2);
- lower_layout[n]->set_text ("mute");
- }
-
- Pango::FontDescription fd3 ("Sans Bold 10");
- for (int n = 0; n < 8; ++n) {
- mid_layout[n] = Pango::Layout::create (context);
- mid_layout[n]->set_font_description (fd3);
- }
build_pad_table ();
build_maps ();
- build_scale_menu ();
+
+ /* master cannot be removed, so no need to connect to going-away signal */
+ master = session->master_out ();
if (open ()) {
throw failed_constructor ();
}
- StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
+ ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
+
+ /* catch current selection, if any */
+ {
+ StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
+ stripable_selection_change (sp);
+ }
/* catch arrival and departure of Push2 itself */
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
@@ -281,6 +262,10 @@ Push2::open ()
connect_to_parser ();
+ mix_layout = new MixLayout (*this, *session, context);
+ scale_layout = new ScaleLayout (*this, *session, context);
+ _current_layout = mix_layout;
+
return 0;
}
@@ -317,7 +302,6 @@ Push2::close ()
vblank_connection.disconnect ();
periodic_connection.disconnect ();
session_connections.drop_connections ();
- stripable_connections.drop_connections ();
if (handle) {
libusb_release_interface (handle, 0x00);
@@ -325,10 +309,6 @@ Push2::close ()
handle = 0;
}
- for (int n = 0; n < 8; ++n) {
- stripable[n].reset ();
- }
-
delete [] device_frame_buffer;
device_frame_buffer = 0;
@@ -509,133 +489,34 @@ Push2::blit_to_device_frame_buffer ()
bool
Push2::redraw ()
{
- string tc_clock_text;
- string bbt_clock_text;
-
if (splash_start) {
- if (get_microseconds() - splash_start > 4000000) {
- splash_start = 0;
- } else {
- return false;
- }
- }
-
- if (current_menu) {
- if (current_menu->dirty() || drawn_menu != current_menu) {
- /* fill background */
- context->set_source_rgb (0.764, 0.882, 0.882);
- context->rectangle (0, 0, 960, 160);
- context->fill ();
- /* now menu */
- current_menu->redraw (context);
- drawn_menu = current_menu;
- return true;
- }
- return false;
- } else {
- drawn_menu = 0;
- }
-
- if (session) {
- framepos_t audible = session->audible_frame();
- Timecode::Time TC;
- bool negative = false;
-
- if (audible < 0) {
- audible = -audible;
- negative = true;
- }
- session->timecode_time (audible, TC);
+ /* display splash for 3 seconds */
- TC.negative = TC.negative || negative;
-
- tc_clock_text = Timecode::timecode_format_time(TC);
-
- Timecode::BBT_Time bbt = session->tempo_map().bbt_at_frame (audible);
- char buf[16];
-
-#define BBT_BAR_CHAR "|"
-
- if (negative) {
- snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
- bbt.bars, bbt.beats, bbt.ticks);
+ if (get_microseconds() - splash_start > 3000000) {
+ splash_start = 0;
} else {
- snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
- bbt.bars, bbt.beats, bbt.ticks);
+ return false;
}
-
- bbt_clock_text = buf;
}
- bool dirty = false;
+ Glib::Threads::Mutex::Lock lm (layout_lock, Glib::Threads::TRY_LOCK);
- if (tc_clock_text != tc_clock_layout->get_text()) {
- dirty = true;
- tc_clock_layout->set_text (tc_clock_text);
- }
-
- if (bbt_clock_text != tc_clock_layout->get_text()) {
- dirty = true;
- bbt_clock_layout->set_text (bbt_clock_text);
- }
-
- string mid_text;
-
- for (int n = 0; n < 8; ++n) {
- if (stripable[n]) {
- mid_text = short_version (stripable[n]->name(), 10);
- if (mid_text != mid_layout[n]->get_text()) {
- mid_layout[n]->set_text (mid_text);
- dirty = true;
- }
- }
- }
-
- if (!dirty) {
+ if (!lm.locked()) {
+ /* can't get layout, no re-render needed */
return false;
}
- context->set_source_rgb (0.764, 0.882, 0.882);
- context->rectangle (0, 0, 960, 160);
- context->fill ();
- context->set_source_rgb (0.23, 0.0, 0.349);
- context->move_to (650, 25);
- tc_clock_layout->update_from_cairo_context (context);
- tc_clock_layout->show_in_cairo_context (context);
- context->move_to (650, 60);
- bbt_clock_layout->update_from_cairo_context (context);
- bbt_clock_layout->show_in_cairo_context (context);
-
- for (int n = 0; n < 8; ++n) {
- context->move_to (10 + (n*120), 2);
- upper_layout[n]->update_from_cairo_context (context);
- upper_layout[n]->show_in_cairo_context (context);
- }
-
- for (int n = 0; n < 8; ++n) {
- context->move_to (10 + (n*120), 140);
- lower_layout[n]->update_from_cairo_context (context);
- lower_layout[n]->show_in_cairo_context (context);
- }
-
- for (int n = 0; n < 8; ++n) {
- if (stripable[n] && stripable[n]->presentation_info().selected()) {
- context->rectangle (10 + (n*120) - 5, 115, 120, 22);
- context->set_source_rgb (1.0, 0.737, 0.172);
- context->fill();
- }
- context->set_source_rgb (0.0, 0.0, 0.0);
- context->move_to (10 + (n*120), 120);
- mid_layout[n]->update_from_cairo_context (context);
- mid_layout[n]->show_in_cairo_context (context);
+ bool render_needed = false;
+
+ if (drawn_layout != _current_layout) {
+ render_needed = true;
}
- /* render clock */
- /* render foo */
- /* render bar */
+ bool dirty = _current_layout->redraw (context);
+ drawn_layout = _current_layout;
- return true;
+ return dirty || render_needed;
}
bool
@@ -709,14 +590,8 @@ Push2::set_active (bool yn)
init_buttons (true);
init_touch_strip ();
set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
- switch_bank (0);
splash ();
- /* catch current selection, if any */
- {
- StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
- stripable_selection_change (sp);
- }
} else {
@@ -861,33 +736,33 @@ Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
switch (ev->controller_number) {
case 71:
- strip_vpot (0, delta);
+ _current_layout->strip_vpot (0, delta);
break;
case 72:
- strip_vpot (1, delta);
+ _current_layout->strip_vpot (1, delta);
break;
case 73:
- strip_vpot (2, delta);
+ _current_layout->strip_vpot (2, delta);
break;
case 74:
- strip_vpot (3, delta);
+ _current_layout->strip_vpot (3, delta);
break;
case 75:
- strip_vpot (4, delta);
+ _current_layout->strip_vpot (4, delta);
break;
case 76:
- strip_vpot (5, delta);
+ _current_layout->strip_vpot (5, delta);
break;
case 77:
- strip_vpot (6, delta);
+ _current_layout->strip_vpot (6, delta);
break;
case 78:
- strip_vpot (7, delta);
+ _current_layout->strip_vpot (7, delta);
break;
/* left side pair */
case 14:
- strip_vpot (8, delta);
+ other_vpot (8, delta);
break;
case 15:
other_vpot (1, delta);
@@ -913,28 +788,28 @@ Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* e
switch (ev->note_number) {
case 0:
- strip_vpot_touch (0, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (0, ev->velocity > 64);
break;
case 1:
- strip_vpot_touch (1, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (1, ev->velocity > 64);
break;
case 2:
- strip_vpot_touch (2, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (2, ev->velocity > 64);
break;
case 3:
- strip_vpot_touch (3, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (3, ev->velocity > 64);
break;
case 4:
- strip_vpot_touch (4, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (4, ev->velocity > 64);
break;
case 5:
- strip_vpot_touch (5, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (5, ev->velocity > 64);
break;
case 6:
- strip_vpot_touch (6, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (6, ev->velocity > 64);
break;
case 7:
- strip_vpot_touch (7, ev->velocity > 64);
+ _current_layout->strip_vpot_touch (7, ev->velocity > 64);
break;
/* left side */
@@ -1234,317 +1109,6 @@ Push2::set_state (const XMLNode & node, int version)
}
void
-Push2::switch_bank (uint32_t base)
-{
- if (!session) {
- return;
- }
-
- stripable_connections.drop_connections ();
-
- /* try to get the first stripable for the requested bank */
-
- stripable[0] = session->get_remote_nth_stripable (base, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-
- if (!stripable[0]) {
- return;
- }
-
- /* at least one stripable in this bank */
- bank_start = base;
-
- stripable[1] = session->get_remote_nth_stripable (base+1, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
- stripable[2] = session->get_remote_nth_stripable (base+2, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
- stripable[3] = session->get_remote_nth_stripable (base+3, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
- stripable[4] = session->get_remote_nth_stripable (base+4, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
- stripable[5] = session->get_remote_nth_stripable (base+5, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
- stripable[6] = session->get_remote_nth_stripable (base+6, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
- stripable[7] = session->get_remote_nth_stripable (base+7, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-
-
- for (int n = 0; n < 8; ++n) {
- if (!stripable[n]) {
- continue;
- }
-
- /* stripable goes away? refill the bank, starting at the same point */
-
- stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::switch_bank, this, bank_start), this);
- boost::shared_ptr<AutomationControl> sc = stripable[n]->solo_control();
- if (sc) {
- sc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::solo_change, this, n), this);
- }
-
- boost::shared_ptr<AutomationControl> mc = stripable[n]->mute_control();
- if (mc) {
- mc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::mute_change, this, n), this);
- }
-
- stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_property_change, this, _1, n), this);
-
- solo_change (n);
- mute_change (n);
-
- }
-
- /* master cannot be removed, so no need to connect to going-away signal */
- master = session->master_out ();
-}
-
-void
-Push2::stripable_property_change (PropertyChange const& what_changed, int which)
-{
- if (what_changed.contains (Properties::selected)) {
- if (!stripable[which]) {
- return;
- }
-
- /* cancel string, which will cause a redraw on the next update
- * cycle. The redraw will reflect selected status
- */
-
- mid_layout[which]->set_text (string());
- }
-}
-
-void
-Push2::stripable_selection_change (StripableNotificationListPtr selected)
-{
-
- boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
- boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
- boost::shared_ptr<MidiTrack> new_pad_target;
-
- /* See if there's a MIDI track selected */
-
- for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
-
- new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
-
- if (new_pad_target) {
- break;
- }
- }
-
- if (new_pad_target) {
- cerr << "new midi pad target " << new_pad_target->name() << endl;
- } else {
- cerr << "no midi pad target\n";
- }
-
- if (current_midi_track == new_pad_target) {
- /* nothing to do */
- return;
- }
-
- if (!new_pad_target) {
- /* leave existing connection alone */
- return;
- }
-
- /* disconnect from pad port, if appropriate */
-
- if (current_midi_track && pad_port) {
- cerr << "Disconnect pads from " << current_midi_track->name() << endl;
- current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
- }
-
- /* now connect the pad port to this (newly) selected midi
- * track, if indeed there is one.
- */
-
- if (new_pad_target && pad_port) {
- cerr << "Reconnect pads to " << new_pad_target->name() << endl;
- new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
- current_pad_target = new_pad_target;
- } else {
- current_pad_target.reset ();
- }
-}
-
-
-void
-Push2::solo_change (int n)
-{
- ButtonID bid;
-
- switch (n) {
- case 0:
- bid = Upper1;
- break;
- case 1:
- bid = Upper2;
- break;
- case 2:
- bid = Upper3;
- break;
- case 3:
- bid = Upper4;
- break;
- case 4:
- bid = Upper5;
- break;
- case 5:
- bid = Upper6;
- break;
- case 6:
- bid = Upper7;
- break;
- case 7:
- bid = Upper8;
- break;
- default:
- return;
- }
-
- boost::shared_ptr<SoloControl> ac = stripable[n]->solo_control ();
- if (!ac) {
- return;
- }
-
- Button* b = id_button_map[bid];
-
- if (ac->soloed()) {
- b->set_color (LED::Green);
- } else {
- b->set_color (LED::Black);
- }
-
- if (ac->soloed_by_others_upstream() || ac->soloed_by_others_downstream()) {
- b->set_state (LED::Blinking4th);
- } else {
- b->set_state (LED::OneShot24th);
- }
-
- write (b->state_msg());
-}
-
-void
-Push2::mute_change (int n)
-{
- ButtonID bid;
-
- if (!stripable[n]) {
- return;
- }
-
- cerr << "Mute changed on " << n << ' ' << stripable[n]->name() << endl;
-
- switch (n) {
- case 0:
- bid = Lower1;
- break;
- case 1:
- bid = Lower2;
- break;
- case 2:
- bid = Lower3;
- break;
- case 3:
- bid = Lower4;
- break;
- case 4:
- bid = Lower5;
- break;
- case 5:
- bid = Lower6;
- break;
- case 6:
- bid = Lower7;
- break;
- case 7:
- bid = Lower8;
- break;
- default:
- return;
- }
-
- boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
-
- if (!mc) {
- return;
- }
-
- Button* b = id_button_map[bid];
-
- if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
-
- if (mc->muted_by_self ()) {
- /* full mute */
- b->set_color (LED::Blue);
- b->set_state (LED::OneShot24th);
- cerr << "FULL MUTE1\n";
- } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
- /* this will reflect both solo mutes AND master mutes */
- b->set_color (LED::Blue);
- b->set_state (LED::Blinking4th);
- cerr << "OTHER MUTE1\n";
- } else {
- /* no mute at all */
- b->set_color (LED::Black);
- b->set_state (LED::OneShot24th);
- cerr << "NO MUTE1\n";
- }
-
- } else {
-
- if (mc->muted_by_self()) {
- /* full mute */
- b->set_color (LED::Blue);
- b->set_state (LED::OneShot24th);
- cerr << "FULL MUTE2\n";
- } else if (mc->muted_by_masters ()) {
- /* this shows only master mutes, not mute-by-others-soloing */
- b->set_color (LED::Blue);
- b->set_state (LED::Blinking4th);
- cerr << "OTHER MUTE1\n";
- } else {
- /* no mute at all */
- b->set_color (LED::Black);
- b->set_state (LED::OneShot24th);
- cerr << "NO MUTE2\n";
- }
- }
-
- write (b->state_msg());
-}
-
-void
-Push2::strip_vpot (int n, int delta)
-{
- if (current_menu) {
- current_menu->step_active (n, delta);
- return;
- }
-
- if (stripable[n]) {
- boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
- if (ac) {
- ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
- }
- }
-}
-
-void
-Push2::strip_vpot_touch (int n, bool touching)
-{
- if (current_menu) {
- return;
- }
-
- if (stripable[n]) {
- boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
- if (ac) {
- if (touching) {
- ac->start_touch (session->audible_frame());
- } else {
- ac->stop_touch (true, session->audible_frame());
- }
- }
- }
-}
-
-void
Push2::other_vpot (int n, int delta)
{
switch (n) {
@@ -1590,7 +1154,7 @@ void
Push2::start_shift ()
{
cerr << "start shift\n";
- modifier_state = ModifierState (modifier_state | ModShift);
+ _modifier_state = ModifierState (_modifier_state | ModShift);
Button* b = id_button_map[Shift];
b->set_color (LED::White);
b->set_state (LED::Blinking16th);
@@ -1600,9 +1164,9 @@ Push2::start_shift ()
void
Push2::end_shift ()
{
- if (modifier_state & ModShift) {
+ if (_modifier_state & ModShift) {
cerr << "end shift\n";
- modifier_state = ModifierState (modifier_state & ~(ModShift));
+ _modifier_state = ModifierState (_modifier_state & ~(ModShift));
Button* b = id_button_map[Shift];
b->timeout_connection.disconnect ();
b->set_color (LED::White);
@@ -1612,31 +1176,6 @@ Push2::end_shift ()
}
void
-Push2::start_select ()
-{
- cerr << "start select\n";
- modifier_state = ModifierState (modifier_state | ModSelect);
- Button* b = id_button_map[Select];
- b->set_color (LED::White);
- b->set_state (LED::Blinking16th);
- write (b->state_msg());
-}
-
-void
-Push2::end_select ()
-{
- if (modifier_state & ModSelect) {
- cerr << "end select\n";
- modifier_state = ModifierState (modifier_state & ~(ModSelect));
- Button* b = id_button_map[Select];
- b->timeout_connection.disconnect ();
- b->set_color (LED::White);
- b->set_state (LED::OneShot24th);
- write (b->state_msg());
- }
-}
-
-void
Push2::splash ()
{
std::string splash_file;
@@ -2005,72 +1544,69 @@ Push2::set_percussive_mode (bool yn)
PadChange (); /* EMIT SIGNAL */
}
-void
-Push2::set_menu (Push2Menu* m)
+Push2Layout*
+Push2::current_layout () const
{
- current_menu = m;
- drawn_menu = 0;
+ Glib::Threads::Mutex::Lock lm (layout_lock);
+ return _current_layout;
}
void
-Push2::build_scale_menu ()
+Push2::stripable_selection_change (StripableNotificationListPtr selected)
{
- vector<string> v;
-
- scale_menu = new Push2Menu (context);
-
- v.push_back ("Dorian");
- v.push_back ("IonianMajor");
- v.push_back ("Minor");
- v.push_back ("HarmonicMinor");
- v.push_back ("MelodicMinorAscending");
- v.push_back ("MelodicMinorDescending");
- v.push_back ("Phrygian");
- v.push_back ("Lydian");
- v.push_back ("Mixolydian");
- v.push_back ("Aeolian");
- v.push_back ("Locrian");
- v.push_back ("PentatonicMajor");
- v.push_back ("PentatonicMinor");
- v.push_back ("Chromatic");
- v.push_back ("BluesScale");
- v.push_back ("NeapolitanMinor");
- v.push_back ("NeapolitanMajor");
- v.push_back ("Oriental");
- v.push_back ("DoubleHarmonic");
- v.push_back ("Enigmatic");
- v.push_back ("Hirajoshi");
- v.push_back ("HungarianMinor");
- v.push_back ("HungarianMajor");
- v.push_back ("Kumoi");
- v.push_back ("Iwato");
- v.push_back ("Hindu");
- v.push_back ("Spanish8Tone");
- v.push_back ("Pelog");
- v.push_back ("HungarianGypsy");
- v.push_back ("Overtone");
- v.push_back ("LeadingWholeTone");
- v.push_back ("Arabian");
- v.push_back ("Balinese");
- v.push_back ("Gypsy");
- v.push_back ("Mohammedan");
- v.push_back ("Javanese");
- v.push_back ("Persian");
- v.push_back ("Algeria");
-
- scale_menu->fill_column (0, v);
-
- v.clear ();
-}
+ boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
+ boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
+ boost::shared_ptr<MidiTrack> new_pad_target;
-void
-Push2::show_scale_menu ()
-{
- set_menu (scale_menu);
+ /* See if there's a MIDI track selected */
+
+ for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
+
+ new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
+
+ if (new_pad_target) {
+ break;
+ }
+ }
+
+ if (new_pad_target) {
+ cerr << "new midi pad target " << new_pad_target->name() << endl;
+ } else {
+ cerr << "no midi pad target\n";
+ }
+
+ if (current_midi_track == new_pad_target) {
+ /* nothing to do */
+ return;
+ }
+
+ if (!new_pad_target) {
+ /* leave existing connection alone */
+ return;
+ }
+
+ /* disconnect from pad port, if appropriate */
+
+ if (current_midi_track && pad_port) {
+ cerr << "Disconnect pads from " << current_midi_track->name() << endl;
+ current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
+ }
+
+ /* now connect the pad port to this (newly) selected midi
+ * track, if indeed there is one.
+ */
+
+ if (new_pad_target && pad_port) {
+ cerr << "Reconnect pads to " << new_pad_target->name() << endl;
+ new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
+ current_pad_target = new_pad_target;
+ } else {
+ current_pad_target.reset ();
+ }
}
-void
-Push2::cancel_menu ()
+Push2::Button*
+Push2::button_by_id (ButtonID bid)
{
- set_menu (0);
+ return id_button_map[bid];
}
diff --git a/libs/surfaces/push2/push2.h b/libs/surfaces/push2/push2.h
index 4ced88b67f..e3fe721359 100644
--- a/libs/surfaces/push2/push2.h
+++ b/libs/surfaces/push2/push2.h
@@ -72,72 +72,12 @@ public:
class P2GUI;
class Push2Menu;
+class Push2Layout;
class Push2 : public ARDOUR::ControlProtocol
, public AbstractUI<Push2Request>
{
- public:
- Push2 (ARDOUR::Session&);
- ~Push2 ();
-
- static bool probe ();
- static void* request_factory (uint32_t);
-
- std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
-
- bool has_editor () const { return true; }
- void* get_gui () const;
- void tear_down_gui ();
-
- int set_active (bool yn);
- XMLNode& get_state();
- int set_state (const XMLNode & node, int version);
-
- PBD::Signal0<void> ConnectionChange;
-
- boost::shared_ptr<ARDOUR::Port> input_port();
- boost::shared_ptr<ARDOUR::Port> output_port();
-
- int pad_note (int row, int col) const;
- PBD::Signal0<void> PadChange;
-
- void set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey);
-
- MusicalMode::Type mode() const { return _mode; }
- int scale_root() const { return _scale_root; }
- int root_octave() const { return _root_octave; }
- bool in_key() const { return _in_key; }
-
- static const int cols;
- static const int rows;
-
- private:
- libusb_device_handle *handle;
- uint8_t frame_header[16];
- uint16_t* device_frame_buffer;
- int device_buffer;
- Cairo::RefPtr<Cairo::ImageSurface> frame_buffer;
- sigc::connection vblank_connection;
- sigc::connection periodic_connection;
-
- enum ModifierState {
- None = 0,
- ModShift = 0x1,
- ModSelect = 0x2,
- };
-
- ModifierState modifier_state;
-
- static const int pixels_per_row;
-
- void do_request (Push2Request*);
- int stop ();
- int open ();
- int close ();
- bool redraw ();
- int blit_to_device_frame_buffer ();
- bool vblank ();
-
+ public:
enum ButtonID {
TapTempo,
Metronome,
@@ -341,6 +281,76 @@ class Push2 : public ARDOUR::ControlProtocol
: Button (bb, ex, press, release, long_press) {}
};
+ public:
+ Push2 (ARDOUR::Session&);
+ ~Push2 ();
+
+ static bool probe ();
+ static void* request_factory (uint32_t);
+
+ std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
+
+ bool has_editor () const { return true; }
+ void* get_gui () const;
+ void tear_down_gui ();
+
+ int set_active (bool yn);
+ XMLNode& get_state();
+ int set_state (const XMLNode & node, int version);
+
+ PBD::Signal0<void> ConnectionChange;
+
+ boost::shared_ptr<ARDOUR::Port> input_port();
+ boost::shared_ptr<ARDOUR::Port> output_port();
+
+ int pad_note (int row, int col) const;
+ PBD::Signal0<void> PadChange;
+
+ void set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey);
+
+ MusicalMode::Type mode() const { return _mode; }
+ int scale_root() const { return _scale_root; }
+ int root_octave() const { return _root_octave; }
+ bool in_key() const { return _in_key; }
+
+ Push2Layout* current_layout() const;
+
+ enum ModifierState {
+ None = 0,
+ ModShift = 0x1,
+ ModSelect = 0x2,
+ };
+
+ ModifierState modifier_state() const { return _modifier_state; }
+
+ Button* button_by_id (ButtonID);
+
+ void write (const MidiByteArray&);
+
+ static const int cols;
+ static const int rows;
+
+ private:
+ libusb_device_handle *handle;
+ uint8_t frame_header[16];
+ uint16_t* device_frame_buffer;
+ int device_buffer;
+ Cairo::RefPtr<Cairo::ImageSurface> frame_buffer;
+ sigc::connection vblank_connection;
+ sigc::connection periodic_connection;
+
+ ModifierState _modifier_state;
+
+ static const int pixels_per_row;
+
+ void do_request (Push2Request*);
+ int stop ();
+ int open ();
+ int close ();
+ bool redraw ();
+ int blit_to_device_frame_buffer ();
+ bool vblank ();
+
void relax () {}
/* map of Buttons by CC */
@@ -394,7 +404,6 @@ class Push2 : public ARDOUR::ControlProtocol
void handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes*);
void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
- void write (const MidiByteArray&);
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
bool periodic ();
@@ -422,24 +431,6 @@ class Push2 : public ARDOUR::ControlProtocol
void button_new ();
void button_browse ();
void button_clip ();
- void button_upper (uint32_t n);
- void button_lower (uint32_t n);
- void button_upper_1 () { button_upper (0); }
- void button_upper_2 () { button_upper (1); }
- void button_upper_3 () { button_upper (2); }
- void button_upper_4 () { button_upper (3); }
- void button_upper_5 () { button_upper (4); }
- void button_upper_6 () { button_upper (5); }
- void button_upper_7 () { button_upper (6); }
- void button_upper_8 () { button_upper (7); }
- void button_lower_1 () { button_lower (0); }
- void button_lower_2 () { button_lower (1); }
- void button_lower_3 () { button_lower (2); }
- void button_lower_4 () { button_lower (3); }
- void button_lower_5 () { button_lower (4); }
- void button_lower_6 () { button_lower (5); }
- void button_lower_7 () { button_lower (6); }
- void button_lower_8 () { button_lower (7); }
void button_undo ();
void button_fwd32t ();
void button_fwd32 ();
@@ -464,49 +455,57 @@ class Push2 : public ARDOUR::ControlProtocol
void button_layout_press ();
void button_scale_press ();
+ void button_upper (uint32_t n);
+ void button_lower (uint32_t n);
+
+ void button_upper_1 () { button_upper (0); }
+ void button_upper_2 () { button_upper (1); }
+ void button_upper_3 () { button_upper (2); }
+ void button_upper_4 () { button_upper (3); }
+ void button_upper_5 () { button_upper (4); }
+ void button_upper_6 () { button_upper (5); }
+ void button_upper_7 () { button_upper (6); }
+ void button_upper_8 () { button_upper (7); }
+ void button_lower_1 () { button_lower (0); }
+ void button_lower_2 () { button_lower (1); }
+ void button_lower_3 () { button_lower (2); }
+ void button_lower_4 () { button_lower (3); }
+ void button_lower_5 () { button_lower (4); }
+ void button_lower_6 () { button_lower (5); }
+ void button_lower_7 () { button_lower (6); }
+ void button_lower_8 () { button_lower (7); }
+
void start_shift ();
void end_shift ();
- void start_select ();
- void end_select ();
- /* encoders */
+ /* non-strip encoders */
- void strip_vpot (int, int);
void other_vpot (int, int);
- void strip_vpot_touch (int, bool);
void other_vpot_touch (int, bool);
- /* widgets */
+ /* special Stripables */
+
+ boost::shared_ptr<ARDOUR::Stripable> master;
+ boost::shared_ptr<ARDOUR::Stripable> monitor;
+
+ /* Cairo graphics context */
Cairo::RefPtr<Cairo::Context> context;
- Glib::RefPtr<Pango::Layout> tc_clock_layout;
- Glib::RefPtr<Pango::Layout> bbt_clock_layout;
- Glib::RefPtr<Pango::Layout> upper_layout[8];
- Glib::RefPtr<Pango::Layout> mid_layout[8];
- Glib::RefPtr<Pango::Layout> lower_layout[8];
void splash ();
ARDOUR::microseconds_t splash_start;
- /* stripables */
-
- int32_t bank_start;
- PBD::ScopedConnectionList stripable_connections;
- boost::shared_ptr<ARDOUR::Stripable> stripable[8];
- boost::shared_ptr<ARDOUR::Stripable> master;
- boost::shared_ptr<ARDOUR::Stripable> monitor;
-
- void solo_change (int);
- void mute_change (int);
- void stripable_property_change (PBD::PropertyChange const& what_changed, int which);
+ /* Layouts */
- void switch_bank (uint32_t base);
+ mutable Glib::Threads::Mutex layout_lock;
+ Push2Layout* _current_layout;
+ Push2Layout* drawn_layout;
+ Push2Layout* mix_layout;
+ Push2Layout* scale_layout;
bool pad_filter (ARDOUR::MidiBuffer& in, ARDOUR::MidiBuffer& out) const;
boost::weak_ptr<ARDOUR::MidiTrack> current_pad_target;
- PBD::ScopedConnection selection_connection;
- void stripable_selection_change (ARDOUR::StripableNotificationListPtr);
PBD::ScopedConnection port_reg_connection;
void port_registration_handler ();
@@ -527,6 +526,9 @@ class Push2 : public ARDOUR::ControlProtocol
/* pad mapping */
+ PBD::ScopedConnection selection_connection;
+ void stripable_selection_change (ARDOUR::StripableNotificationListPtr);
+
std::map<int,int> pad_map;
void build_pad_table();
@@ -539,16 +541,92 @@ class Push2 : public ARDOUR::ControlProtocol
bool percussion;
void set_percussive_mode (bool);
+};
- /* menus */
- Push2Menu* current_menu;
- Push2Menu* drawn_menu;
- Push2Menu* scale_menu;
+class Push2Layout
+{
+ public:
+ Push2Layout (Push2& p, ARDOUR::Session& s);
+ virtual ~Push2Layout ();
+
+ bool mapped() const;
+
+ virtual bool redraw (Cairo::RefPtr<Cairo::Context>) const = 0;
+
+ virtual void button_upper (uint32_t n) {}
+ virtual void button_lower (uint32_t n) {}
+ virtual void button_up () {}
+ virtual void button_down () {}
+ virtual void button_right () {}
+ virtual void button_left () {}
+ virtual void button_select_press () {}
+ virtual void button_select_release () {}
+
+ virtual void strip_vpot (int, int) = 0;
+ virtual void strip_vpot_touch (int, bool) = 0;
+
+ protected:
+ Push2& p2;
+ ARDOUR::Session& session;
+};
+
+
+class MixLayout : public Push2Layout
+{
+ public:
+ MixLayout (Push2& p, ARDOUR::Session&, Cairo::RefPtr<Cairo::Context>);
+ ~MixLayout ();
+
+ bool redraw (Cairo::RefPtr<Cairo::Context>) const;
+
+ void button_upper (uint32_t n);
+ void button_lower (uint32_t n);
+ void button_left ();
+ void button_right ();
+ void button_select_press ();
+ void button_select_release ();
+
+ void strip_vpot (int, int);
+ void strip_vpot_touch (int, bool);
+
+ private:
+ Glib::RefPtr<Pango::Layout> tc_clock_layout;
+ Glib::RefPtr<Pango::Layout> bbt_clock_layout;
+ Glib::RefPtr<Pango::Layout> upper_layout[8];
+ Glib::RefPtr<Pango::Layout> mid_layout[8];
+ Glib::RefPtr<Pango::Layout> lower_layout[8];
+
+ /* stripables */
- void build_scale_menu ();
- void set_menu (Push2Menu*);
- void show_scale_menu ();
- void cancel_menu ();
+ int32_t bank_start;
+ PBD::ScopedConnectionList stripable_connections;
+ boost::shared_ptr<ARDOUR::Stripable> stripable[8];
+
+ void solo_change (int);
+ void mute_change (int);
+
+ void stripable_property_change (PBD::PropertyChange const& what_changed, int which);
+
+ void switch_bank (uint32_t base);
+};
+
+class ScaleLayout : public Push2Layout
+{
+ public:
+ ScaleLayout (Push2& p, ARDOUR::Session&, Cairo::RefPtr<Cairo::Context>);
+ ~ScaleLayout ();
+
+ bool redraw (Cairo::RefPtr<Cairo::Context>) const;
+
+ void button_upper (uint32_t n);
+ void button_lower (uint32_t n);
+
+ void strip_vpot (int, int);
+ void strip_vpot_touch (int, bool);
+
+ private:
+ Push2Menu* scale_menu;
+ void build_scale_menu (Cairo::RefPtr<Cairo::Context>);
};
} /* namespace */
diff --git a/libs/surfaces/push2/scale.cc b/libs/surfaces/push2/scale.cc
new file mode 100644
index 0000000000..da9c4fa3b1
--- /dev/null
+++ b/libs/surfaces/push2/scale.cc
@@ -0,0 +1,159 @@
+/*
+ Copyright (C) 2016 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <pangomm/layout.h>
+
+#include "pbd/compose.h"
+#include "pbd/convert.h"
+#include "pbd/debug.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
+#include "pbd/enumwriter.h"
+
+#include "midi++/parser.h"
+#include "timecode/time.h"
+#include "timecode/bbt_time.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/debug.h"
+#include "ardour/filesystem_paths.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/midi_track.h"
+#include "ardour/midi_port.h"
+#include "ardour/session.h"
+#include "ardour/tempo.h"
+
+#include "push2.h"
+#include "menu.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+using namespace Glib;
+using namespace ArdourSurface;
+
+ScaleLayout::ScaleLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
+ : Push2Layout (p, s)
+{
+ build_scale_menu (context);
+}
+
+ScaleLayout::~ScaleLayout ()
+{
+}
+
+bool
+ScaleLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
+{
+ bool draw = false;
+
+ if (scale_menu->dirty()) {
+ draw = true;
+ }
+
+ if (!draw) {
+ return false;
+ }
+
+ context->set_source_rgb (0.764, 0.882, 0.882);
+ context->rectangle (0, 0, 960, 160);
+ context->fill ();
+
+ scale_menu->redraw (context);
+
+ return true;
+}
+
+void
+ScaleLayout::button_upper (uint32_t n)
+{
+}
+
+void
+ScaleLayout::button_lower (uint32_t n)
+{
+}
+
+void
+ScaleLayout::strip_vpot (int n, int delta)
+{
+ if (n == 0) {
+ scale_menu->step_active (n, delta);
+ return;
+ }
+}
+
+void
+ScaleLayout::strip_vpot_touch (int, bool)
+{
+}
+
+void
+ScaleLayout::build_scale_menu (Cairo::RefPtr<Cairo::Context> context)
+{
+ vector<string> v;
+
+ scale_menu = new Push2Menu (context);
+
+ v.push_back ("Dorian");
+ v.push_back ("IonianMajor");
+ v.push_back ("Minor");
+ v.push_back ("HarmonicMinor");
+ v.push_back ("MelodicMinorAscending");
+ v.push_back ("MelodicMinorDescending");
+ v.push_back ("Phrygian");
+ v.push_back ("Lydian");
+ v.push_back ("Mixolydian");
+ v.push_back ("Aeolian");
+ v.push_back ("Locrian");
+ v.push_back ("PentatonicMajor");
+ v.push_back ("PentatonicMinor");
+ v.push_back ("Chromatic");
+ v.push_back ("BluesScale");
+ v.push_back ("NeapolitanMinor");
+ v.push_back ("NeapolitanMajor");
+ v.push_back ("Oriental");
+ v.push_back ("DoubleHarmonic");
+ v.push_back ("Enigmatic");
+ v.push_back ("Hirajoshi");
+ v.push_back ("HungarianMinor");
+ v.push_back ("HungarianMajor");
+ v.push_back ("Kumoi");
+ v.push_back ("Iwato");
+ v.push_back ("Hindu");
+ v.push_back ("Spanish8Tone");
+ v.push_back ("Pelog");
+ v.push_back ("HungarianGypsy");
+ v.push_back ("Overtone");
+ v.push_back ("LeadingWholeTone");
+ v.push_back ("Arabian");
+ v.push_back ("Balinese");
+ v.push_back ("Gypsy");
+ v.push_back ("Mohammedan");
+ v.push_back ("Javanese");
+ v.push_back ("Persian");
+ v.push_back ("Algeria");
+
+ scale_menu->fill_column (0, v);
+
+ v.clear ();
+}
diff --git a/libs/surfaces/push2/wscript b/libs/surfaces/push2/wscript
index 409311e0f5..1469bd523c 100644
--- a/libs/surfaces/push2/wscript
+++ b/libs/surfaces/push2/wscript
@@ -26,8 +26,11 @@ def build(bld):
midi_byte_array.cc
leds.cc
gui.cc
+ layout.cc
mode.cc
menu.cc
+ mix.cc
+ scale.cc
'''
obj.export_includes = ['.']
obj.defines = [ 'PACKAGE="ardour_push2"' ]