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
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <fcntl.h>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <iomanip>
-#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <float.h>
#include <sys/time.h>
#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<MackieControlUIRequest> ("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<const MackiePort &>( *_ports[0] );
- }
+ _instance = 0;
}
-Mackie::SurfacePort & MackieControlProtocol::mcu_port()
+void
+MackieControlProtocol::thread_init ()
{
- if ( _ports.size() < 1 )
- {
- return _dummy_port;
- }
- else
- {
- return dynamic_cast<MackiePort &>( *_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<Route> & a, const shared_ptr<Route> & b ) const
+ bool operator () (const boost::shared_ptr<Route> & a, const boost::shared_ptr<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();
}
- 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;
// 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> 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<boost::shared_ptr<Route> > 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<MackiePort&>( 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<Glib::TimeoutSource> 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> 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<Button&>( 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<Button&>( 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<const Strip&>( 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<Button*>( surface().controls_by_name[name] );
- mcu_port().write( builder.build_led( button->led(), ls ) );
+ boost::shared_ptr<Surface> 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<Button*> (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<Led*>( surface().controls_by_name[name] );
- mcu_port().write( builder.build_led( *led, ls ) );
+ boost::shared_ptr<Surface> 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<Led*> (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<Route> mr = master_route ();
- if (mr) {
- master_route_signal = shared_ptr<RouteSignal> (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<Route> 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<Strip&>( *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> 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<IOSource> 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<sigc::connection>::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<MackieControlProtocol*>(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;
}
}
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> 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<Button&>( 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<Pot &>( 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> 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> 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> surface = surfaces.front();
+
+ Button * rude_solo = reinterpret_cast<Button*> (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<Button*> (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<boost::shared_ptr<ARDOUR::Bundle> >
+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<boost::shared_ptr<ARDOUR::Bundle> > 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<Glib::TimeoutSource> 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<Button*>( 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<Button*>( 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<Button*>( 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<Locations>(*(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<Route> 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 ();
}
+