#include <boost/shared_array.hpp>
-#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/route.h>
-#include <ardour/session.h>
-#include <ardour/location.h>
-#include <ardour/dB.h>
-#include <ardour/panner.h>
-#include <ardour/tempo.h>
-#include <ardour/types.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/panner.h"
+#include "ardour/tempo.h"
+#include "ardour/types.h"
#include "mackie_control_protocol.h"
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);
+ pthread_create_and_store (X_("mackie monitor"), &thread, _monitor_work, this);
}
MackieControlProtocol::~MackieControlProtocol()
current_max += (*it)->strips();
if ( index < current_max ) return **it;
}
-
+
// oops - no matching port
ostringstream os;
os << "No port for index " << index;
MackieControlProtocol::Sorted MackieControlProtocol::get_sorted_routes()
{
Sorted sorted;
-
+
// fetch all routes
boost::shared_ptr<RouteList> routes = session->get_routes();
set<uint32_t> remote_ids;
-
+
// routes with remote_id 0 should never be added
// TODO verify this with ardour devs
// 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 )
{
// DON'T prevent bank switch if initial == _current_initial_bank
// because then this method can't be used as a refresh
-
+
// sanity checking
Sorted sorted = get_sorted_routes();
int delta = sorted.size() - route_table.size();
return;
}
_current_initial_bank = initial;
-
+
// first clear the signals from old routes
// taken care of by the RouteSignal destructors
clear_route_signals();
-
+
// now set the signals for new routes
if ( _current_initial_bank <= sorted.size() )
{
#ifdef DEBUG
cout << "switch to " << _current_initial_bank << ", " << end_pos << endl;
#endif
-
+
// link routes to strips
uint32_t i = 0;
for ( ; it != end && it != sorted.end(); ++it, ++i )
// 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 )
port.write( builder.zero_strip( port, strip ) );
}
}
-
+
// display the current start bank.
surface().display_bank_start( mcu_port(), builder, _current_initial_bank );
}
void MackieControlProtocol::zero_all()
{
- // TODO turn off SMPTE displays
-
+ // TODO turn off Timecode displays
+
// zero all strips
for ( Surface::Strips::iterator it = surface().strips.begin(); it != surface().strips.end(); ++it )
{
MackiePort & port = port_for_id( (*it)->index() );
port.write( builder.zero_strip( port, **it ) );
}
-
+
// and the master strip
mcu_port().write( builder.zero_strip( dynamic_cast<MackiePort&>( mcu_port() ), master_strip() ) );
-
+
// turn off global buttons and leds
// global buttons are only ever on mcu_port, so we don't have
// to figure out which port.
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
{
(*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();
//state = default_button_press( dynamic_cast<Button&>( control ) );
}
}
-
+
if ( control.name() == "fader_touch" )
{
state = bs == press;
control.strip().gain().in_use( state );
}
-
+
return state;
}
}
}
-void MackieControlProtocol::update_smpte_beats_led()
+void MackieControlProtocol::update_timecode_beats_led()
{
switch ( _timecode_type )
{
case ARDOUR::AnyTime::BBT:
update_global_led( "beats", on );
- update_global_led( "smpte", off );
+ update_global_led( "timecode", off );
break;
- case ARDOUR::AnyTime::SMPTE:
- update_global_led( "smpte", on );
+ case ARDOUR::AnyTime::Timecode:
+ update_global_led( "timecode", on );
update_global_led( "beats", off );
break;
default:
// 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 ();
// 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_smpte_beats_led();
+ update_timecode_beats_led();
}
}
session->config.ParameterChanged.connect ( ( mem_fun (*this, &MackieControlProtocol::notify_parameter_changed) ) );
// receive rude solo changed
connections_back = session->SoloActive.connect( ( mem_fun (*this, &MackieControlProtocol::notify_solo_active_changed) ) );
-
+
// make sure remote id changed signals reach here
// see also notify_route_added
Sorted sorted = get_sorted_routes();
{
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
)
);
-
+
_ports_changed = true;
}
}
}
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_";
{
strips += (*it)->strips();
}
-
+
set_route_table_size( strips );
-
+
// TODO same as code in mackie_port.cc
string emulation = ARDOUR::Config->get_mackie_emulation();
if ( emulation == "bcf" )
}
_surface->init();
-
+
// Connect events. Must be after route table otherwise there will be trouble
for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
{
// 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
it->disconnect();
}
#endif
-
+
if ( _surface != 0 )
{
// These will fail if the port has gone away.
cout << "MackieControlProtocol::close caught exception: " << e.what() << endl;
#endif
}
-
+
for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
{
try
#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();
-
+
// this is done already in monitor_work. But it's here so we know.
delete[] pfd;
pfd = 0;
#ifdef DEBUG
cout << "MackieControlProtocol::get_state" << endl;
#endif
-
+
// add name of protocol
XMLNode* node = new XMLNode( X_("Protocol") );
node->add_property( X_("name"), _name );
-
+
// add current bank
ostringstream os;
os << _current_initial_bank;
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
int retval = 0;
-
+
// fetch current bank
if ( node.property( X_("bank") ) != 0 )
{
return -1;
}
}
-
+
return retval;
}
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.
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() )
{
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() )
// 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 );
}
}
}
}
break;
-
+
default:
cout << "Control::type not handled: " << control.type() << endl;
}
cout << e.what() << endl;
}
}
-
+
void MackieControlProtocol::notify_gain_changed( RouteSignal * route_signal, bool force_update )
{
try
{
string line1;
string fullname = route_signal->route()->name();
-
+
if ( fullname.length() <= 6 )
{
line1 = fullname;
{
line1 = PBD::short_version( fullname, 6 );
}
-
+
SurfacePort & port = route_signal->port();
port.write( builder.strip_display( port, strip, 0, line1 ) );
port.write( builder.strip_display_blank( port, strip, 1 ) );
{
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
{
notify_gain_changed( &rs, false );
}
-
+
if ( rs.route()->panner() ) {
ARDOUR::AutoState panner_state = rs.route()->panner()->automation_state();
if ( panner_state == Touch || panner_state == Play )
{
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 Meter & meter = session->tempo_map().meter_at( now_frame );
int subdiv = 2;
{
subdiv = 3;
}
-
+
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;
-
+
return os.str();
}
-string MackieControlProtocol::format_smpte_timecode( nframes_t now_frame )
+string MackieControlProtocol::format_timecode_timecode( nframes_t now_frame )
{
- SMPTE::Time smpte;
- session->smpte_time( now_frame, smpte );
+ Timecode::Time timecode;
+ session->timecode_time( now_frame, timecode );
// According to the Logic docs
// digits: 888/88/88/888
- // SMPTE mode: Hours/Minutes/Seconds/Frames
+ // Timecode mode: Hours/Minutes/Seconds/Frames
ostringstream os;
- os << setw(3) << setfill('0') << smpte.hours;
- os << setw(2) << setfill('0') << smpte.minutes;
- os << setw(2) << setfill('0') << smpte.seconds;
- os << setw(3) << setfill('0') << smpte.frames;
-
+ 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();
}
// 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::SMPTE:
- timecode = format_smpte_timecode( current_frame );
+ 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 )
{
update_automation( **it );
}
-
+
// and the master strip
if ( master_route_signal != 0 )
{
update_automation( *master_route_signal );
}
-
+
update_timecode_display();
-
+
_automation_last.start();
}
}
{
// can use first_mark_before/after as well
unsigned long elapsed = _frm_left_last.restart();
-
+
Location * loc = session->locations()->first_location_before (
session->transport_frame()
);
-
- // allow a quick double to go past a previous mark
+
+ // 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() );
loc = loc_two_back;
}
}
-
+
// move to the location, if it's valid
if ( loc != 0 )
{
session->request_locate( loc->start(), session->transport_rolling() );
}
-
+
return on;
}
LedState MackieControlProtocol::global_solo_press (Button &)
{
bool state = !session->soloing();
- session->set_all_solo ( state );
+ session->set_solo (session->get_routes(), state);
return state;
}
refresh_current_bank();
}
// 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 )
void MackieControlProtocol::notify_remote_id_changed()
{
Sorted sorted = get_sorted_routes();
-
+
// 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() )
update_global_button( "play", session->transport_rolling() );
update_global_button( "stop", !session->transport_rolling() );
update_global_button( "loop", session->get_play_loop() );
-
+
_transport_previously_rolling = session->transport_rolling();
-
+
// 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 ) ) );
session->set_dirty();
switch_banks( new_initial );
}
-
+
return on;
}
else
session->set_dirty();
switch_banks( _current_initial_bank + delta );
}
-
+
return on;
}
else
return off;
}
-LedState MackieControlProtocol::smpte_beats_press (Button &)
+LedState MackieControlProtocol::timecode_beats_press (Button &)
{
switch ( _timecode_type )
{
case ARDOUR::AnyTime::BBT:
- _timecode_type = ARDOUR::AnyTime::SMPTE;
+ _timecode_type = ARDOUR::AnyTime::Timecode;
break;
- case ARDOUR::AnyTime::SMPTE:
+ case ARDOUR::AnyTime::Timecode:
_timecode_type = ARDOUR::AnyTime::BBT;
break;
default:
os << "Unknown Anytime::Type " << _timecode_type;
throw runtime_error( os.str() );
}
- update_smpte_beats_led();
+ update_timecode_beats_led();
return on;
}
-LedState MackieControlProtocol::smpte_beats_release( Button & )
+LedState MackieControlProtocol::timecode_beats_release( Button & )
{
return off;
}