X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fsurfaces%2Fmackie%2Fmackie_control_protocol.cc;h=d7fda8ad7f2e79db1a038330906b0e68d230a7cb;hb=f387e834fd8966eff5f71955f013a905db9f7153;hp=d93bf78d3393b5019af8b226bbd518566b47fc44;hpb=c83e48e07a0b4790512c6251d8ad8f941c881021;p=ardour.git diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index d93bf78d33..d7fda8ad7f 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -8,7 +8,7 @@ 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 + 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 @@ -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,186 +34,168 @@ #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" #include "pbd/convert.h" -#include "ardour/session.h" -#include "ardour/route.h" -#include "ardour/location.h" #include "ardour/dB.h" +#include "ardour/debug.h" +#include "ardour/location.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 sigc; 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 -MackieControlProtocol::MackieControlProtocol (Session& session) - : ControlProtocol (session, X_("Mackie")) - , _current_initial_bank( 0 ) - , connections_back( _connections ) - , _surface( 0 ) - , _ports_changed( false ) - , _polling( true ) - , pfd( 0 ) - , nfds( 0 ) - , _jog_wheel( *this ) - , _timecode_type( ARDOUR::AnyTime::BBT ) +#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() { -#ifdef DEBUG - cout << "MackieControlProtocol::MackieControlProtocol" << endl; -#endif - // will start reading from ports, as soon as there are some - pthread_create_and_store (X_("mackie monitor"), &thread, 0, _monitor_work, this); + return true; +} + +MackieControlProtocol::MackieControlProtocol (Session& session) + : ControlProtocol (session, X_("Mackie"), this) + , AbstractUI ("mackie") + , _current_initial_bank (0) + , _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() { -#ifdef DEBUG - cout << "~MackieControlProtocol::MackieControlProtocol" << endl; -#endif - try - { + DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol\n"); + + _active = false; + + try { close(); } - catch ( exception & e ) - { + catch (exception & e) { cout << "~MackieControlProtocol caught " << e.what() << endl; } - catch ( ... ) - { + catch (...) { cout << "~MackieControlProtocol caught unknown" << endl; } -#ifdef DEBUG - cout << "finished ~MackieControlProtocol::MackieControlProtocol" << endl; -#endif -} -Mackie::Surface & MackieControlProtocol::surface() -{ - if ( _surface == 0 ) - { - throw MackieControlException( "_surface is 0 in MackieControlProtocol::surface" ); - } - return *_surface; -} + DEBUG_TRACE (DEBUG::MackieControl, "finished ~MackieControlProtocol::MackieControlProtocol\n"); -const Mackie::SurfacePort & MackieControlProtocol::mcu_port() const -{ - if ( _ports.size() < 1 ) - { - return _dummy_port; - } - else - { - return dynamic_cast( *_ports[0] ); - } + _instance = 0; } -Mackie::SurfacePort & MackieControlProtocol::mcu_port() +void +MackieControlProtocol::thread_init () { - if ( _ports.size() < 1 ) - { - return _dummy_port; - } - else - { - return dynamic_cast( *_ports[0] ); + struct sched_param rtparam; + + 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. } } // go to the previous track. // Assume that get_sorted_routes().size() > route_table.size() -void MackieControlProtocol::prev_track() +void +MackieControlProtocol::prev_track() { - if ( _current_initial_bank >= 1 ) - { + if (_current_initial_bank >= 1) { session->set_dirty(); - switch_banks( _current_initial_bank - 1 ); + switch_banks (_current_initial_bank - 1); } } // go to the next track. // Assume that get_sorted_routes().size() > route_table.size() -void MackieControlProtocol::next_track() +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; + switch_banks (_current_initial_bank + 1); } - - // 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(); } - bool operator () ( const Route & a, const Route & b ) const + bool operator () (const Route & a, const Route & b) const { return a.remote_control_id() < b.remote_control_id(); } - bool operator () ( const Route * a, const Route * b ) const + bool operator () (const Route * a, const Route * b) const { return a->remote_control_id() < b->remote_control_id(); } }; -MackieControlProtocol::Sorted MackieControlProtocol::get_sorted_routes() +MackieControlProtocol::Sorted +MackieControlProtocol::get_sorted_routes() { Sorted sorted; @@ -223,611 +205,368 @@ MackieControlProtocol::Sorted MackieControlProtocol::get_sorted_routes() // routes with remote_id 0 should never be added // TODO verify this with ardour devs - // remote_ids.insert( 0 ); + // remote_ids.insert (0); // 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() - ) - { - sorted.push_back( *it ); - remote_ids.insert( route.remote_control_id() ); + 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()); } } - sort( sorted.begin(), sorted.end(), RouteByRemoteId() ); + sort (sorted.begin(), sorted.end(), RouteByRemoteId()); return sorted; } -void MackieControlProtocol::refresh_current_bank() +void +MackieControlProtocol::refresh_current_bank() +{ + switch_banks (_current_initial_bank, true); +} + +uint32_t +MackieControlProtocol::n_strips() const { - switch_banks( _current_initial_bank ); + 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 ) +void +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 ) ) - { -#ifdef DEBUG - cout << "not switching to " << initial << endl; -#endif + 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; - // first clear the signals from old routes - // taken care of by the RouteSignal destructors - clear_route_signals(); + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + (*si)->drop_routes (); + } - // 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; -#ifdef DEBUG - cout << "switch to " << _current_initial_bank << ", " << end_pos << endl; -#endif + // Map current bank of routes onto each surface(+strip) + + if (_current_initial_bank <= sorted.size()) { + + 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]; -#ifdef DEBUG - cout << "remote id " << route->remote_control_id() << " connecting " << route->name() << " to " << strip.name() << " with port " << port_for_id(i) << endl; -#endif - 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 ) ); + + Sorted::iterator r = sorted.begin() + _current_initial_bank; + + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + vector > routes; + uint32_t added = 0; + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface has %1 strips\n", (*si)->n_strips())); + + for (; r != sorted.end() && added < (*si)->n_strips(); ++r, ++added) { + routes.push_back (*r); + cerr << "\t\tadded " << (*r)->name() << endl; + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 routes\n", routes.size())); + + (*si)->map_routes (routes); } } // display the current start bank. - surface().display_bank_start( mcu_port(), builder, _current_initial_bank ); + surfaces.front()->display_bank_start (_current_initial_bank); } -void MackieControlProtocol::zero_all() +int +MackieControlProtocol::set_active (bool yn) { - // TODO turn off Timecode displays + if (yn == _active) { + return 0; + } - // zero all strips - for ( Surface::Strips::iterator it = surface().strips.begin(); it != surface().strips.end(); ++it ) + try { - MackiePort & port = port_for_id( (*it)->index() ); - port.write( builder.zero_strip( port, **it ) ); - } + if (yn) { - // and the master strip - mcu_port().write( builder.zero_strip( dynamic_cast( mcu_port() ), master_strip() ) ); + /* start event loop */ - // 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 ) ); - } - } + BaseUI::run (); - // any hardware-specific stuff - surface().zero_all( mcu_port(), builder ); -} + create_surfaces (); + connect_session_signals (); + + _active = true; + update_surfaces (); -int MackieControlProtocol::set_active( bool yn ) -{ - if ( yn != _active ) - { - 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(); - } - - // make sure the ports are being listened to - update_ports(); - - // wait until poll thread is running, with ports to poll - // the mutex is only there because conditions require a mutex - { - Glib::Mutex::Lock lock( update_mutex ); - while ( nfds == 0 ) update_cond.wait( update_mutex ); - } - - // 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; - } - } - catch( exception & e ) - { -#ifdef DEBUG - cout << "set_active to false because exception caught: " << e.what() << endl; -#endif + /* 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() +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 ); + update_global_led ("beats", on); + update_global_led ("timecode", off); break; case ARDOUR::AnyTime::Timecode: - update_global_led( "timecode", on ); - update_global_led( "beats", off ); + update_global_led ("timecode", on); + update_global_led ("beats", off); break; default: ostringstream os; os << "Unknown Anytime::Type " << _timecode_type; - throw runtime_error( os.str() ); + throw runtime_error (os.str()); } } -void MackieControlProtocol::update_global_button( const string & name, LedState ls ) +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 - { -#ifdef DEBUG - cout << "Button " << name << " not found" << endl; -#endif + + 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)); } } -void MackieControlProtocol::update_global_led( 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 - { -#ifdef DEBUG - cout << "Led " << name << " not found" << endl; -#endif + + 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() +void +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() +void +MackieControlProtocol::connect_session_signals() { // receive routes added - connections_back = session->RouteAdded.connect( ( mem_fun (*this, &MackieControlProtocol::notify_route_added) ) ); + session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_route_added, this, _1), this); // receive record state toggled - connections_back = session->RecordStateChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_record_state_changed) ) ); + session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_record_state_changed, this), this); // receive transport state changed - connections_back = session->TransportStateChange.connect( ( mem_fun (*this, &MackieControlProtocol::notify_transport_state_changed) ) ); + 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 - connections_back = Config->ParameterChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_parameter_changed) ) ); - session->config.ParameterChanged.connect ( ( mem_fun (*this, &MackieControlProtocol::notify_parameter_changed) ) ); + 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 - connections_back = session->SoloActive.connect( ( mem_fun (*this, &MackieControlProtocol::notify_solo_active_changed) ) ); + 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 ) - { - connections_back = (*it)->RemoteControlIDChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_remote_id_changed) ) ); - } -} -void MackieControlProtocol::add_port( MIDI::Port & midi_port, int number ) -{ -#ifdef DEBUG - cout << "add port " << midi_port.name() << ", " << midi_port.device() << ", " << midi_port.type() << endl; - cout << "MIDI::Port::ALSA_Sequencer " << MIDI::Port::ALSA_Sequencer << endl; - cout << "MIDI::Port::Unknown " << MIDI::Port::Unknown << endl; -#endif - 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 ); - - connections_back = sport->init_event.connect( - sigc::bind ( - mem_fun (*this, &MackieControlProtocol::handle_port_init) - , sport - ) - ); - - connections_back = sport->active_event.connect( - sigc::bind ( - mem_fun (*this, &MackieControlProtocol::handle_port_active) - , sport - ) - ); - - connections_back = sport->inactive_event.connect( - sigc::bind ( - mem_fun (*this, &MackieControlProtocol::handle_port_inactive) - , sport - ) - ); - - _ports_changed = true; + for (Sorted::iterator it = sorted.begin(); it != sorted.end(); ++it) { + (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, ui_bind(&MackieControlProtocol::notify_remote_id_changed, this), this); } } -void MackieControlProtocol::create_ports() +void +MackieControlProtocol::create_surfaces () { - MIDI::Manager * mm = MIDI::Manager::instance(); - - // open main port - { - MIDI::Port * midi_port = mm->port( default_port_name ); - - 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() ); - } - add_port( *midi_port, 0 ); - } - - // open extender ports. Up to 9. Should be enough. - // could also use mm->get_midi_ports() - string ext_port_base = "mcu_xt_"; - 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 ); - } -} + string device_name = "mcu"; + surface_type_t stype = mcu; -shared_ptr MackieControlProtocol::master_route() -{ - return session->master_out (); -} + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", + 1 + ARDOUR::Config->get_mackie_extenders())); -Strip & MackieControlProtocol::master_strip() -{ - return dynamic_cast( *surface().groups["master"] ); -} + for (uint32_t n = 0; n < 1 + ARDOUR::Config->get_mackie_extenders(); ++n) { -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(); - } + boost::shared_ptr surface (new Surface (*this, session->engine().jack(), device_name, n, stype)); + surfaces.push_back (surface); + + device_name = "mcu_xt"; + stype = ext; - set_route_table_size( strips ); + _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()) + ); - // 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() ); - } + 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); - _surface->init(); + 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* - // Connect events. Must be after route table otherwise there will be trouble - for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it ) - { - connections_back = (*it)->control_event.connect( ( mem_fun (*this, &MackieControlProtocol::handle_control_event) ) ); + port_sources.push_back (psrc->gobj()); + g_source_ref (psrc->gobj()); + } } } -void MackieControlProtocol::close() +void +MackieControlProtocol::close() { - // stop polling, and wait for it... - // must be before other shutdown otherwise polling loop - // calls methods on objects that are deleted - _polling = false; - pthread_join( thread, 0 ); - - // TODO disconnect port active/inactive signals - // Or at least put a lock here - - // disconnect global signals from Session - // TODO Since *this is a sigc::trackable, this shouldn't be necessary - // but it is for some reason -#if 0 - for( vector::iterator it = _connections.begin(); it != _connections.end(); ++it ) - { - it->disconnect(); - } -#endif - - 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 ) - { -#ifdef DEBUG - cout << "MackieControlProtocol::close caught exception: " << e.what() << endl; -#endif - } - - 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 ) - { -#ifdef DEBUG - cout << "MackieControlProtocol::close caught exception: " << e.what() << endl; -#endif - } - } - - // 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(); + clear_ports (); - // this is done already in monitor_work. But it's here so we know. - delete[] pfd; - pfd = 0; - nfds = 0; -} + port_connections.drop_connections (); + session_connections.drop_connections (); + route_connections.drop_connections (); + periodic_connection.disconnect (); -void* MackieControlProtocol::_monitor_work (void* arg) -{ - return static_cast(arg)->monitor_work (); + surfaces.clear (); } -XMLNode & MackieControlProtocol::get_state() +XMLNode& +MackieControlProtocol::get_state() { -#ifdef DEBUG - cout << "MackieControlProtocol::get_state" << endl; -#endif + DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::get_state\n"); // add name of protocol - XMLNode* node = new XMLNode( X_("Protocol") ); - node->add_property( X_("name"), _name ); + XMLNode* node = new XMLNode (X_("Protocol")); + node->add_property (X_("name"), ARDOUR::ControlProtocol::_name); // add current bank ostringstream os; os << _current_initial_bank; - node->add_property( X_("bank"), os.str() ); + node->add_property (X_("bank"), os.str()); return *node; } -int MackieControlProtocol::set_state (const XMLNode & node, int /*version*/) +int +MackieControlProtocol::set_state (const XMLNode & node, int /*version*/) { -#ifdef DEBUG - cout << "MackieControlProtocol::set_state: active " << _active << endl; -#endif + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::set_state: active %1\n", _active)); + int retval = 0; // fetch current bank - if ( node.property( X_("bank") ) != 0 ) - { - string bank = node.property( X_("bank") )->value(); - try - { - set_active( true ); - uint32_t new_bank = atoi( bank.c_str() ); - if ( _current_initial_bank != new_bank ) switch_banks( new_bank ); - } - catch ( exception & e ) - { -#ifdef DEBUG - cout << "exception in MackieControlProtocol::set_state: " << e.what() << endl; -#endif + + if (node.property (X_("bank")) != 0) { + string bank = node.property (X_("bank"))->value(); + try { + set_active (true); + uint32_t new_bank = atoi (bank.c_str()); + if (_current_initial_bank != new_bank) { + switch_banks (new_bank); + } + } + catch (exception & e) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("exception in MackieControlProtocol::set_state: %1\n", e.what())); return -1; } } @@ -835,903 +574,881 @@ int 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 // TODO should these be part of RouteSignal? -// They started off as sigc handlers for signals +// They started off as signal/slot handlers for signals // 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 ) +void +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 *) -{ - try - { -#ifdef DEBUG - cout << "MackieControlProtocol::notify_active_changed" << endl; -#endif - refresh_current_bank(); - } - catch( exception & e ) - { - cout << e.what() << endl; - } -} +/////////////////////////////////////////// +// Session signals +/////////////////////////////////////////// -void MackieControlProtocol::notify_gain_changed( RouteSignal * route_signal, bool force_update ) +void MackieControlProtocol::notify_parameter_changed (std::string const & p) { - 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; + 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)); } } -void MackieControlProtocol::notify_name_changed( RouteSignal * route_signal ) +// RouteList is the set of routes that have just been added +void +MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) { - 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 ); - } + // currently assigned banks are less than the full set of + // strips, so activate the new strip now. - 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; - } -} + refresh_current_bank(); -void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal, bool force_update ) -{ - 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; + // 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); } } -// TODO handle plugin automation polling -void MackieControlProtocol::update_automation( RouteSignal & rs ) +void +MackieControlProtocol::notify_solo_active_changed (bool active) { - ARDOUR::AutoState gain_state = rs.route()->gain_control()->automation_state(); - if ( gain_state == Touch || gain_state == Play ) - { - notify_gain_changed( &rs, false ); - } + boost::shared_ptr surface = surfaces.front(); + + Button * rude_solo = reinterpret_cast (surface->controls_by_name["solo"]); - 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 (rude_solo) { + surface->write (builder.build_led (*rude_solo, active ? flashing : off)); } - _automation_last.start(); } -string MackieControlProtocol::format_bbt_timecode( nframes_t now_frame ) +void +MackieControlProtocol::notify_remote_id_changed() { - BBT_Time bbt_time; - session->bbt_time( now_frame, bbt_time ); + Sorted sorted = get_sorted_routes(); + uint32_t sz = n_strips(); - // 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; + // if a remote id has been moved off the end, we need to shift + // the current bank backwards. - // 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; + 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(); } +} - 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 ); - - os << setw(2) << setfill('0') << subdivisions + 1; - os << setw(3) << setfill('0') << ticks; +/////////////////////////////////////////// +// Transport signals +/////////////////////////////////////////// - return os.str(); +void +MackieControlProtocol::notify_loop_state_changed() +{ + update_global_button ("loop", session->get_play_loop()); } -string MackieControlProtocol::format_timecode_timecode( nframes_t now_frame ) +void +MackieControlProtocol::notify_transport_state_changed() { - Timecode::Time timecode; - session->timecode_time( now_frame, timecode ); + // 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); - // 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; + _transport_previously_rolling = session->transport_rolling(); - return os.str(); } -void MackieControlProtocol::update_timecode_display() +void +MackieControlProtocol::notify_record_state_changed () { - 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; + Button * rec = reinterpret_cast (surfaces.front()->controls_by_name["record"]); + if (rec) { + LedState ls; + + switch (session->record_status()) { + case Session::Disabled: + ls = off; + break; + case Session::Recording: + ls = on; + break; + case Session::Enabled: + ls = flashing; + break; } + + surfaces.front()->write (builder.build_led (*rec, ls)); } } -void MackieControlProtocol::poll_session_data() +list > +MackieControlProtocol::bundles () { - if ( _active && _automation_last.elapsed() >= 20 ) - { - // do all currently mapped routes - for( RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it ) - { - update_automation( **it ); - } + list > b; + b.push_back (_input_bundle); + b.push_back (_output_bundle); + return b; +} - // and the master strip - if ( master_route_signal != 0 ) - { - update_automation( *master_route_signal ); - } +void +MackieControlProtocol::port_connected_or_disconnected (string a, string b, bool connected) +{ + /* If something is connected to one of our output ports, send MIDI to update the surface + to whatever state it should have. + */ - update_timecode_display(); + if (!connected) { + return; + } - _automation_last.start(); + 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; + } } } -///////////////////////////////////// -// 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(); - return on; + if (touch_control) { + // empty control_state + ControlState control_state; + surface->handle_control_event (*touch_control, control_state); + } + + // 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; + if (ls != none) { + surface.port().write (builder.build_led (button, ls)); } - else - return off; } -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_all_solo ( 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 - { -#ifdef DEBUG - cout << "parameter changed: " << p << endl; -#endif - } -} + 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; - for ( ARS::iterator it = rl.begin(); it != rl.end(); ++it ) - { - connections_back = (*it)->RemoteControlIDChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_remote_id_changed) ) ); - } -} + case 0x45: // F16 + switch (bs) { + case press: ls = F16_press (button); break; + case release: ls = F16_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 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_remote_id_changed() -{ - Sorted sorted = get_sorted_routes(); + case 0x47: // option + switch (bs) { + case press: ls = option_press (button); break; + case release: ls = option_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 Button::Ctrl: + switch (bs) { + case press: ls = control_press (button); break; + case release: ls = control_release (button); break; + case neither: break; + } + break; -/////////////////////////////////////////// -// Transport signals -/////////////////////////////////////////// + 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; -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 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_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 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; - _transport_previously_rolling = session->transport_rolling(); + case Button::Undo: // undo + switch (bs) { + case press: ls = undo_press (button); break; + case release: ls = undo_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::Save: + switch (bs) { + case press: ls = save_press (button); break; + case release: ls = save_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::Touch: // touch + switch (bs) { + case press: ls = touch_press (button); break; + case release: ls = touch_release (button); break; + case neither: break; } + break; - return on; - } - else - { - return flashing; - } -} + case Button::Redo: // redo + switch (bs) { + case press: ls = redo_press (button); break; + case release: ls = redo_release (button); break; + case neither: break; + } + break; -LedState MackieControlProtocol::left_release (Button &) -{ - return off; -} + 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::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 Button::Enter: // enter + switch (bs) { + case press: ls = enter_press (button); break; + case release: ls = enter_release (button); break; + case neither: break; } + break; - return on; - } - else - { - return flashing; - } -} + case 0x52: // cancel + switch (bs) { + case press: ls = cancel_press (button); break; + case release: ls = cancel_release (button); break; + case neither: break; + } + break; -LedState MackieControlProtocol::right_release (Button &) -{ - return off; -} + 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::channel_left_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if ( sorted.size() > route_table.size() ) - { - prev_track(); - return on; - } - else - { - return flashing; - } -} + 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_release (Button &) -{ - return off; -} + 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_right_press (Button &) -{ - Sorted sorted = get_sorted_routes(); - if ( sorted.size() > route_table.size() ) - { - next_track(); - return on; - } - else - { - return flashing; - } -} + 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_release (Button &) -{ - return off; -} + 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; -///////////////////////////////////// -// 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 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; -LedState MackieControlProtocol::marker_release (Button &) -{ - return off; -} + case 0x59: // home + switch (bs) { + case press: ls = home_press (button); break; + case release: ls = home_release (button); break; + case neither: break; + } + break; + + 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 (); } +