X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fsurfaces%2Fmackie%2Fmackie_control_protocol.cc;h=d7fda8ad7f2e79db1a038330906b0e68d230a7cb;hb=f387e834fd8966eff5f71955f013a905db9f7153;hp=48d074e1177582aae3b833d6f56eafbcd8983990;hpb=f450df300c9c057141a4caf79ff6dbfbf58492d9;p=ardour.git diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index 48d074e117..d7fda8ad7f 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -16,6 +16,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -23,7 +24,6 @@ #include #include -#define __STDC_FORMAT_MACROS #include #include #include @@ -34,7 +34,6 @@ #include "midi++/types.h" #include "midi++/port.h" -#include "midi++/manager.h" #include "pbd/pthread_utils.h" #include "pbd/error.h" #include "pbd/memento_command.h" @@ -43,52 +42,83 @@ #include "ardour/dB.h" #include "ardour/debug.h" #include "ardour/location.h" -#include "ardour/midi_ui.h" +#include "ardour/meter.h" #include "ardour/panner.h" +#include "ardour/panner_shell.h" #include "ardour/route.h" #include "ardour/session.h" #include "ardour/tempo.h" #include "ardour/types.h" +#include "ardour/audioengine.h" #include "mackie_control_protocol.h" #include "midi_byte_array.h" #include "mackie_control_exception.h" -#include "route_signal.h" #include "mackie_midi_builder.h" #include "surface_port.h" #include "surface.h" -#include "bcf_surface.h" -#include "mackie_surface.h" + +#include "strip.h" +#include "control_group.h" +#include "meter.h" +#include "button.h" +#include "fader.h" +#include "pot.h" using namespace ARDOUR; using namespace std; using namespace Mackie; using namespace PBD; - -using boost::shared_ptr; +using namespace Glib; #include "i18n.h" -MackieMidiBuilder builder; +#include "pbd/abstract_ui.cc" // instantiate template -#define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */ #define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__)) +const int MackieControlProtocol::MODIFIER_OPTION = 0x1; +const int MackieControlProtocol::MODIFIER_CONTROL = 0x2; +const int MackieControlProtocol::MODIFIER_SHIFT = 0x3; +const int MackieControlProtocol::MODIFIER_CMDALT = 0x4; + +MackieControlProtocol* MackieControlProtocol::_instance = 0; + +bool MackieControlProtocol::probe() +{ + return true; +} + MackieControlProtocol::MackieControlProtocol (Session& session) - : ControlProtocol (session, X_("Mackie"), MidiControlUI::instance()) + : ControlProtocol (session, X_("Mackie"), this) + , AbstractUI ("mackie") , _current_initial_bank (0) - , _surface (0) - , _jog_wheel (*this) , _timecode_type (ARDOUR::AnyTime::BBT) + , _input_bundle (new ARDOUR::Bundle (_("Mackie Control In"), true)) + , _output_bundle (new ARDOUR::Bundle (_("Mackie Control Out"), false)) + , _gui (0) + , _zoom_mode (false) + , _scrub_mode (false) + , _flip_mode (false) + , _current_selected_track (-1) { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n"); + + AudioEngine::instance()->PortConnectedOrDisconnected.connect ( + audio_engine_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::port_connected_or_disconnected, this, _2, _4, _5), + this + ); + + _instance = this; } MackieControlProtocol::~MackieControlProtocol() { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol\n"); + _active = false; + try { close(); } @@ -100,34 +130,25 @@ MackieControlProtocol::~MackieControlProtocol() } DEBUG_TRACE (DEBUG::MackieControl, "finished ~MackieControlProtocol::MackieControlProtocol\n"); -} -Mackie::Surface& -MackieControlProtocol::surface() -{ - if (_surface == 0) { - throw MackieControlException ("_surface is 0 in MackieControlProtocol::surface"); - } - return *_surface; + _instance = 0; } -const Mackie::SurfacePort& -MackieControlProtocol::mcu_port() const +void +MackieControlProtocol::thread_init () { - if (_ports.size() < 1) { - return _dummy_port; - } else { - return dynamic_cast (*_ports[0]); - } -} + struct sched_param rtparam; -Mackie::SurfacePort& -MackieControlProtocol::mcu_port() -{ - if (_ports.size() < 1) { - return _dummy_port; - } else { - return dynamic_cast (*_ports[0]); + pthread_set_name (X_("MackieControl")); + + PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("MackieControl"), 2048); + ARDOUR::SessionEvent::create_per_thread_pool (X_("MackieControl"), 128); + + memset (&rtparam, 0, sizeof (rtparam)); + rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */ + + if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) { + // do we care? not particularly. } } @@ -148,43 +169,16 @@ void MackieControlProtocol::next_track() { Sorted sorted = get_sorted_routes(); - if (_current_initial_bank + route_table.size() < sorted.size()) - { + if (_current_initial_bank + n_strips() < sorted.size()) { session->set_dirty(); switch_banks (_current_initial_bank + 1); } } -void -MackieControlProtocol::clear_route_signals() -{ - for (RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it) { - delete *it; - } - route_signals.clear(); -} - -// return the port for a given id - 0 based -// throws an exception if no port found -MackiePort& -MackieControlProtocol::port_for_id (uint32_t index) -{ - uint32_t current_max = 0; - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - current_max += (*it)->strips(); - if (index < current_max) return **it; - } - - // oops - no matching port - ostringstream os; - os << "No port for index " << index; - throw MackieControlException (os.str()); -} - // predicate for sort call in get_sorted_routes struct RouteByRemoteId { - bool operator () (const shared_ptr & a, const shared_ptr & b) const + bool operator () (const boost::shared_ptr & a, const boost::shared_ptr & b) const { return a->remote_control_id() < b->remote_control_id(); } @@ -215,17 +209,15 @@ MackieControlProtocol::get_sorted_routes() // sort in remote_id order, and exclude master, control and hidden routes // and any routes that are already set. - for (RouteList::iterator it = routes->begin(); it != routes->end(); ++it) - { + for (RouteList::iterator it = routes->begin(); it != routes->end(); ++it) { Route & route = **it; if ( - route.active() - && !route.is_master() - && !route.is_hidden() - && !route.is_control() - && remote_ids.find (route.remote_control_id()) == remote_ids.end() - ) - { + route.active() + && !route.is_master() + && !route.is_hidden() + && !route.is_monitor() + && remote_ids.find (route.remote_control_id()) == remote_ids.end() + ) { sorted.push_back (*it); remote_ids.insert (route.remote_control_id()); } @@ -237,233 +229,152 @@ MackieControlProtocol::get_sorted_routes() void MackieControlProtocol::refresh_current_bank() { - switch_banks (_current_initial_bank); + switch_banks (_current_initial_bank, true); +} + +uint32_t +MackieControlProtocol::n_strips() const +{ + uint32_t strip_count = 0; + + for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + if ((*si)->active()) { + strip_count += (*si)->n_strips (); + } + } + + return strip_count; } void -MackieControlProtocol::switch_banks (int initial) +MackieControlProtocol::switch_banks (uint32_t initial, bool force) { - // DON'T prevent bank switch if initial == _current_initial_bank - // because then this method can't be used as a refresh + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch banking to start at %1 force ? %2 current = %3\n", initial, force, _current_initial_bank)); + + if (initial == _current_initial_bank && !force) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("not switching to %1\n", initial)); + return; + } - // sanity checking Sorted sorted = get_sorted_routes(); - int delta = sorted.size() - route_table.size(); - if (initial < 0 || (delta > 0 && initial > delta)) - { + uint32_t strip_cnt = n_strips(); + + if (sorted.size() <= strip_cnt && !force) { + /* no banking */ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("not switching to %1\n", initial)); + return; + } + + uint32_t delta = sorted.size() - strip_cnt; + + if (delta > 0 && initial > delta) { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("not switching to %1\n", initial)); return; } + _current_initial_bank = initial; + _current_selected_track = -1; + + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + (*si)->drop_routes (); + } - // first clear the signals from old routes - // taken care of by the RouteSignal destructors - clear_route_signals(); + // Map current bank of routes onto each surface(+strip) - // now set the signals for new routes - if (_current_initial_bank <= sorted.size()) - { - // fetch the bank start and end to switch to - uint32_t end_pos = min (route_table.size(), sorted.size()); - Sorted::iterator it = sorted.begin() + _current_initial_bank; - Sorted::iterator end = sorted.begin() + _current_initial_bank + end_pos; + if (_current_initial_bank <= sorted.size()) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2\n", _current_initial_bank, end_pos)); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available routes %3\n", _current_initial_bank, strip_cnt, sorted.size())); // link routes to strips - uint32_t i = 0; - for (; it != end && it != sorted.end(); ++it, ++i) - { - boost::shared_ptr route = *it; - Strip & strip = *surface().strips[i]; - - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("remote id %1 connecting %2 to %3 with port %4\n", - route->remote_control_id(), route->name(), strip.name(), port_for_id(i))); - route_table[i] = route; - RouteSignal * rs = new RouteSignal (route, *this, strip, port_for_id(i)); - route_signals.push_back (rs); - // update strip from route - rs->notify_all(); - } - - // create dead strips if there aren't enough routes to - // fill a bank - for (; i < route_table.size(); ++i) - { - Strip & strip = *surface().strips[i]; - // send zero for this strip - MackiePort & port = port_for_id(i); - port.write (builder.zero_strip (port, strip)); - } - } - // display the current start bank. - surface().display_bank_start (mcu_port(), builder, _current_initial_bank); -} + Sorted::iterator r = sorted.begin() + _current_initial_bank; + + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + vector > routes; + uint32_t added = 0; -void -MackieControlProtocol::zero_all() -{ - // TODO turn off Timecode displays + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface has %1 strips\n", (*si)->n_strips())); - // zero all strips - for (Surface::Strips::iterator it = surface().strips.begin(); it != surface().strips.end(); ++it) - { - MackiePort & port = port_for_id ((*it)->index()); - port.write (builder.zero_strip (port, **it)); - } + for (; r != sorted.end() && added < (*si)->n_strips(); ++r, ++added) { + routes.push_back (*r); + cerr << "\t\tadded " << (*r)->name() << endl; + } - // and the master strip - mcu_port().write (builder.zero_strip (dynamic_cast (mcu_port()), master_strip())); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 routes\n", routes.size())); - // turn off global buttons and leds - // global buttons are only ever on mcu_port, so we don't have - // to figure out which port. - for (Surface::Controls::iterator it = surface().controls.begin(); it != surface().controls.end(); ++it) - { - Control & control = **it; - if (!control.group().is_strip() && control.accepts_feedback()) - { - mcu_port().write (builder.zero_control (control)); + (*si)->map_routes (routes); } } - // any hardware-specific stuff - surface().zero_all (mcu_port(), builder); + // display the current start bank. + surfaces.front()->display_bank_start (_current_initial_bank); } int MackieControlProtocol::set_active (bool yn) { - if (yn != _active) + if (yn == _active) { + return 0; + } + + try { - try - { - // the reason for the locking and unlocking is that - // glibmm can't do a condition wait on a RecMutex - if (yn) - { - // TODO what happens if this fails half way? - - // create MackiePorts - { - Glib::Mutex::Lock lock (update_mutex); - create_ports(); - } - - // now initialise MackiePorts - ie exchange sysex messages - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - (*it)->open(); - } - - // wait until all ports are active - // TODO a more sophisticated approach would - // allow things to start up with only an MCU, even if - // extenders were specified but not responding. - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - (*it)->wait_for_init(); - } - - // create surface object. This depends on the ports being - // correctly initialised - initialize_surface(); - connect_session_signals(); - - // yeehah! - _active = true; - - // send current control positions to surface - // must come after _active = true otherwise it won't run - update_surface(); - } else { - close(); - _active = false; - } - } + if (yn) { - catch (exception & e) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("set_active to false because exception caught: %1\n", e.what())); + /* start event loop */ + + BaseUI::run (); + + create_surfaces (); + connect_session_signals (); + + _active = true; + update_surfaces (); + + /* set up periodic task for metering and automation + */ + + Glib::RefPtr periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds + periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::periodic)); + periodic_timeout->attach (main_loop()->get_context()); + + } else { + BaseUI::quit (); + close(); _active = false; - throw; } } + + catch (exception & e) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("set_active to false because exception caught: %1\n", e.what())); + _active = false; + throw; + } return 0; } -bool -MackieControlProtocol::handle_strip_button (Control & control, ButtonState bs, boost::shared_ptr route) +bool +MackieControlProtocol::periodic () { - bool state = false; - - if (bs == press) - { - if (control.name() == "recenable") - { - state = !route->record_enabled(); - route->set_record_enable (state, this); - } - else if (control.name() == "mute") - { - state = !route->muted(); - route->set_mute (state, this); - } - else if (control.name() == "solo") - { - state = !route->soloed(); - route->set_solo (state, this); - } - else if (control.name() == "select") - { - // TODO make the track selected. Whatever that means. - //state = default_button_press (dynamic_cast (control)); - } - else if (control.name() == "vselect") - { - // TODO could be used to select different things to apply the pot to? - //state = default_button_press (dynamic_cast (control)); - } + if (!_active) { + return false; } - if (control.name() == "fader_touch") - { - state = bs == press; - control.strip().gain().in_use (state); + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->periodic (); } + + update_timecode_display(); - return state; + return true; } -void -MackieControlProtocol::update_led (Mackie::Button & button, Mackie::LedState ls) -{ - if (ls != none) - { - SurfacePort * port = 0; - if (button.group().is_strip()) - { - if (button.group().is_master()) - { - port = &mcu_port(); - } - else - { - port = &port_for_id (dynamic_cast (button.group()).index()); - } - } - else - { - port = &mcu_port(); - } - port->write (builder.build_led (button, ls)); - } -} void MackieControlProtocol::update_timecode_beats_led() { - switch (_timecode_type) - { + switch (_timecode_type) { case ARDOUR::AnyTime::BBT: update_global_led ("beats", on); update_global_led ("timecode", off); @@ -482,13 +393,16 @@ MackieControlProtocol::update_timecode_beats_led() void MackieControlProtocol::update_global_button (const string & name, LedState ls) { - if (surface().controls_by_name.find (name) != surface().controls_by_name.end()) - { - Button * button = dynamic_cast (surface().controls_by_name[name]); - mcu_port().write (builder.build_led (button->led(), ls)); + boost::shared_ptr surface = surfaces.front(); + + if (!surface->type() == mcu) { + return; } - else - { + + if (surface->controls_by_name.find (name) != surface->controls_by_name.end()) { + Button * button = dynamic_cast (surface->controls_by_name[name]); + surface->write (builder.build_led (button->led(), ls)); + } else { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Button %1 not found\n", name)); } } @@ -496,221 +410,124 @@ MackieControlProtocol::update_global_button (const string & name, LedState ls) void MackieControlProtocol::update_global_led (const string & name, LedState ls) { - if (surface().controls_by_name.find (name) != surface().controls_by_name.end()) - { - Led * led = dynamic_cast (surface().controls_by_name[name]); - mcu_port().write (builder.build_led (*led, ls)); + boost::shared_ptr surface = surfaces.front(); + + if (!surface->type() == mcu) { + return; } - else - { + + if (surface->controls_by_name.find (name) != surface->controls_by_name.end()) { + Led * led = dynamic_cast (surface->controls_by_name[name]); + surface->write (builder.build_led (*led, ls)); + } else { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Led %1 not found\n", name)); } } // send messages to surface to set controls to correct values void -MackieControlProtocol::update_surface() +MackieControlProtocol::update_surfaces() { - if (_active) - { - // do the initial bank switch to connect signals - // _current_initial_bank is initialised by set_state - switch_banks (_current_initial_bank); - - // create a RouteSignal for the master route - - boost::shared_ptr mr = master_route (); - if (mr) { - master_route_signal = shared_ptr (new RouteSignal (mr, *this, master_strip(), mcu_port())); - // update strip from route - master_route_signal->notify_all(); - } - - // sometimes the jog wheel is a pot - surface().blank_jog_ring (mcu_port(), builder); - - // update global buttons and displays - notify_record_state_changed(); - notify_transport_state_changed(); - update_timecode_beats_led(); + if (!_active) { + return; } + + // do the initial bank switch to connect signals + // _current_initial_bank is initialised by set_state + switch_banks (_current_initial_bank, true); + + // sometimes the jog wheel is a pot + surfaces.front()->blank_jog_ring (); + + // update global buttons and displays + + notify_record_state_changed(); + notify_transport_state_changed(); + update_timecode_beats_led(); } void MackieControlProtocol::connect_session_signals() { // receive routes added - session->RouteAdded.connect(session_connections, ui_bind (&MackieControlProtocol::notify_route_added, this, _1), midi_ui_context()); + session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_route_added, this, _1), this); // receive record state toggled - session->RecordStateChanged.connect(session_connections, ui_bind (&MackieControlProtocol::notify_record_state_changed, this), midi_ui_context()); + session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_record_state_changed, this), this); // receive transport state changed - session->TransportStateChange.connect(session_connections, ui_bind (&MackieControlProtocol::notify_transport_state_changed, this), midi_ui_context()); + session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_transport_state_changed, this), this); + session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_loop_state_changed, this), this); // receive punch-in and punch-out - Config->ParameterChanged.connect(session_connections, ui_bind (&MackieControlProtocol::notify_parameter_changed, this, _1), midi_ui_context()); - session->config.ParameterChanged.connect (session_connections, ui_bind (&MackieControlProtocol::notify_parameter_changed, this, _1), midi_ui_context()); + Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_parameter_changed, this, _1), this); + session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_parameter_changed, this, _1), this); // receive rude solo changed - session->SoloActive.connect(session_connections, ui_bind (&MackieControlProtocol::notify_solo_active_changed, this, _1), midi_ui_context()); + session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_solo_active_changed, this, _1), this); // make sure remote id changed signals reach here // see also notify_route_added Sorted sorted = get_sorted_routes(); for (Sorted::iterator it = sorted.begin(); it != sorted.end(); ++it) { - (*it)->RemoteControlIDChanged.connect (route_connections, ui_bind(&MackieControlProtocol::notify_remote_id_changed, this), midi_ui_context()); - } -} - -void -MackieControlProtocol::add_port (MIDI::Port & midi_port, int number) -{ - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1,%2,%3\n", midi_port.name(), midi_port.device(), midi_port.type())); - - if (string (midi_port.device()) == string ("ardour") && midi_port.type() == MIDI::Port::ALSA_Sequencer) { - throw MackieControlException ("The Mackie MCU driver will not use a port with device=ardour"); - } else if (midi_port.type() == MIDI::Port::ALSA_Sequencer) { - throw MackieControlException ("alsa/sequencer ports don't work with the Mackie MCU driver right now"); - } else { - MackiePort * sport = new MackiePort (*this, midi_port, number); - _ports.push_back (sport); - - sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport)); - sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport)); - sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport)); + (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, ui_bind(&MackieControlProtocol::notify_remote_id_changed, this), this); } } void -MackieControlProtocol::create_ports() +MackieControlProtocol::create_surfaces () { - MIDI::Manager * mm = MIDI::Manager::instance(); - MIDI::Port * midi_port = mm->port (default_port_name); - - // open main port + string device_name = "mcu"; + surface_type_t stype = mcu; - if (midi_port == 0) { - ostringstream os; - os << string_compose (_("no MIDI port named \"%1\" exists - Mackie control disabled"), default_port_name); - error << os.str() << endmsg; - throw MackieControlException (os.str()); - } + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", + 1 + ARDOUR::Config->get_mackie_extenders())); - add_port (*midi_port, 0); + for (uint32_t n = 0; n < 1 + ARDOUR::Config->get_mackie_extenders(); ++n) { - // open extender ports. Up to 9. Should be enough. - // could also use mm->get_midi_ports() + boost::shared_ptr surface (new Surface (*this, session->engine().jack(), device_name, n, stype)); + surfaces.push_back (surface); + + device_name = "mcu_xt"; + stype = ext; + + _input_bundle->add_channel ( + surface->port().input_port().name(), + ARDOUR::DataType::MIDI, + session->engine().make_port_name_non_relative (surface->port().input_port().name()) + ); + + _output_bundle->add_channel ( + surface->port().output_port().name(), + ARDOUR::DataType::MIDI, + session->engine().make_port_name_non_relative (surface->port().output_port().name()) + ); + + int fd; + MIDI::Port& input_port (surface->port().input_port()); + + if ((fd = input_port.selectable ()) >= 0) { + Glib::RefPtr psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR); - string ext_port_base = "mcu_xt_"; + psrc->connect (sigc::bind (sigc::mem_fun (this, &MackieControlProtocol::midi_input_handler), &input_port)); + psrc->attach (main_loop()->get_context()); + + // glibmm hack: for now, store only the GSource* - for (int index = 1; index <= 9; ++index) { - ostringstream os; - os << ext_port_base << index; - MIDI::Port * midi_port = mm->port (os.str()); - if (midi_port != 0) { - add_port (*midi_port, index); + port_sources.push_back (psrc->gobj()); + g_source_ref (psrc->gobj()); } } } -shared_ptr -MackieControlProtocol::master_route() -{ - return session->master_out (); -} - -Strip& -MackieControlProtocol::master_strip() -{ - return dynamic_cast (*surface().groups["master"]); -} - -void -MackieControlProtocol::initialize_surface() -{ - // set up the route table - int strips = 0; - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - strips += (*it)->strips(); - } - - set_route_table_size (strips); - - // TODO same as code in mackie_port.cc - string emulation = ARDOUR::Config->get_mackie_emulation(); - if (emulation == "bcf") - { - _surface = new BcfSurface (strips); - } - else if (emulation == "mcu") - { - _surface = new MackieSurface (strips); - } - else - { - ostringstream os; - os << "no Surface class found for emulation: " << emulation; - throw MackieControlException (os.str()); - } - - _surface->init(); - - // Connect events. Must be after route table otherwise there will be trouble - - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - (*it)->control_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_control_event, this, _1, _2, _3)); - } -} - void MackieControlProtocol::close() { - - // must be before other shutdown otherwise polling loop - // calls methods on objects that are deleted + clear_ports (); port_connections.drop_connections (); session_connections.drop_connections (); route_connections.drop_connections (); + periodic_connection.disconnect (); - if (_surface != 0) { - // These will fail if the port has gone away. - // So catch the exception and do the rest of the - // close afterwards - // because the bcf doesn't respond to the next 3 sysex messages - try { - zero_all(); - } - - catch (exception & e) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::close caught exception: %1\n", e.what())); - } - - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - try { - MackiePort & port = **it; - // faders to minimum - port.write_sysex (0x61); - // All LEDs off - port.write_sysex (0x62); - // Reset (reboot into offline mode) - port.write_sysex (0x63); - } - catch (exception & e) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::close caught exception: %1\n", e.what())); - } - } - - // disconnect routes from strips - clear_route_signals(); - delete _surface; - _surface = 0; - } - - // shut down MackiePorts - for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) { - delete *it; - } - - _ports.clear(); + surfaces.clear (); } XMLNode& @@ -720,7 +537,7 @@ MackieControlProtocol::get_state() // add name of protocol XMLNode* node = new XMLNode (X_("Protocol")); - node->add_property (X_("name"), _name); + node->add_property (X_("name"), ARDOUR::ControlProtocol::_name); // add current bank ostringstream os; @@ -757,108 +574,6 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/) return retval; } -void -MackieControlProtocol::handle_control_event (SurfacePort & port, Control & control, const ControlState & state) -{ - // find the route for the control, if there is one - boost::shared_ptr route; - if (control.group().is_strip()) { - if (control.group().is_master()) { - route = master_route(); - } else { - uint32_t index = control.ordinal() - 1 + (port.number() * port.strips()); - if (index < route_table.size()) - route = route_table[index]; - else - cerr << "Warning: index is " << index << " which is not in the route table, size: " << route_table.size() << endl; - } - } - - // This handles control element events from the surface - // the state of the controls on the surface is usually updated - // from UI events. - switch (control.type()) { - case Control::type_fader: - // find the route in the route table for the id - // if the route isn't available, skip it - // at which point the fader should just reset itself - if (route != 0) - { - route->gain_control()->set_value (state.pos); - - // must echo bytes back to slider now, because - // the notifier only works if the fader is not being - // touched. Which it is if we're getting input. - port.write (builder.build_fader ((Fader&)control, state.pos)); - } - break; - - case Control::type_button: - if (control.group().is_strip()) { - // strips - if (route != 0) { - handle_strip_button (control, state.button_state, route); - } else { - // no route so always switch the light off - // because no signals will be emitted by a non-route - port.write (builder.build_led (control.led(), off)); - } - } else if (control.group().is_master()) { - // master fader touch - if (route != 0) { - handle_strip_button (control, state.button_state, route); - } - } else { - // handle all non-strip buttons - surface().handle_button (*this, state.button_state, dynamic_cast (control)); - } - break; - - // pot (jog wheel, external control) - case Control::type_pot: - if (control.group().is_strip()) { - if (route != 0 && route->panner()) - { - // pan for mono input routes, or stereo linked panners - if (route->panner()->npanners() == 1 || (route->panner()->npanners() == 2 && route->panner()->linked())) - { - // assume pan for now - float xpos; - route->panner()->streampanner (0).get_effective_position (xpos); - - // calculate new value, and trim - xpos += state.delta * state.sign; - if (xpos > 1.0) - xpos = 1.0; - else if (xpos < 0.0) - xpos = 0.0; - - route->panner()->streampanner (0).set_position (xpos); - } - } - else - { - // it's a pot for an umnapped route, so turn all the lights off - port.write (builder.build_led_ring (dynamic_cast (control), off)); - } - } - else - { - if (control.is_jog()) - { - _jog_wheel.jog_event (port, control, state); - } - else - { - cout << "external controller" << state.ticks * state.sign << endl; - } - } - break; - - default: - cout << "Control::type not handled: " << control.type() << endl; - } -} ///////////////////////////////////////////////// // handlers for Route signals @@ -867,833 +582,873 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr // from Route, but they're also used in polling for automation ///////////////////////////////////////////////// -void -MackieControlProtocol::notify_solo_changed (RouteSignal * route_signal) +// TODO handle plugin automation polling +string +MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) { - try - { - Button & button = route_signal->strip().solo(); - route_signal->port().write (builder.build_led (button, route_signal->route()->soloed())); - } - catch (exception & e) - { - cout << e.what() << endl; + Timecode::BBT_Time bbt_time; + session->bbt_time (now_frame, bbt_time); + + // According to the Logic docs + // digits: 888/88/88/888 + // BBT mode: Bars/Beats/Subdivisions/Ticks + ostringstream os; + os << setw(3) << setfill('0') << bbt_time.bars; + os << setw(2) << setfill('0') << bbt_time.beats; + + // figure out subdivisions per beat + const ARDOUR::Meter & meter = session->tempo_map().meter_at (now_frame); + int subdiv = 2; + if (meter.note_divisor() == 8 && (meter.divisions_per_bar() == 12.0 || meter.divisions_per_bar() == 9.0 || meter.divisions_per_bar() == 6.0)) { + subdiv = 3; } + + uint32_t subdivisions = bbt_time.ticks / uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv); + uint32_t ticks = bbt_time.ticks % uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv); + + os << setw(2) << setfill('0') << subdivisions + 1; + os << setw(3) << setfill('0') << ticks; + + return os.str(); } -void -MackieControlProtocol::notify_mute_changed (RouteSignal * route_signal) +string +MackieControlProtocol::format_timecode_timecode (framepos_t now_frame) { - try - { - Button & button = route_signal->strip().mute(); - route_signal->port().write (builder.build_led (button, route_signal->route()->muted())); - } - catch (exception & e) - { - cout << e.what() << endl; - } + Timecode::Time timecode; + session->timecode_time (now_frame, timecode); + + // According to the Logic docs + // digits: 888/88/88/888 + // Timecode mode: Hours/Minutes/Seconds/Frames + ostringstream os; + os << setw(3) << setfill('0') << timecode.hours; + os << setw(2) << setfill('0') << timecode.minutes; + os << setw(2) << setfill('0') << timecode.seconds; + os << setw(3) << setfill('0') << timecode.frames; + + return os.str(); } void -MackieControlProtocol::notify_record_enable_changed (RouteSignal * route_signal) +MackieControlProtocol::update_timecode_display() { - try - { - Button & button = route_signal->strip().recenable(); - route_signal->port().write (builder.build_led (button, route_signal->route()->record_enabled())); + boost::shared_ptr surface = surfaces.front(); + + if (surface->type() != mcu || !surface->has_timecode_display()) { + return; } - catch (exception & e) - { - cout << e.what() << endl; + + // do assignment here so current_frame is fixed + framepos_t current_frame = session->transport_frame(); + string timecode; + + switch (_timecode_type) { + case ARDOUR::AnyTime::BBT: + timecode = format_bbt_timecode (current_frame); + break; + case ARDOUR::AnyTime::Timecode: + timecode = format_timecode_timecode (current_frame); + break; + default: + return; + } + + // only write the timecode string to the MCU if it's changed + // since last time. This is to reduce midi bandwidth used. + if (timecode != _timecode_last) { + surface->display_timecode (timecode, _timecode_last); + _timecode_last = timecode; } } -void MackieControlProtocol::notify_active_changed (RouteSignal *) +/////////////////////////////////////////// +// Session signals +/////////////////////////////////////////// + +void MackieControlProtocol::notify_parameter_changed (std::string const & p) { - try - { - DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::notify_active_changed\n"); - refresh_current_bank(); - } - catch (exception & e) - { - cout << e.what() << endl; + if (p == "punch-in") { + update_global_button ("punch_in", session->config.get_punch_in()); + } else if (p == "punch-out") { + update_global_button ("punch_out", session->config.get_punch_out()); + } else if (p == "clicking") { + update_global_button ("clicking", Config->get_clicking()); + } else { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("parameter changed: %1\n", p)); } } +// RouteList is the set of routes that have just been added void -MackieControlProtocol::notify_gain_changed (RouteSignal * route_signal, bool force_update) +MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) { - try - { - Fader & fader = route_signal->strip().gain(); - if (!fader.in_use()) - { - float gain_value = route_signal->route()->gain_control()->get_value(); - // check that something has actually changed - if (force_update || gain_value != route_signal->last_gain_written()) - { - route_signal->port().write (builder.build_fader (fader, gain_value)); - route_signal->last_gain_written (gain_value); - } - } - } - catch (exception & e) - { - cout << e.what() << endl; - } -} + // currently assigned banks are less than the full set of + // strips, so activate the new strip now. -void -MackieControlProtocol::notify_name_changed (RouteSignal * route_signal) -{ - try - { - Strip & strip = route_signal->strip(); - if (!strip.is_master()) - { - string line1; - string fullname = route_signal->route()->name(); - - if (fullname.length() <= 6) - { - line1 = fullname; - } - else - { - line1 = PBD::short_version (fullname, 6); - } + refresh_current_bank(); - SurfacePort & port = route_signal->port(); - port.write (builder.strip_display (port, strip, 0, line1)); - port.write (builder.strip_display_blank (port, strip, 1)); - } - } - catch (exception & e) - { - cout << e.what() << endl; + // otherwise route added, but current bank needs no updating + + // make sure remote id changes in the new route are handled + typedef ARDOUR::RouteList ARS; + + for (ARS::iterator it = rl.begin(); it != rl.end(); ++it) { + (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_remote_id_changed, this), this); } } void -MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool force_update) +MackieControlProtocol::notify_solo_active_changed (bool active) { - try - { - Pot & pot = route_signal->strip().vpot(); - boost::shared_ptr panner = route_signal->route()->panner(); - if ((panner && panner->npanners() == 1) || (panner->npanners() == 2 && panner->linked())) - { - float pos; - route_signal->route()->panner()->streampanner(0).get_effective_position (pos); - - // cache the MidiByteArray here, because the mackie led control is much lower - // resolution than the panner control. So we save lots of byte - // sends in spite of more work on the comparison - MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, pos), MackieMidiBuilder::midi_pot_mode_dot); - // check that something has actually changed - if (force_update || bytes != route_signal->last_pan_written()) - { - route_signal->port().write (bytes); - route_signal->last_pan_written (bytes); - } - } - else - { - route_signal->port().write (builder.zero_control (pot)); - } - } - catch (exception & e) - { - cout << e.what() << endl; + boost::shared_ptr surface = surfaces.front(); + + Button * rude_solo = reinterpret_cast (surface->controls_by_name["solo"]); + + if (rude_solo) { + surface->write (builder.build_led (*rude_solo, active ? flashing : off)); } } -// TODO handle plugin automation polling void -MackieControlProtocol::update_automation (RouteSignal & rs) +MackieControlProtocol::notify_remote_id_changed() { - ARDOUR::AutoState gain_state = rs.route()->gain_control()->automation_state(); + Sorted sorted = get_sorted_routes(); + uint32_t sz = n_strips(); - if (gain_state == Touch || gain_state == Play) - { - notify_gain_changed (&rs, false); - } + // if a remote id has been moved off the end, we need to shift + // the current bank backwards. - if (rs.route()->panner()) { - ARDOUR::AutoState panner_state = rs.route()->panner()->automation_state(); - if (panner_state == Touch || panner_state == Play) - { - notify_panner_changed (&rs, false); - } + if (sorted.size() - _current_initial_bank < sz) { + // but don't shift backwards past the zeroth channel + switch_banks (max((Sorted::size_type) 0, sorted.size() - sz)); + } else { + // Otherwise just refresh the current bank + refresh_current_bank(); } } -string -MackieControlProtocol::format_bbt_timecode (nframes_t now_frame) -{ - BBT_Time bbt_time; - session->bbt_time (now_frame, bbt_time); - - // According to the Logic docs - // digits: 888/88/88/888 - // BBT mode: Bars/Beats/Subdivisions/Ticks - ostringstream os; - os << setw(3) << setfill('0') << bbt_time.bars; - os << setw(2) << setfill('0') << bbt_time.beats; +/////////////////////////////////////////// +// Transport signals +/////////////////////////////////////////// - // figure out subdivisions per beat - const Meter & meter = session->tempo_map().meter_at (now_frame); - int subdiv = 2; - if (meter.note_divisor() == 8 && (meter.beats_per_bar() == 12.0 || meter.beats_per_bar() == 9.0 || meter.beats_per_bar() == 6.0)) - { - subdiv = 3; - } +void +MackieControlProtocol::notify_loop_state_changed() +{ + update_global_button ("loop", session->get_play_loop()); +} - uint32_t subdivisions = bbt_time.ticks / uint32_t (Meter::ticks_per_beat / subdiv); - uint32_t ticks = bbt_time.ticks % uint32_t (Meter::ticks_per_beat / subdiv); +void +MackieControlProtocol::notify_transport_state_changed() +{ + // switch various play and stop buttons on / off + update_global_button ("play", session->transport_rolling()); + update_global_button ("stop", !session->transport_rolling()); + update_global_button ("rewind", session->transport_speed() < 0.0); + update_global_button ("ffwd", session->transport_speed() > 1.0); - os << setw(2) << setfill('0') << subdivisions + 1; - os << setw(3) << setfill('0') << ticks; + _transport_previously_rolling = session->transport_rolling(); - return os.str(); } -string -MackieControlProtocol::format_timecode_timecode (nframes_t now_frame) +void +MackieControlProtocol::notify_record_state_changed () { - Timecode::Time timecode; - session->timecode_time (now_frame, timecode); + Button * rec = reinterpret_cast (surfaces.front()->controls_by_name["record"]); + if (rec) { + LedState ls; - // According to the Logic docs - // digits: 888/88/88/888 - // Timecode mode: Hours/Minutes/Seconds/Frames - ostringstream os; - os << setw(3) << setfill('0') << timecode.hours; - os << setw(2) << setfill('0') << timecode.minutes; - os << setw(2) << setfill('0') << timecode.seconds; - os << setw(3) << setfill('0') << timecode.frames; + switch (session->record_status()) { + case Session::Disabled: + ls = off; + break; + case Session::Recording: + ls = on; + break; + case Session::Enabled: + ls = flashing; + break; + } - return os.str(); + surfaces.front()->write (builder.build_led (*rec, ls)); + } } -void -MackieControlProtocol::update_timecode_display() +list > +MackieControlProtocol::bundles () { - if (surface().has_timecode_display()) - { - // do assignment here so current_frame is fixed - nframes_t current_frame = session->transport_frame(); - string timecode; - - switch (_timecode_type) - { - case ARDOUR::AnyTime::BBT: - timecode = format_bbt_timecode (current_frame); - break; - case ARDOUR::AnyTime::Timecode: - timecode = format_timecode_timecode (current_frame); - break; - default: - ostringstream os; - os << "Unknown timecode: " << _timecode_type; - throw runtime_error (os.str()); - } - - // only write the timecode string to the MCU if it's changed - // since last time. This is to reduce midi bandwidth used. - if (timecode != _timecode_last) - { - surface().display_timecode (mcu_port(), builder, timecode, _timecode_last); - _timecode_last = timecode; - } - } + list > b; + b.push_back (_input_bundle); + b.push_back (_output_bundle); + return b; } -void -MackieControlProtocol::poll_session_data() +void +MackieControlProtocol::port_connected_or_disconnected (string a, string b, bool connected) { - // XXX need to attach this to a timer in the MIDI UI event loop (20msec) + /* If something is connected to one of our output ports, send MIDI to update the surface + to whatever state it should have. + */ - if (_active) { - // do all currently mapped routes - for (RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it) { - update_automation (**it); - } + if (!connected) { + return; + } - // and the master strip - if (master_route_signal != 0) { - update_automation (*master_route_signal); + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + string const n = AudioEngine::instance()->make_port_name_non_relative ((*s)->port().output_port().name ()); + if (a == n || b == n) { + update_surfaces (); + return; } - - update_timecode_display(); } } -///////////////////////////////////// -// Transport Buttons -///////////////////////////////////// - -LedState -MackieControlProtocol::frm_left_press (Button &) +void +MackieControlProtocol::do_request (MackieControlUIRequest* req) { - // can use first_mark_before/after as well - unsigned long elapsed = _frm_left_last.restart(); + if (req->type == CallSlot) { - Location * loc = session->locations()->first_location_before ( - session->transport_frame() - ); + call_slot (MISSING_INVALIDATOR, req->the_slot); - // allow a quick double to go past a previous mark - if (session->transport_rolling() && elapsed < 500 && loc != 0) - { - Location * loc_two_back = session->locations()->first_location_before (loc->start()); - if (loc_two_back != 0) - { - loc = loc_two_back; - } - } + } else if (req->type == Quit) { - // move to the location, if it's valid - if (loc != 0) - { - session->request_locate (loc->start(), session->transport_rolling()); + stop (); } - - return on; } -LedState -MackieControlProtocol::frm_left_release (Button &) +int +MackieControlProtocol::stop () { - return off; -} + BaseUI::quit (); -LedState -MackieControlProtocol::frm_right_press (Button &) -{ - // can use first_mark_before/after as well - Location * loc = session->locations()->first_location_after ( - session->transport_frame() - ); - if (loc != 0) session->request_locate (loc->start(), session->transport_rolling()); - return on; + return 0; } -LedState -MackieControlProtocol::frm_right_release (Button &) +/** Add a timeout so that a control's in_use flag will be reset some time in the future. + * @param in_use_control the control whose in_use flag to reset. + * @param touch_control a touch control to emit an event for, or 0. + */ +void +MackieControlProtocol::add_in_use_timeout (Surface& surface, Control& in_use_control, Control* touch_control) { - return off; -} + Glib::RefPtr timeout (Glib::TimeoutSource::create (250)); // milliseconds -LedState -MackieControlProtocol::stop_press (Button &) -{ - session->request_stop(); - return on; -} + in_use_control.in_use_connection.disconnect (); + in_use_control.in_use_connection = timeout->connect ( + sigc::bind (sigc::mem_fun (*this, &MackieControlProtocol::control_in_use_timeout), &surface, &in_use_control, touch_control)); + in_use_control.in_use_touch_control = touch_control; + + timeout->attach (main_loop()->get_context()); -LedState -MackieControlProtocol::stop_release (Button &) -{ - return session->transport_stopped(); -} + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("timeout queued for surface %1, control %2 touch control %3\n", + surface.number(), &in_use_control, touch_control));} -LedState -MackieControlProtocol::play_press (Button &) +/** Handle timeouts to reset in_use for controls that can't + * do this by themselves (e.g. pots, and faders without touch support). + * @param in_use_control the control whose in_use flag to reset. + * @param touch_control a touch control to emit an event for, or 0. + */ +bool +MackieControlProtocol::control_in_use_timeout (Surface* surface, Control* in_use_control, Control* touch_control) { - session->request_transport_speed (1.0); - return on; -} + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("timeout elapsed for surface %1, control %2 touch control %3\n", + surface->number(), in_use_control, touch_control)); -LedState -MackieControlProtocol::play_release (Button &) -{ - return session->transport_rolling(); -} + in_use_control->set_in_use (false); -LedState -MackieControlProtocol::record_press (Button &) -{ - if (session->get_record_enabled()) { - session->disable_record (false); - } else { - session->maybe_enable_record(); + if (touch_control) { + // empty control_state + ControlState control_state; + surface->handle_control_event (*touch_control, control_state); } - return on; + + // only call this method once from the timer + return false; } -LedState -MackieControlProtocol::record_release (Button &) +void +MackieControlProtocol::update_led (Surface& surface, Button& button, Mackie::LedState ls) { - if (session->get_record_enabled()) { - if (session->transport_rolling()) { - return on; - } else { - return flashing; - } - } else { - return off; + if (ls != none) { + surface.port().write (builder.build_led (button, ls)); } } -LedState -MackieControlProtocol::rewind_press (Button &) +void +MackieControlProtocol::handle_button_event (Surface& surface, Button& button, ButtonState bs) { - _jog_wheel.push (JogWheel::speed); - _jog_wheel.transport_direction (-1); - session->request_transport_speed (-_jog_wheel.transport_speed()); - return on; -} + if (bs != press && bs != release) { + update_led (surface, button, none); + return; + } + + LedState ls; -LedState -MackieControlProtocol::rewind_release (Button &) -{ - _jog_wheel.pop(); - _jog_wheel.transport_direction (0); - if (_transport_previously_rolling) - session->request_transport_speed (1.0); - else - session->request_stop(); - return off; -} + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Handling %1 for button %2\n", (bs == press ? "press" : "release"), button.raw_id())); -LedState -MackieControlProtocol::ffwd_press (Button &) -{ - _jog_wheel.push (JogWheel::speed); - _jog_wheel.transport_direction (1); - session->request_transport_speed (_jog_wheel.transport_speed()); - return on; -} + switch (button.raw_id()) { + case 0x28: // io + switch (bs) { + case press: ls = io_press (button); break; + case release: ls = io_release (button); break; + case neither: break; + } + break; + + case 0x29: // sends + switch (bs) { + case press: ls = sends_press (button); break; + case release: ls = sends_release (button); break; + case neither: break; + } + break; + + case 0x2a: // pan + switch (bs) { + case press: ls = pan_press (button); break; + case release: ls = pan_release (button); break; + case neither: break; + } + break; + + case 0x2b: // plugin + switch (bs) { + case press: ls = plugin_press (button); break; + case release: ls = plugin_release (button); break; + case neither: break; + } + break; + + case 0x2c: // eq + switch (bs) { + case press: ls = eq_press (button); break; + case release: ls = eq_release (button); break; + case neither: break; + } + break; + + case 0x2d: // dyn + switch (bs) { + case press: ls = dyn_press (button); break; + case release: ls = dyn_release (button); break; + case neither: break; + } + break; + + case 0x2e: // left + switch (bs) { + case press: ls = left_press (button); break; + case release: ls = left_release (button); break; + case neither: break; + } + break; + + case 0x2f: // right + switch (bs) { + case press: ls = right_press (button); break; + case release: ls = right_release (button); break; + case neither: break; + } + break; + + case 0x30: // channel_left + switch (bs) { + case press: ls = channel_left_press (button); break; + case release: ls = channel_left_release (button); break; + case neither: break; + } + break; + + case 0x31: // channel_right + switch (bs) { + case press: ls = channel_right_press (button); break; + case release: ls = channel_right_release (button); break; + case neither: break; + } + break; + + case 0x32: // flip + switch (bs) { + case press: ls = flip_press (button); break; + case release: ls = flip_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::ffwd_release (Button &) -{ - _jog_wheel.pop(); - _jog_wheel.transport_direction (0); - if (_transport_previously_rolling) - session->request_transport_speed (1.0); - else - session->request_stop(); - return off; -} + case 0x33: // edit + switch (bs) { + case press: ls = edit_press (button); break; + case release: ls = edit_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::loop_press (Button &) -{ - session->request_play_loop (!session->get_play_loop()); - return on; -} + case 0x34: // name_value + switch (bs) { + case press: ls = name_value_press (button); break; + case release: ls = name_value_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::loop_release (Button &) -{ - return session->get_play_loop(); -} + case 0x35: // timecode_beats + switch (bs) { + case press: ls = timecode_beats_press (button); break; + case release: ls = timecode_beats_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::punch_in_press (Button &) -{ - bool state = !session->config.get_punch_in(); - session->config.set_punch_in (state); - return state; -} + case 0x36: // F1 + switch (bs) { + case press: ls = F1_press (button); break; + case release: ls = F1_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::punch_in_release (Button &) -{ - return session->config.get_punch_in(); -} + case 0x37: // F2 + switch (bs) { + case press: ls = F2_press (button); break; + case release: ls = F2_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::punch_out_press (Button &) -{ - bool state = !session->config.get_punch_out(); - session->config.set_punch_out (state); - return state; -} + case 0x38: // F3 + switch (bs) { + case press: ls = F3_press (button); break; + case release: ls = F3_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::punch_out_release (Button &) -{ - return session->config.get_punch_out(); -} + case 0x39: // F4 + switch (bs) { + case press: ls = F4_press (button); break; + case release: ls = F4_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::home_press (Button &) -{ - session->goto_start(); - return on; -} + case 0x3a: // F5 + switch (bs) { + case press: ls = F5_press (button); break; + case release: ls = F5_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::home_release (Button &) -{ - return off; -} + case 0x3b: // F6 + switch (bs) { + case press: ls = F6_press (button); break; + case release: ls = F6_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::end_press (Button &) -{ - session->goto_end(); - return on; -} + case 0x3c: // F7 + switch (bs) { + case press: ls = F7_press (button); break; + case release: ls = F7_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::end_release (Button &) -{ - return off; -} + case 0x3d: // F8 + switch (bs) { + case press: ls = F8_press (button); break; + case release: ls = F8_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::clicking_press (Button &) -{ - bool state = !Config->get_clicking(); - Config->set_clicking (state); - return state; -} + case 0x3e: // F9 + switch (bs) { + case press: ls = F9_press (button); break; + case release: ls = F9_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::clicking_release (Button &) -{ - return Config->get_clicking(); -} + case 0x3f: // F10 + switch (bs) { + case press: ls = F10_press (button); break; + case release: ls = F10_release (button); break; + case neither: break; + } + break; -LedState MackieControlProtocol::global_solo_press (Button &) -{ - bool state = !session->soloing(); - session->set_solo (session->get_routes(), state); - return state; -} + case 0x40: // F11 + switch (bs) { + case press: ls = F11_press (button); break; + case release: ls = F11_release (button); break; + case neither: break; + } + break; -LedState MackieControlProtocol::global_solo_release (Button &) -{ - return session->soloing(); -} + case 0x41: // F12 + switch (bs) { + case press: ls = F12_press (button); break; + case release: ls = F12_release (button); break; + case neither: break; + } + break; -/////////////////////////////////////////// -// Session signals -/////////////////////////////////////////// + case 0x42: // F13 + switch (bs) { + case press: ls = F13_press (button); break; + case release: ls = F13_release (button); break; + case neither: break; + } + break; -void MackieControlProtocol::notify_parameter_changed (std::string const & p) -{ - if (p == "punch-in") - { - update_global_button ("punch_in", session->config.get_punch_in()); - } - else if (p == "punch-out") - { - update_global_button ("punch_out", session->config.get_punch_out()); - } - else if (p == "clicking") - { - update_global_button ("clicking", Config->get_clicking()); - } - else - { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("parameter changed: %1\n", p)); - } -} + case 0x43: // F14 + switch (bs) { + case press: ls = F14_press (button); break; + case release: ls = F14_release (button); break; + case neither: break; + } + break; -// RouteList is the set of routes that have just been added -void -MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) -{ - // currently assigned banks are less than the full set of - // strips, so activate the new strip now. - if (route_signals.size() < route_table.size()) - { - refresh_current_bank(); - } - // otherwise route added, but current bank needs no updating + case 0x44: // F15 + switch (bs) { + case press: ls = F15_press (button); break; + case release: ls = F15_release (button); break; + case neither: break; + } + break; - // make sure remote id changes in the new route are handled - typedef ARDOUR::RouteList ARS; + case 0x45: // F16 + switch (bs) { + case press: ls = F16_press (button); break; + case release: ls = F16_release (button); break; + case neither: break; + } + break; - for (ARS::iterator it = rl.begin(); it != rl.end(); ++it) { - (*it)->RemoteControlIDChanged.connect (route_connections, ui_bind (&MackieControlProtocol::notify_remote_id_changed, this), midi_ui_context()); - } -} + case 0x46: // shift + switch (bs) { + case press: ls = shift_press (button); break; + case release: ls = shift_release (button); break; + case neither: break; + } + break; -void -MackieControlProtocol::notify_solo_active_changed (bool active) -{ - Button * rude_solo = reinterpret_cast (surface().controls_by_name["solo"]); - mcu_port().write (builder.build_led (*rude_solo, active ? flashing : off)); -} + case 0x47: // option + switch (bs) { + case press: ls = option_press (button); break; + case release: ls = option_release (button); break; + case neither: break; + } + break; -void -MackieControlProtocol::notify_remote_id_changed() -{ - Sorted sorted = get_sorted_routes(); + case Button::Ctrl: + switch (bs) { + case press: ls = control_press (button); break; + case release: ls = control_release (button); break; + case neither: break; + } + break; - // if a remote id has been moved off the end, we need to shift - // the current bank backwards. - if (sorted.size() - _current_initial_bank < route_signals.size()) - { - // but don't shift backwards past the zeroth channel - switch_banks (max((Sorted::size_type) 0, sorted.size() - route_signals.size())); - } - // Otherwise just refresh the current bank - else - { - refresh_current_bank(); - } -} + case 0x49: // cmd_alt + switch (bs) { + case press: ls = cmd_alt_press (button); break; + case release: ls = cmd_alt_release (button); break; + case neither: break; + } + break; -/////////////////////////////////////////// -// Transport signals -/////////////////////////////////////////// + case 0x4a: // on + switch (bs) { + case press: ls = on_press (button); break; + case release: ls = on_release (button); break; + case neither: break; + } + break; -void -MackieControlProtocol::notify_record_state_changed() -{ - // switch rec button on / off / flashing - Button * rec = reinterpret_cast (surface().controls_by_name["record"]); - mcu_port().write (builder.build_led (*rec, record_release (*rec))); -} + case 0x4b: // rec_ready + switch (bs) { + case press: ls = rec_ready_press (button); break; + case release: ls = rec_ready_release (button); break; + case neither: break; + } + break; -void -MackieControlProtocol::notify_transport_state_changed() -{ - // switch various play and stop buttons on / off - update_global_button ("play", session->transport_rolling()); - update_global_button ("stop", !session->transport_rolling()); - update_global_button ("loop", session->get_play_loop()); + case Button::Undo: // undo + switch (bs) { + case press: ls = undo_press (button); break; + case release: ls = undo_release (button); break; + case neither: break; + } + break; - _transport_previously_rolling = session->transport_rolling(); + case Button::Save: + switch (bs) { + case press: ls = save_press (button); break; + case release: ls = save_release (button); break; + case neither: break; + } + break; - // rec is special because it's tristate - Button * rec = reinterpret_cast (surface().controls_by_name["record"]); - mcu_port().write (builder.build_led (*rec, record_release (*rec))); -} + case Button::Touch: // touch + switch (bs) { + case press: ls = touch_press (button); break; + case release: ls = touch_release (button); break; + case neither: break; + } + break; -///////////////////////////////////// -// Bank Switching -///////////////////////////////////// -LedState -MackieControlProtocol::left_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) - { - int new_initial = _current_initial_bank - route_table.size(); - if (new_initial < 0) new_initial = 0; - if (new_initial != int (_current_initial_bank)) - { - session->set_dirty(); - switch_banks (new_initial); + case Button::Redo: // redo + switch (bs) { + case press: ls = redo_press (button); break; + case release: ls = redo_release (button); break; + case neither: break; } + break; - return on; - } - else - { - return flashing; - } -} + case Button::Marker: // marker + switch (bs) { + case press: ls = marker_press (button); break; + case release: ls = marker_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::left_release (Button &) -{ - return off; -} + case Button::Enter: // enter + switch (bs) { + case press: ls = enter_press (button); break; + case release: ls = enter_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::right_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) { - uint32_t delta = sorted.size() - (route_table.size() + _current_initial_bank); - if (delta > route_table.size()) delta = route_table.size(); - if (delta > 0) { - session->set_dirty(); - switch_banks (_current_initial_bank + delta); + case 0x52: // cancel + switch (bs) { + case press: ls = cancel_press (button); break; + case release: ls = cancel_release (button); break; + case neither: break; } + break; - return on; - } else { - return flashing; - } -} + case 0x53: // mixer + switch (bs) { + case press: ls = mixer_press (button); break; + case release: ls = mixer_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::right_release (Button &) -{ - return off; -} + case 0x54: // frm_left + switch (bs) { + case press: ls = frm_left_press (button); break; + case release: ls = frm_left_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::channel_left_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) - { - prev_track(); - return on; - } - else - { - return flashing; - } -} + case 0x55: // frm_right + switch (bs) { + case press: ls = frm_right_press (button); break; + case release: ls = frm_right_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::channel_left_release (Button &) -{ - return off; -} + case 0x56: // loop + switch (bs) { + case press: ls = loop_press (button); break; + case release: ls = loop_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::channel_right_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if (sorted.size() > route_table.size()) - { - next_track(); - return on; - } - else - { - return flashing; - } -} + case 0x57: // punch_in + switch (bs) { + case press: ls = punch_in_press (button); break; + case release: ls = punch_in_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::channel_right_release (Button &) -{ - return off; -} + case 0x58: // punch_out + switch (bs) { + case press: ls = punch_out_press (button); break; + case release: ls = punch_out_release (button); break; + case neither: break; + } + break; -///////////////////////////////////// -// Functions -///////////////////////////////////// -LedState -MackieControlProtocol::marker_press (Button &) -{ - // cut'n'paste from LocationUI::add_new_location() - string markername; - nframes_t where = session->audible_frame(); - session->locations()->next_available_name(markername,"mcu"); - Location *location = new Location (where, where, markername, Location::IsMark); - session->begin_reversible_command (_("add marker")); - XMLNode &before = session->locations()->get_state(); - session->locations()->add (location, true); - XMLNode &after = session->locations()->get_state(); - session->add_command (new MementoCommand(*(session->locations()), &before, &after)); - session->commit_reversible_command (); - return on; -} + case 0x59: // home + switch (bs) { + case press: ls = home_press (button); break; + case release: ls = home_release (button); break; + case neither: break; + } + break; -LedState -MackieControlProtocol::marker_release (Button &) -{ - return off; -} + case 0x5a: // end + switch (bs) { + case press: ls = end_press (button); break; + case release: ls = end_release (button); break; + case neither: break; + } + break; + + case Button::Rewind: + switch (bs) { + case press: ls = rewind_press (button); break; + case release: ls = rewind_release (button); break; + case neither: break; + } + break; + + case Button::Ffwd: + switch (bs) { + case press: ls = ffwd_press (button); break; + case release: ls = ffwd_release (button); break; + case neither: break; + } + break; + + case Button::Stop: + switch (bs) { + case press: ls = stop_press (button); break; + case release: ls = stop_release (button); break; + case neither: break; + } + break; + + case Button::Play: + switch (bs) { + case press: ls = play_press (button); break; + case release: ls = play_release (button); break; + case neither: break; + } + break; + + case 0x5f: // record + switch (bs) { + case press: ls = record_press (button); break; + case release: ls = record_release (button); break; + case neither: break; + } + break; + + case 0x60: // cursor_up + switch (bs) { + case press: ls = cursor_up_press (button); break; + case release: ls = cursor_up_release (button); break; + case neither: break; + } + break; + + case 0x61: // cursor_down + switch (bs) { + case press: ls = cursor_down_press (button); break; + case release: ls = cursor_down_release (button); break; + case neither: break; + } + break; + + case 0x62: // cursor_left + switch (bs) { + case press: ls = cursor_left_press (button); break; + case release: ls = cursor_left_release (button); break; + case neither: break; + } + break; + + case 0x63: // cursor_right + switch (bs) { + case press: ls = cursor_right_press (button); break; + case release: ls = cursor_right_release (button); break; + case neither: break; + } + break; + + case 0x64: // zoom + switch (bs) { + case press: ls = zoom_press (button); break; + case release: ls = zoom_release (button); break; + case neither: break; + } + break; + + case 0x65: // scrub + switch (bs) { + case press: ls = scrub_press (button); break; + case release: ls = scrub_release (button); break; + case neither: break; + } + break; + + case 0x66: // user_a + switch (bs) { + case press: ls = user_a_press (button); break; + case release: ls = user_a_release (button); break; + case neither: break; + } + break; + + case 0x67: // user_b + switch (bs) { + case press: ls = user_b_press (button); break; + case release: ls = user_b_release (button); break; + case neither: break; + } + break; -void -jog_wheel_state_display (JogWheel::State state, SurfacePort & port) -{ - switch (state) - { - case JogWheel::zoom: port.write (builder.two_char_display ("Zm")); break; - case JogWheel::scroll: port.write (builder.two_char_display ("Sc")); break; - case JogWheel::scrub: port.write (builder.two_char_display ("Sb")); break; - case JogWheel::shuttle: port.write (builder.two_char_display ("Sh")); break; - case JogWheel::speed: port.write (builder.two_char_display ("Sp")); break; - case JogWheel::select: port.write (builder.two_char_display ("Se")); break; } -} -Mackie::LedState -MackieControlProtocol::zoom_press (Mackie::Button &) -{ - _jog_wheel.zoom_state_toggle(); - update_global_button ("scrub", _jog_wheel.jog_wheel_state() == JogWheel::scrub); - jog_wheel_state_display (_jog_wheel.jog_wheel_state(), mcu_port()); - return _jog_wheel.jog_wheel_state() == JogWheel::zoom; + update_led (surface, button, ls); } -Mackie::LedState -MackieControlProtocol::zoom_release (Mackie::Button &) +void +MackieControlProtocol::select_track (boost::shared_ptr r) { - return _jog_wheel.jog_wheel_state() == JogWheel::zoom; + if (_modifier_state == MODIFIER_SHIFT) { + r->gain_control()->set_value (0.0); + } else { + if (_current_selected_track > 0 && r->remote_control_id() == (uint32_t) _current_selected_track) { + UnselectTrack (); /* EMIT SIGNAL */ + _current_selected_track = -1; + } else { + SelectByRID (r->remote_control_id()); /* EMIT SIGNAL */ + _current_selected_track = r->remote_control_id();; + } + } } -Mackie::LedState -MackieControlProtocol::scrub_press (Mackie::Button &) +bool +MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port) { - _jog_wheel.scrub_state_cycle(); - update_global_button ("zoom", _jog_wheel.jog_wheel_state() == JogWheel::zoom); - jog_wheel_state_display (_jog_wheel.jog_wheel_state(), mcu_port()); - return - _jog_wheel.jog_wheel_state() == JogWheel::scrub - || - _jog_wheel.jog_wheel_state() == JogWheel::shuttle - ; -} + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("something happend on %1\n", port->name())); -Mackie::LedState -MackieControlProtocol::scrub_release (Mackie::Button &) -{ - return - _jog_wheel.jog_wheel_state() == JogWheel::scrub - || - _jog_wheel.jog_wheel_state() == JogWheel::shuttle - ; -} + if (ioc & ~IO_IN) { + return false; + } -LedState -MackieControlProtocol::drop_press (Button &) -{ - session->remove_last_capture(); - return on; -} + if (ioc & IO_IN) { -LedState -MackieControlProtocol::drop_release (Button &) -{ - return off; -} + CrossThreadChannel::drain (port->selectable()); -LedState -MackieControlProtocol::save_press (Button &) -{ - session->save_state (""); - return on; -} + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name())); + framepos_t now = session->engine().frame_time(); + port->parse (now); + } -LedState -MackieControlProtocol::save_release (Button &) -{ - return off; + return true; } -LedState -MackieControlProtocol::timecode_beats_press (Button &) +void +MackieControlProtocol::clear_ports () { - switch (_timecode_type) - { - case ARDOUR::AnyTime::BBT: - _timecode_type = ARDOUR::AnyTime::Timecode; - break; - case ARDOUR::AnyTime::Timecode: - _timecode_type = ARDOUR::AnyTime::BBT; - break; - default: - ostringstream os; - os << "Unknown Anytime::Type " << _timecode_type; - throw runtime_error (os.str()); + for (PortSources::iterator i = port_sources.begin(); i != port_sources.end(); ++i) { + g_source_destroy (*i); + g_source_unref (*i); } - update_timecode_beats_led(); - return on; -} -LedState -MackieControlProtocol::timecode_beats_release (Button &) -{ - return off; + port_sources.clear (); }