From 682e152aafbc49b5a7908fcd4b2734aed3b872e6 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 30 Nov 2015 12:51:18 -0500 Subject: [PATCH] faderport: some code cleanups, add timing for button presses, add new comboboxes to GUI to allow more button programming, save button state --- libs/surfaces/faderport/faderport.cc | 391 +++++++++++++++++--------- libs/surfaces/faderport/faderport.h | 31 +- libs/surfaces/faderport/gui.cc | 291 ++++++++++--------- libs/surfaces/faderport/gui.h | 106 +++++++ libs/surfaces/faderport/operations.cc | 8 +- 5 files changed, 547 insertions(+), 280 deletions(-) create mode 100644 libs/surfaces/faderport/gui.h diff --git a/libs/surfaces/faderport/faderport.cc b/libs/surfaces/faderport/faderport.cc index 31b87187f6..12707ac577 100644 --- a/libs/surfaces/faderport/faderport.cc +++ b/libs/surfaces/faderport/faderport.cc @@ -94,77 +94,73 @@ FaderPort::FaderPort (Session& s) /* Catch port connections and disconnections */ ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort::connection_handler, this, _1, _2, _3, _4, _5), this); - buttons.insert (std::make_pair (Mute, ButtonInfo (*this, _("Mute"), Mute, 21))); - buttons.insert (std::make_pair (Solo, ButtonInfo (*this, _("Solo"), Solo, 22))); - buttons.insert (std::make_pair (Rec, ButtonInfo (*this, _("Rec"), Rec, 23))); - buttons.insert (std::make_pair (Left, ButtonInfo (*this, _("Left"), Left, 20))); - buttons.insert (std::make_pair (Bank, ButtonInfo (*this, _("Bank"), Bank, 19))); - buttons.insert (std::make_pair (Right, ButtonInfo (*this, _("Right"), Right, 18))); - buttons.insert (std::make_pair (Output, ButtonInfo (*this, _("Output"), Output, 17))); - buttons.insert (std::make_pair (FP_Read, ButtonInfo (*this, _("Read"), FP_Read, 13))); - buttons.insert (std::make_pair (FP_Write, ButtonInfo (*this, _("Write"), FP_Write, 14))); - buttons.insert (std::make_pair (FP_Touch, ButtonInfo (*this, _("Touch"), FP_Touch, 15))); - buttons.insert (std::make_pair (FP_Off, ButtonInfo (*this, _("Off"), FP_Off, 16))); - buttons.insert (std::make_pair (Mix, ButtonInfo (*this, _("Mix"), Mix, 12))); - buttons.insert (std::make_pair (Proj, ButtonInfo (*this, _("Proj"), Proj, 11))); - buttons.insert (std::make_pair (Trns, ButtonInfo (*this, _("Trns"), Trns, 10))); - buttons.insert (std::make_pair (Undo, ButtonInfo (*this, _("Undo"), Undo, 9))); - buttons.insert (std::make_pair (Shift, ButtonInfo (*this, _("Shift"), Shift, 5))); - buttons.insert (std::make_pair (Punch, ButtonInfo (*this, _("Punch"), Punch, 6))); - buttons.insert (std::make_pair (User, ButtonInfo (*this, _("User"), User, 7))); - buttons.insert (std::make_pair (Loop, ButtonInfo (*this, _("Loop"), Loop, 8))); - buttons.insert (std::make_pair (Rewind, ButtonInfo (*this, _("Rewind"), Rewind, 4))); - buttons.insert (std::make_pair (Ffwd, ButtonInfo (*this, _("Ffwd"), Ffwd, 3))); - buttons.insert (std::make_pair (Stop, ButtonInfo (*this, _("Stop"), Stop, 2))); - buttons.insert (std::make_pair (Play, ButtonInfo (*this, _("Play"), Play, 1))); - buttons.insert (std::make_pair (RecEnable, ButtonInfo (*this, _("RecEnable"), RecEnable, 0))); - buttons.insert (std::make_pair (FaderTouch, ButtonInfo (*this, _("Fader (touch)"), FaderTouch, -1))); + buttons.insert (std::make_pair (Mute, Button (*this, _("Mute"), Mute, 21))); + buttons.insert (std::make_pair (Solo, Button (*this, _("Solo"), Solo, 22))); + buttons.insert (std::make_pair (Rec, Button (*this, _("Rec"), Rec, 23))); + buttons.insert (std::make_pair (Left, Button (*this, _("Left"), Left, 20))); + buttons.insert (std::make_pair (Bank, Button (*this, _("Bank"), Bank, 19))); + buttons.insert (std::make_pair (Right, Button (*this, _("Right"), Right, 18))); + buttons.insert (std::make_pair (Output, Button (*this, _("Output"), Output, 17))); + buttons.insert (std::make_pair (FP_Read, Button (*this, _("Read"), FP_Read, 13))); + buttons.insert (std::make_pair (FP_Write, Button (*this, _("Write"), FP_Write, 14))); + buttons.insert (std::make_pair (FP_Touch, Button (*this, _("Touch"), FP_Touch, 15))); + buttons.insert (std::make_pair (FP_Off, Button (*this, _("Off"), FP_Off, 16))); + buttons.insert (std::make_pair (Mix, Button (*this, _("Mix"), Mix, 12))); + buttons.insert (std::make_pair (Proj, Button (*this, _("Proj"), Proj, 11))); + buttons.insert (std::make_pair (Trns, Button (*this, _("Trns"), Trns, 10))); + buttons.insert (std::make_pair (Undo, Button (*this, _("Undo"), Undo, 9))); + buttons.insert (std::make_pair (Shift, Button (*this, _("Shift"), Shift, 5))); + buttons.insert (std::make_pair (Punch, Button (*this, _("Punch"), Punch, 6))); + buttons.insert (std::make_pair (User, Button (*this, _("User"), User, 7))); + buttons.insert (std::make_pair (Loop, Button (*this, _("Loop"), Loop, 8))); + buttons.insert (std::make_pair (Rewind, Button (*this, _("Rewind"), Rewind, 4))); + buttons.insert (std::make_pair (Ffwd, Button (*this, _("Ffwd"), Ffwd, 3))); + buttons.insert (std::make_pair (Stop, Button (*this, _("Stop"), Stop, 2))); + buttons.insert (std::make_pair (Play, Button (*this, _("Play"), Play, 1))); + buttons.insert (std::make_pair (RecEnable, Button (*this, _("RecEnable"), RecEnable, 0))); + buttons.insert (std::make_pair (FaderTouch, Button (*this, _("Fader (touch)"), FaderTouch, -1))); - button_info (Mix).set_action ( string("Common/toggle-editor-mixer"), true); - button_info (Proj).set_action ( string("Common/toggle-meterbridge"), true); - button_info (Trns).set_action ( string("Window/toggle-locations"), true); + get_button (Left).set_action ( boost::bind (&FaderPort::left, this), true); + get_button (Right).set_action ( boost::bind (&FaderPort::right, this), true); - button_info (Left).set_action ( boost::bind (&FaderPort::left, this), true); - button_info (Right).set_action ( boost::bind (&FaderPort::right, this), true); + get_button (Undo).set_action (boost::bind (&FaderPort::undo, this), true); + get_button (Undo).set_action (boost::bind (&FaderPort::redo, this), true, ShiftDown); + get_button (Undo).set_flash (true); - button_info (Undo).set_action (boost::bind (&FaderPort::undo, this), true); - button_info (Undo).set_action (boost::bind (&FaderPort::redo, this), true, ShiftDown); - button_info (Undo).set_flash (true); + get_button (FP_Read).set_action (boost::bind (&FaderPort::read, this), true); + get_button (FP_Write).set_action (boost::bind (&FaderPort::write, this), true); + get_button (FP_Touch).set_action (boost::bind (&FaderPort::touch, this), true); + get_button (FP_Off).set_action (boost::bind (&FaderPort::off, this), true); - button_info (FP_Read).set_action (boost::bind (&FaderPort::read, this), true); - button_info (FP_Write).set_action (boost::bind (&FaderPort::write, this), true); - button_info (FP_Touch).set_action (boost::bind (&FaderPort::touch, this), true); - button_info (FP_Off).set_action (boost::bind (&FaderPort::off, this), true); - - button_info (Play).set_action (boost::bind (&BasicUI::transport_play, this, true), true); - button_info (RecEnable).set_action (boost::bind (&BasicUI::rec_enable_toggle, this), true); + get_button (Play).set_action (boost::bind (&BasicUI::transport_play, this, true), true); + get_button (RecEnable).set_action (boost::bind (&BasicUI::rec_enable_toggle, this), true); /* Stop is a modifier, so we have to use its own button state to get the default action (since StopDown will be set when looking for the action to invoke. */ - button_info (Stop).set_action (boost::bind (&BasicUI::transport_stop, this), true, StopDown); - button_info (Ffwd).set_action (boost::bind (&BasicUI::ffwd, this), true); + get_button (Stop).set_action (boost::bind (&BasicUI::transport_stop, this), true, StopDown); + get_button (Ffwd).set_action (boost::bind (&BasicUI::ffwd, this), true); /* See comments about Stop above .. */ - button_info (Rewind).set_action (boost::bind (&BasicUI::rewind, this), true, RewindDown); - button_info (Rewind).set_action (boost::bind (&BasicUI::goto_zero, this), true, ButtonState (RewindDown|StopDown)); - button_info (Rewind).set_action (boost::bind (&BasicUI::goto_start, this), true, ButtonState (RewindDown|ShiftDown)); + get_button (Rewind).set_action (boost::bind (&BasicUI::rewind, this), true, RewindDown); + get_button (Rewind).set_action (boost::bind (&BasicUI::goto_zero, this), true, ButtonState (RewindDown|StopDown)); + get_button (Rewind).set_action (boost::bind (&BasicUI::goto_start, this), true, ButtonState (RewindDown|ShiftDown)); - button_info (Ffwd).set_action (boost::bind (&BasicUI::ffwd, this), true); - button_info (Ffwd).set_action (boost::bind (&BasicUI::goto_end, this), true, ShiftDown); + get_button (Ffwd).set_action (boost::bind (&BasicUI::ffwd, this), true); + get_button (Ffwd).set_action (boost::bind (&BasicUI::goto_end, this), true, ShiftDown); - button_info (Loop).set_action (boost::bind (&BasicUI::loop_toggle, this), true); - button_info (Loop).set_action (boost::bind (&BasicUI::add_marker, this, string()), true, ShiftDown); + get_button (Loop).set_action (boost::bind (&BasicUI::loop_toggle, this), true); + get_button (Loop).set_action (boost::bind (&BasicUI::add_marker, this, string()), true, ShiftDown); - button_info (Punch).set_action (boost::bind (&BasicUI::prev_marker, this), true, ShiftDown); - button_info (User).set_action (boost::bind (&BasicUI::next_marker, this), true, ShiftDown); + get_button (Punch).set_action (boost::bind (&BasicUI::prev_marker, this), true, ShiftDown); + get_button (User).set_action (boost::bind (&BasicUI::next_marker, this), true, ShiftDown); - button_info (Mute).set_action (boost::bind (&FaderPort::mute, this), true); - button_info (Solo).set_action (boost::bind (&FaderPort::solo, this), true); - button_info (Rec).set_action (boost::bind (&FaderPort::rec_enable, this), true); + get_button (Mute).set_action (boost::bind (&FaderPort::mute, this), true); + get_button (Solo).set_action (boost::bind (&FaderPort::solo, this), true); + get_button (Rec).set_action (boost::bind (&FaderPort::rec_enable, this), true); - button_info (Output).set_action (boost::bind (&FaderPort::use_master, this), true); - button_info (Output).set_action (boost::bind (&FaderPort::use_monitor, this), true, ShiftDown); + get_button (Output).set_action (boost::bind (&FaderPort::use_master, this), true); + get_button (Output).set_action (boost::bind (&FaderPort::use_monitor, this), true, ShiftDown); } FaderPort::~FaderPort () @@ -190,8 +186,8 @@ FaderPort::start_midi_handling () { /* handle device inquiry response */ _input_port->parser()->sysex.connect_same_thread (midi_connections, boost::bind (&FaderPort::sysex_handler, this, _1, _2, _3)); - /* handle switches */ - _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort::switch_handler, this, _1, _2)); + /* handle buttons */ + _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort::button_handler, this, _1, _2)); /* handle encoder */ _input_port->parser()->pitchbend.connect_same_thread (midi_connections, boost::bind (&FaderPort::encoder_handler, this, _1, _2)); /* handle fader */ @@ -263,18 +259,21 @@ FaderPort::all_lights_out () } } -FaderPort::ButtonInfo& -FaderPort::button_info (ButtonID id) const +FaderPort::Button& +FaderPort::get_button (ButtonID id) const { ButtonMap::const_iterator b = buttons.find (id); assert (b != buttons.end()); - return const_cast(b->second); + return const_cast(b->second); } void -FaderPort::switch_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb) +FaderPort::button_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb) { ButtonID id (ButtonID (tb->controller_number)); + Button& button (get_button (id)); + + button.do_timing (tb->value ? true : false); switch (id) { case Shift: @@ -307,13 +306,11 @@ FaderPort::switch_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb) break; } - ButtonInfo& bi (button_info (id)); - - if (bi.uses_flash()) { - bi.set_led_state (_output_port, (int)tb->value); + if (button.uses_flash()) { + button.set_led_state (_output_port, (int)tb->value); } - bi.invoke (button_state, tb->value ? true : false); + button.invoke (button_state, tb->value ? true : false); } void @@ -489,7 +486,7 @@ FaderPort::blink () blink_state = !blink_state; for (Blinkers::iterator b = blinkers.begin(); b != blinkers.end(); b++) { - button_info(*b).set_led_state (_output_port, blink_state); + get_button(*b).set_led_state (_output_port, blink_state); } return true; @@ -517,15 +514,15 @@ FaderPort::notify_record_state_changed () { switch (session->record_status()) { case Session::Disabled: - button_info (RecEnable).set_led_state (_output_port, false); + get_button (RecEnable).set_led_state (_output_port, false); blinkers.remove (RecEnable); break; case Session::Enabled: - button_info (RecEnable).set_led_state (_output_port, true); + get_button (RecEnable).set_led_state (_output_port, true); blinkers.push_back (RecEnable); break; case Session::Recording: - button_info (RecEnable).set_led_state (_output_port, true); + get_button (RecEnable).set_led_state (_output_port, true); blinkers.remove (RecEnable); break; } @@ -534,11 +531,11 @@ FaderPort::notify_record_state_changed () void FaderPort::notify_transport_state_changed () { - button_info (Loop).set_led_state (_output_port, session->get_play_loop()); - button_info (Play).set_led_state (_output_port, session->transport_speed() == 1.0); - button_info (Stop).set_led_state (_output_port, session->transport_stopped ()); - button_info (Rewind).set_led_state (_output_port, session->transport_speed() < 0.0); - button_info (Ffwd).set_led_state (_output_port, session->transport_speed() > 1.0); + get_button (Loop).set_led_state (_output_port, session->get_play_loop()); + get_button (Play).set_led_state (_output_port, session->transport_speed() == 1.0); + get_button (Stop).set_led_state (_output_port, session->transport_stopped ()); + get_button (Rewind).set_led_state (_output_port, session->transport_speed() < 0.0); + get_button (Ffwd).set_led_state (_output_port, session->transport_speed() > 1.0); } void @@ -588,6 +585,17 @@ FaderPort::get_state () child->add_child_nocopy (boost::shared_ptr(_output_port)->get_state()); node.add_child_nocopy (*child); + /* Save action state for Mix, Proj, Trns and User buttons, since these + * are user controlled. We can only save named-action operations, since + * internal functions are just pointers to functions and hard to + * serialize without enumerating them all somewhere. + */ + + node.add_child_nocopy (get_button (Mix).get_state()); + node.add_child_nocopy (get_button (Proj).get_state()); + node.add_child_nocopy (get_button (Trns).get_state()); + node.add_child_nocopy (get_button (User).get_state()); + return node; } @@ -616,6 +624,21 @@ FaderPort::set_state (const XMLNode& node, int version) } } + for (XMLNodeList::const_iterator n = node.children().begin(); n != node.children().end(); ++n) { + if ((*n)->name() == X_("Button")) { + XMLProperty const * prop = (*n)->property (X_("id")); + if (!prop) { + continue; + } + int xid = atoi (prop->value()); + ButtonMap::iterator b = buttons.find (ButtonID (xid)); + if (b == buttons.end()) { + continue; + } + b->second.set_state (**n); + } + } + return 0; } @@ -646,6 +669,8 @@ FaderPort::connection_handler (boost::weak_ptr, std::string name1, return false; } + cerr << "Connection state = " << hex << connection_state << dec << endl; + if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) { /* XXX this is a horrible hack. Without a short sleep here, @@ -661,13 +686,15 @@ FaderPort::connection_handler (boost::weak_ptr, std::string name1, _device_active = false; } + ConnectionChange (); /* emit signal for our GUI */ + return true; /* connection status changed */ } void FaderPort::connected () { - std::cerr << "faderport connected\n"; + std::cerr << "faderport connected or disconnected\n"; start_midi_handling (); @@ -686,68 +713,91 @@ FaderPort::connected () } void -FaderPort::ButtonInfo::invoke (FaderPort::ButtonState bs, bool press) +FaderPort::Button::invoke (FaderPort::ButtonState bs, bool press) { - switch (type) { + if (!press) { + if (long_press == 1) { + bs = FaderPort::ButtonState (bs | LongishPress); + } else if (long_press == 2) { + bs = FaderPort::ButtonState (bs | LongPress); + } + } + + ToDoMap::iterator x; + + if (press) { + if ((x = on_press.find (bs)) == on_press.end()) { + return; + } + } else { + if ((x = on_press.find (bs)) == on_release.end()) { + return; + } + } + + switch (x->second.type) { case NamedAction: - if (press) { - ToDoMap::iterator x = on_press.find (bs); - if (x != on_press.end()) { - if (!x->second.action_name.empty()) { - fp.access_action (x->second.action_name); - } - } - } else { - ToDoMap::iterator x = on_release.find (bs); - if (x != on_release.end()) { - if (!x->second.action_name.empty()) { - fp.access_action (x->second.action_name); - } - } + if (!x->second.action_name.empty()) { + fp.access_action (x->second.action_name); } - break; + break; case InternalFunction: - if (press) { - ToDoMap::iterator x = on_press.find (bs); - if (x != on_press.end()) { - if (x->second.function) { - x->second.function (); - } - } - } else { - ToDoMap::iterator x = on_release.find (bs); - if (x != on_release.end()) { - if (x->second.function) { - x->second.function (); - } - } + if (x->second.function) { + x->second.function (); } - break; } } void -FaderPort::ButtonInfo::set_action (string const& name, bool when_pressed, FaderPort::ButtonState bs) +FaderPort::Button::do_timing (bool press) +{ + if (press) { + pressed_at = get_microseconds (); + long_press = 0; + } else { + if (pressed_at > 0) { + const ARDOUR::microseconds_t delta = ARDOUR::get_microseconds () - pressed_at; + if (delta < 500000) { + long_press = 0; + } else if (delta < 1000000) { + long_press = 1; + } else { + long_press = 2; + } + pressed_at = 0; + } + } +} + +void +FaderPort::Button::set_action (string const& name, bool when_pressed, FaderPort::ButtonState bs) { ToDo todo; - type = NamedAction; + todo.type = NamedAction; if (when_pressed) { - todo.action_name = name; - on_press[bs] = todo; + if (name.empty()) { + on_press.erase (bs); + } else { + todo.action_name = name; + on_press[bs] = todo; + } } else { - todo.action_name = name; - on_release[bs] = todo; + if (name.empty()) { + on_release.erase (bs); + } else { + todo.action_name = name; + on_release[bs] = todo; + } } - } void -FaderPort::ButtonInfo::set_action (boost::function f, bool when_pressed, FaderPort::ButtonState bs) +FaderPort::Button::set_action (boost::function f, bool when_pressed, FaderPort::ButtonState bs) { ToDo todo; - type = InternalFunction; + todo.type = InternalFunction; if (when_pressed) { todo.function = f; @@ -759,7 +809,7 @@ FaderPort::ButtonInfo::set_action (boost::function f, bool when_pressed, } void -FaderPort::ButtonInfo::set_led_state (boost::shared_ptr port, int onoff, bool force) +FaderPort::Button::set_led_state (boost::shared_ptr port, int onoff, bool force) { if (!force && (led_on == (bool) onoff)) { /* nothing to do */ @@ -779,6 +829,87 @@ FaderPort::ButtonInfo::set_led_state (boost::shared_ptr port, int on led_on = (onoff ? true : false); } +int +FaderPort::Button::set_state (XMLNode const& node) +{ + const XMLProperty* prop = node.property ("id"); + if (!prop) { + return -1; + } + + int xid = atoi (prop->value()); + if (xid != id) { + return -1; + } + + typedef pair state_pair_t; + vector state_pairs; + + state_pairs.push_back (make_pair (string ("plain"), ButtonState (0))); + state_pairs.push_back (make_pair (string ("shift"), ShiftDown)); + state_pairs.push_back (make_pair (string ("longish"), LongishPress)); + state_pairs.push_back (make_pair (string ("long"), LongPress)); + + on_press.clear (); + on_release.clear (); + + for (vector::const_iterator sp = state_pairs.begin(); sp != state_pairs.end(); ++sp) { + string propname; + + propname = sp->first + X_("-press"); + if ((prop = node.property (propname)) == 0) { + continue; + } + set_action (prop->value(), true, sp->second); + + propname = sp->first + X_("-release"); + if ((prop = node.property (propname)) == 0) { + continue; + } + set_action (prop->value(), false, sp->second); + } + + return 0; +} + +XMLNode& +FaderPort::Button::get_state () const +{ + XMLNode* node = new XMLNode (X_("Button")); + char buf[16]; + snprintf (buf, sizeof (buf), "%d", id); + + node->add_property (X_("id"), buf); + + ToDoMap::const_iterator x; + ToDo null; + null.type = NamedAction; + + typedef pair state_pair_t; + vector state_pairs; + + state_pairs.push_back (make_pair (string ("plain"), ButtonState (0))); + state_pairs.push_back (make_pair (string ("shift"), ShiftDown)); + state_pairs.push_back (make_pair (string ("longish"), LongishPress)); + state_pairs.push_back (make_pair (string ("long"), LongPress)); + + for (vector::const_iterator sp = state_pairs.begin(); sp != state_pairs.end(); ++sp) { + if ((x = on_press.find (sp->second)) != on_press.end()) { + if (x->second.type == NamedAction) { + node->add_property (string (sp->first + X_("-press")).c_str(), x->second.action_name); + } + } + + if ((x = on_release.find (sp->second)) != on_release.end()) { + if (x->second.type == NamedAction) { + node->add_property (string (sp->first + X_("-release")).c_str(), x->second.action_name); + } + } + } + + return *node; +} + void FaderPort::gui_track_selection_changed (RouteNotificationListPtr routes) { @@ -813,7 +944,7 @@ FaderPort::set_current_route (boost::shared_ptr r) /* turn this off. It will be turned on back on in use_master() or use_monitor() as appropriate. */ - button_info(Output).set_led_state (_output_port, false); + get_button(Output).set_led_state (_output_port, false); if (_current_route) { _current_route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::drop_current_route, this), this); @@ -850,7 +981,7 @@ FaderPort::map_cut () if (mp) { bool yn = mp->cut_all (); - button_info (Mute).set_led_state (_output_port, yn); + get_button (Mute).set_led_state (_output_port, yn); if (yn) { blinkers.push_back (Mute); } else { @@ -864,19 +995,19 @@ FaderPort::map_cut () void FaderPort::map_mute (void*) { - button_info (Mute).set_led_state (_output_port, _current_route->muted()); + get_button (Mute).set_led_state (_output_port, _current_route->muted()); } void FaderPort::map_solo (bool, void*, bool) { - button_info (Solo).set_led_state (_output_port, _current_route->soloed() || _current_route->listening_via_monitor()); + get_button (Solo).set_led_state (_output_port, _current_route->soloed() || _current_route->listening_via_monitor()); } void FaderPort::map_listen (void*, bool) { - button_info (Solo).set_led_state (_output_port, _current_route->listening_via_monitor()); + get_button (Solo).set_led_state (_output_port, _current_route->listening_via_monitor()); } void @@ -884,9 +1015,9 @@ FaderPort::map_recenable () { boost::shared_ptr t = boost::dynamic_pointer_cast (_current_route); if (t) { - button_info (Rec).set_led_state (_output_port, t->record_enabled()); + get_button (Rec).set_led_state (_output_port, t->record_enabled()); } else { - button_info (Rec).set_led_state (_output_port, false); + get_button (Rec).set_led_state (_output_port, false); } } @@ -944,9 +1075,9 @@ void FaderPort::map_route_state () { if (!_current_route) { - button_info (Mute).set_led_state (_output_port, false); - button_info (Solo).set_led_state (_output_port, false); - button_info (Rec).set_led_state (_output_port, false); + get_button (Mute).set_led_state (_output_port, false); + get_button (Solo).set_led_state (_output_port, false); + get_button (Rec).set_led_state (_output_port, false); blinkers.remove (Mute); blinkers.remove (Solo); } else { @@ -974,5 +1105,5 @@ FaderPort::input_port() void FaderPort::set_action (ButtonID id, std::string const& action_name, bool on_press, ButtonState bs) { - button_info(id).set_action (action_name, on_press, bs); + get_button(id).set_action (action_name, on_press, bs); } diff --git a/libs/surfaces/faderport/faderport.h b/libs/surfaces/faderport/faderport.h index fc656d7f2a..0436e9e01e 100644 --- a/libs/surfaces/faderport/faderport.h +++ b/libs/surfaces/faderport/faderport.h @@ -151,6 +151,8 @@ class FaderPort : public ARDOUR::ControlProtocol, public AbstractUI function; }; @@ -245,10 +254,10 @@ class FaderPort : public ARDOUR::ControlProtocol, public AbstractUI ButtonMap; + typedef std::map ButtonMap; ButtonMap buttons; - ButtonInfo& button_info (ButtonID) const; + Button& get_button (ButtonID) const; void all_lights_out (); void close (); diff --git a/libs/surfaces/faderport/gui.cc b/libs/surfaces/faderport/gui.cc index 9e08885f88..4dcf2920e9 100644 --- a/libs/surfaces/faderport/gui.cc +++ b/libs/surfaces/faderport/gui.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2009-2012 Paul Davis + Copyright (C) 2015 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 @@ -17,24 +17,9 @@ */ -#include -#include -#include -#include - -#include #include -#include -#include -#include #include -#include -#include -#include - -namespace Gtk { - class CellRendererCombo; -} +#include #include "pbd/unwind.h" #include "pbd/strsplit.h" @@ -47,68 +32,10 @@ namespace Gtk { #include "ardour/audioengine.h" #include "faderport.h" +#include "gui.h" #include "i18n.h" -namespace ArdourSurface { - -class FPGUI : public Gtk::VBox -{ -public: - FPGUI (FaderPort&); - ~FPGUI (); - -private: - FaderPort& fp; - Gtk::Table table; - Gtk::ComboBox input_combo; - Gtk::ComboBox output_combo; - Gtk::ComboBox mix_combo; - Gtk::ComboBox proj_combo; - Gtk::ComboBox trns_combo; - - void update_port_combos (std::vector const&, std::vector const&); - PBD::ScopedConnection connection_change_connection; - void connection_handler (); - - struct MidiPortColumns : public Gtk::TreeModel::ColumnRecord { - MidiPortColumns() { - add (short_name); - add (full_name); - } - Gtk::TreeModelColumn short_name; - Gtk::TreeModelColumn full_name; - }; - - MidiPortColumns midi_port_columns; - bool ignore_active_change; - - Glib::RefPtr build_midi_port_list (std::vector const & ports, bool for_input); - void active_port_changed (Gtk::ComboBox*,bool for_input); - - struct ActionColumns : public Gtk::TreeModel::ColumnRecord { - ActionColumns() { - add (name); - add (path); - } - Gtk::TreeModelColumn name; - Gtk::TreeModelColumn path; - }; - - ActionColumns action_columns; - Glib::RefPtr available_action_model; - std::map action_map; // map from action names to paths - - void build_mix_action_combo (Gtk::ComboBox&); - void build_proj_action_combo (Gtk::ComboBox&); - void build_trns_action_combo (Gtk::ComboBox&); - - void build_available_action_menu (); - void action_changed (Gtk::ComboBox*, FaderPort::ButtonID); -}; - -} - using namespace PBD; using namespace ARDOUR; using namespace ArdourSurface; @@ -136,7 +63,7 @@ FaderPort::tear_down_gui () delete w; } } - delete (FPGUI*) gui; + delete static_cast (gui); gui = 0; } @@ -151,6 +78,7 @@ FaderPort::build_gui () FPGUI::FPGUI (FaderPort& p) : fp (p) , table (2, 5) + , action_table (4, 5) , ignore_active_change (false) { set_border_width (12); @@ -164,65 +92,138 @@ FPGUI::FPGUI (FaderPort& p) Gtk::Alignment* align; int row = 0; - vector midi_inputs; - vector midi_outputs; - - ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsPhysical), midi_inputs); - ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsPhysical), midi_outputs); - - update_port_combos (midi_inputs, midi_outputs); - input_combo.pack_start (midi_port_columns.short_name); output_combo.pack_start (midi_port_columns.short_name); input_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FPGUI::active_port_changed), &input_combo, true)); output_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FPGUI::active_port_changed), &output_combo, false)); - l = manage (new Gtk::Label (_("Sends MIDI via:"))); + l = manage (new Gtk::Label (_("Sends MIDI to:"))); l->set_alignment (1.0, 0.5); table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0)); table.attach (input_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0); row++; - l = manage (new Gtk::Label (_("Receives MIDI via:"))); + l = manage (new Gtk::Label (_("Receives MIDI from:"))); l->set_alignment (1.0, 0.5); table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0)); table.attach (output_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0); row++; - build_mix_action_combo (mix_combo); - build_proj_action_combo (proj_combo); - build_trns_action_combo (trns_combo); + build_mix_action_combo (mix_combo[0], FaderPort::ButtonState(0)); + build_mix_action_combo (mix_combo[1], FaderPort::ShiftDown); + build_mix_action_combo (mix_combo[2], FaderPort::LongishPress); + build_mix_action_combo (mix_combo[3], FaderPort::LongPress); - l = manage (new Gtk::Label (_("Mix Button"))); + build_proj_action_combo (proj_combo[0], FaderPort::ButtonState(0)); + build_proj_action_combo (proj_combo[1], FaderPort::ShiftDown); + build_proj_action_combo (proj_combo[2], FaderPort::LongishPress); + build_proj_action_combo (proj_combo[3], FaderPort::LongPress); + + build_trns_action_combo (trns_combo[0], FaderPort::ButtonState(0)); + build_trns_action_combo (trns_combo[1], FaderPort::ShiftDown); + build_trns_action_combo (trns_combo[2], FaderPort::LongishPress); + build_trns_action_combo (trns_combo[3], FaderPort::LongPress); + + action_table.set_row_spacings (4); + action_table.set_col_spacings (6); + action_table.set_border_width (12); + action_table.set_homogeneous (false); + + int action_row = 0; + + + l = manage (new Gtk::Label (_("Button"))); l->set_alignment (1.0, 0.5); - table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Press"))); + l->set_alignment (1.0, 0.5); + action_table.attach (*l, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Shift"))); + l->set_alignment (1.0, 0.5); + action_table.attach (*l, 2, 3, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Medium"))); + l->set_alignment (1.0, 0.5); + action_table.attach (*l, 3, 4, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + l = manage (new Gtk::Label (_("Long"))); + l->set_alignment (1.0, 0.5); + action_table.attach (*l, 4, 5, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + action_row++; + + l = manage (new Gtk::Label (_("Mix"))); + l->set_alignment (1.0, 0.5); + action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); align = manage (new Alignment); align->set (0.0, 0.5); - align->add (mix_combo); - table.attach (*align, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); - row++; - - l = manage (new Gtk::Label (_("Proj Button"))); - l->set_alignment (1.0, 0.5); - table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align->add (mix_combo[0]); + action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); align = manage (new Alignment); align->set (0.0, 0.5); - align->add (proj_combo); - table.attach (*align, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); - row++; - - l = manage (new Gtk::Label (_("Trns Button"))); - l->set_alignment (1.0, 0.5); - table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align->add (mix_combo[1]); + action_table.attach (*align, 2, 3, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); align = manage (new Alignment); align->set (0.0, 0.5); - align->add (trns_combo); - table.attach (*align, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align->add (mix_combo[2]); + action_table.attach (*align, 3, 4, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (mix_combo[3]); + action_table.attach (*align, 4, 5, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + action_row++; + + l = manage (new Gtk::Label (_("Proj"))); + l->set_alignment (1.0, 0.5); + action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (proj_combo[0]); + action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (proj_combo[1]); + action_table.attach (*align, 2, 3, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (proj_combo[2]); + action_table.attach (*align, 3, 4, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (proj_combo[3]); + action_table.attach (*align, 4, 5, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + action_row++; + + l = manage (new Gtk::Label (_("Trns"))); + l->set_alignment (1.0, 0.5); + action_table.attach (*l, 0, 1, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (trns_combo[0]); + action_table.attach (*align, 1, 2, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (trns_combo[1]); + action_table.attach (*align, 2, 3, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (trns_combo[2]); + action_table.attach (*align, 3, 4, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + align = manage (new Alignment); + align->set (0.0, 0.5); + align->add (trns_combo[3]); + action_table.attach (*align, 4, 5, action_row, action_row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); + action_row++; + + table.attach (action_table, 0, 5, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0)); row++; pack_start (table, false, false); + /* update the port connection combos */ + + update_port_combos (); + + /* catch future changes to connection state */ + fp.ConnectionChange.connect (connection_change_connection, invalidator (*this), boost::bind (&FPGUI::connection_handler, this), gui_context()); } @@ -240,18 +241,18 @@ FPGUI::connection_handler () PBD::Unwinder ici (ignore_active_change, true); + update_port_combos (); +} + +void +FPGUI::update_port_combos () +{ vector midi_inputs; vector midi_outputs; ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal), midi_inputs); ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal), midi_outputs); - update_port_combos (midi_inputs, midi_outputs); -} - -void -FPGUI::update_port_combos (vector const& midi_inputs, vector const& midi_outputs) -{ Glib::RefPtr input = build_midi_port_list (midi_inputs, true); Glib::RefPtr output = build_midi_port_list (midi_outputs, false); bool input_found = false; @@ -411,7 +412,16 @@ FPGUI::build_available_action_menu () } void -FPGUI::build_mix_action_combo (Gtk::ComboBox& cb) +FPGUI::action_changed (Gtk::ComboBox* cb, FaderPort::ButtonID id) +{ + TreeModel::const_iterator row = cb->get_active (); + string action_path = (*row)[action_columns.path]; + + fp.set_action (id, action_path, true); +} + +void +FPGUI::build_action_combo (Gtk::ComboBox& cb, vector > const & actions, FaderPort::ButtonID id, FaderPort::ButtonState bs) { Glib::RefPtr model (Gtk::ListStore::create (action_columns)); TreeIter rowp; @@ -419,29 +429,51 @@ FPGUI::build_mix_action_combo (Gtk::ComboBox& cb) rowp = model->append(); row = *(rowp); - row[action_columns.name] = _("Toggle Editor & Mixer Windows"); - row[action_columns.path] = X_("Common/toggle-editor-mixer"); + row[action_columns.name] = _("Disabled"); + row[action_columns.path] = string(); - rowp = model->append(); - row = *(rowp); - row[action_columns.name] = _("Show/Hide Editor mixer strip"); - row[action_columns.path] = X_("Editor/show-editor-mixer"); + for (vector >::const_iterator i = actions.begin(); i != actions.end(); ++i) { + rowp = model->append(); + row = *(rowp); + row[action_columns.name] = i->first; + row[action_columns.path] = i->second; + } cb.set_model (model); cb.pack_start (action_columns.name); - cb.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FPGUI::action_changed), &cb, FaderPort::Mix)); + cb.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FPGUI::action_changed), &cb, id)); } void -FPGUI::build_proj_action_combo (Gtk::ComboBox& cb) +FPGUI::build_mix_action_combo (Gtk::ComboBox& cb, FaderPort::ButtonState bs) { + vector > actions; + + actions.push_back (make_pair (string (_("Toggle Editor & Mixer Windows")), string (X_("Common/toggle-editor-mixer")))); + actions.push_back (make_pair (string (_("Show/Hide Editor mixer strip")), string (X_("Editor/show-editor-mixer")))); + + build_action_combo (cb, actions, FaderPort::Mix, bs); } +void +FPGUI::build_proj_action_combo (Gtk::ComboBox& cb, FaderPort::ButtonState bs) +{ + vector > actions; + + actions.push_back (make_pair (string("Toggle Meterbridge"), string(X_("Common/toggle-meterbridge")))); + + build_action_combo (cb, actions, FaderPort::Proj, bs); +} void -FPGUI::build_trns_action_combo (Gtk::ComboBox& cb) +FPGUI::build_trns_action_combo (Gtk::ComboBox& cb, FaderPort::ButtonState bs) { + vector > actions; + + actions.push_back (make_pair (string("Toggle Locations"), string(X_("Window/toggle-locations")))); + + build_action_combo (cb, actions, FaderPort::Trns, bs); } Glib::RefPtr @@ -499,14 +531,3 @@ FPGUI::active_port_changed (Gtk::ComboBox* combo, bool for_input) } } } - -void -FPGUI::action_changed (Gtk::ComboBox* cb, FaderPort::ButtonID id) -{ - TreeModel::const_iterator row = cb->get_active (); - string action_path = (*row)[action_columns.path]; - - cerr << "Change " << id << " to " << action_path << endl; - - fp.set_action (id, action_path, true); -} diff --git a/libs/surfaces/faderport/gui.h b/libs/surfaces/faderport/gui.h new file mode 100644 index 0000000000..5c2ce4334e --- /dev/null +++ b/libs/surfaces/faderport/gui.h @@ -0,0 +1,106 @@ +/* + Copyright (C) 2015 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. + +*/ + +#ifndef __ardour_faderport_gui_h__ +#define __ardour_faderport_gui_h__ + +#include +#include + +#include +#include +#include +#include + +namespace Gtk { + class CellRendererCombo; + class ListStore; +} + +#include "faderport.h" + +namespace ArdourSurface { + +class FPGUI : public Gtk::VBox +{ +public: + FPGUI (FaderPort&); + ~FPGUI (); + +private: + FaderPort& fp; + Gtk::Table table; + Gtk::Table action_table; + Gtk::ComboBox input_combo; + Gtk::ComboBox output_combo; + + /* the mix, proj, trns and user buttons have no obvious semantics for + * ardour, mixbus etc., so we allow the user to define their + * functionality from a small, curated set of options. + */ + + Gtk::ComboBox mix_combo[4]; + Gtk::ComboBox proj_combo[4]; + Gtk::ComboBox trns_combo[4]; + Gtk::ComboBox user_combo[4]; + + void update_port_combos (); + PBD::ScopedConnection connection_change_connection; + void connection_handler (); + + struct MidiPortColumns : public Gtk::TreeModel::ColumnRecord { + MidiPortColumns() { + add (short_name); + add (full_name); + } + Gtk::TreeModelColumn short_name; + Gtk::TreeModelColumn full_name; + }; + + MidiPortColumns midi_port_columns; + bool ignore_active_change; + + Glib::RefPtr build_midi_port_list (std::vector const & ports, bool for_input); + void active_port_changed (Gtk::ComboBox*,bool for_input); + + struct ActionColumns : public Gtk::TreeModel::ColumnRecord { + ActionColumns() { + add (name); + add (path); + } + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn path; + }; + + ActionColumns action_columns; + Glib::RefPtr available_action_model; + std::map action_map; // map from action names to paths + + void build_action_combo (Gtk::ComboBox& cb, std::vector > const & actions, FaderPort::ButtonID, FaderPort::ButtonState); + void build_mix_action_combo (Gtk::ComboBox&, FaderPort::ButtonState); + void build_proj_action_combo (Gtk::ComboBox&, FaderPort::ButtonState); + void build_trns_action_combo (Gtk::ComboBox&, FaderPort::ButtonState); + + void build_available_action_menu (); + void action_changed (Gtk::ComboBox*, FaderPort::ButtonID); +}; + +} + +#endif /* __ardour_faderport_gui_h__ */ diff --git a/libs/surfaces/faderport/operations.cc b/libs/surfaces/faderport/operations.cc index 9693986e00..57062a8b29 100644 --- a/libs/surfaces/faderport/operations.cc +++ b/libs/surfaces/faderport/operations.cc @@ -171,14 +171,14 @@ FaderPort::use_master () if (_current_route == r) { r = pre_master_route.lock(); set_current_route (r); - button_info(Output).set_led_state (_output_port, false); + get_button(Output).set_led_state (_output_port, false); blinkers.remove (Output); } else { if (_current_route != session->master_out() && _current_route != session->monitor_out()) { pre_master_route = boost::weak_ptr (_current_route); } set_current_route (r); - button_info(Output).set_led_state (_output_port, true); + get_button(Output).set_led_state (_output_port, true); blinkers.remove (Output); } } @@ -193,14 +193,14 @@ FaderPort::use_monitor () if (_current_route == r) { r = pre_monitor_route.lock(); set_current_route (r); - button_info(Output).set_led_state (_output_port, false); + get_button(Output).set_led_state (_output_port, false); blinkers.remove (Output); } else { if (_current_route != session->master_out() && _current_route != session->monitor_out()) { pre_monitor_route = boost::weak_ptr (_current_route); } set_current_route (r); - button_info(Output).set_led_state (_output_port, true); + get_button(Output).set_led_state (_output_port, true); blinkers.push_back (Output); } } else {