diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2016-07-10 10:49:39 -0400 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2016-09-27 14:59:30 -0500 |
commit | 14d80ec5f86c06b6327ccd4a08582afd1f95b4ce (patch) | |
tree | a4729042681cf33188d3563febb3e487d884ca48 /libs/surfaces/push2 | |
parent | 86578ea0cc1daef464953fdb80820d40a8167f78 (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.cc | 157 | ||||
-rw-r--r-- | libs/surfaces/push2/layout.cc | 38 | ||||
-rw-r--r-- | libs/surfaces/push2/mix.cc | 570 | ||||
-rw-r--r-- | libs/surfaces/push2/push2.cc | 706 | ||||
-rw-r--r-- | libs/surfaces/push2/push2.h | 306 | ||||
-rw-r--r-- | libs/surfaces/push2/scale.cc | 159 | ||||
-rw-r--r-- | libs/surfaces/push2/wscript | 3 |
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"' ] |