substantial overhaul of MCU code - no more separate thread, just connect to signals on ports already listened to by the MidiUI thread in libardour (feedback is missing - needs a timeout connection); also reformat some big chunks of code to fit ardour coding style

git-svn-id: svn://localhost/ardour2/branches/3.0@6377 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2009-12-20 16:49:55 +00:00
parent aae367b63c
commit 96cd6c993b
5 changed files with 728 additions and 942 deletions

View file

@ -228,7 +228,7 @@ public:
virtual unsigned int in_use_timeout() { return _in_use_timeout; } virtual unsigned int in_use_timeout() { return _in_use_timeout; }
/// Keep track of the timeout so it can be updated with more incoming events /// Keep track of the timeout so it can be updated with more incoming events
PBD::ScopedConnection in_use_connection; sigc::connection in_use_connection;
private: private:
int _id; int _id;

File diff suppressed because it is too large Load diff

View file

@ -1,20 +1,21 @@
/* /*
Copyright (C) 2006,2007 John Anderson Copyright (C) 2006,2007 John Anderson
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of 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. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#ifndef ardour_mackie_control_protocol_h #ifndef ardour_mackie_control_protocol_h
#define ardour_mackie_control_protocol_h #define ardour_mackie_control_protocol_h
@ -26,6 +27,7 @@
#include <glibmm/thread.h> #include <glibmm/thread.h>
#include "ardour/types.h" #include "ardour/types.h"
#include "ardour/midi_ui.h"
#include "midi++/types.h" #include "midi++/types.h"
#include "control_protocol/control_protocol.h" #include "control_protocol/control_protocol.h"
@ -40,7 +42,6 @@
namespace MIDI { namespace MIDI {
class Port; class Port;
class Parser;
} }
namespace Mackie { namespace Mackie {
@ -66,12 +67,13 @@ namespace Mackie {
up the relevant Strip in Surface. Then the state is retrieved from up the relevant Strip in Surface. Then the state is retrieved from
the Route and encoded as the correct midi message. the Route and encoded as the correct midi message.
*/ */
class MackieControlProtocol
: public ARDOUR::ControlProtocol class MackieControlProtocol
, public Mackie::MackieButtonHandler : public ARDOUR::ControlProtocol
, public Mackie::MackieButtonHandler
{ {
public: public:
MackieControlProtocol( ARDOUR::Session & ); MackieControlProtocol(ARDOUR::Session &);
virtual ~MackieControlProtocol(); virtual ~MackieControlProtocol();
int set_active (bool yn); int set_active (bool yn);
@ -83,130 +85,131 @@ class MackieControlProtocol
Mackie::Surface & surface(); Mackie::Surface & surface();
// control events // control events
void handle_control_event( Mackie::SurfacePort & port, Mackie::Control & control, const Mackie::ControlState & state ); void handle_control_event(Mackie::SurfacePort & port, Mackie::Control & control, const Mackie::ControlState & state);
// strip/route related stuff // strip/route related stuff
public: public:
/// Signal handler for Route::solo /// Signal handler for Route::solo
void notify_solo_changed( Mackie::RouteSignal * ); void notify_solo_changed(Mackie::RouteSignal *);
/// Signal handler for Route::mute /// Signal handler for Route::mute
void notify_mute_changed( Mackie::RouteSignal * ); void notify_mute_changed(Mackie::RouteSignal *);
/// Signal handler for Route::record_enable_changed /// Signal handler for Route::record_enable_changed
void notify_record_enable_changed( Mackie::RouteSignal * ); void notify_record_enable_changed(Mackie::RouteSignal *);
/// Signal handler for Route::gain_changed ( from IO ) /// Signal handler for Route::gain_changed (from IO)
void notify_gain_changed( Mackie::RouteSignal *, bool force_update = true ); void notify_gain_changed(Mackie::RouteSignal *, bool force_update = true);
/// Signal handler for Route::name_change /// Signal handler for Route::name_change
void notify_name_changed( Mackie::RouteSignal * ); void notify_name_changed(Mackie::RouteSignal *);
/// Signal handler from Panner::Change /// Signal handler from Panner::Change
void notify_panner_changed( Mackie::RouteSignal *, bool force_update = true ); void notify_panner_changed(Mackie::RouteSignal *, bool force_update = true);
/// Signal handler for new routes added /// Signal handler for new routes added
void notify_route_added( ARDOUR::RouteList & ); void notify_route_added(ARDOUR::RouteList &);
/// Signal handler for Route::active_changed /// Signal handler for Route::active_changed
void notify_active_changed( Mackie::RouteSignal * ); void notify_active_changed(Mackie::RouteSignal *);
void notify_remote_id_changed(); void notify_remote_id_changed();
/// rebuild the current bank. Called on route added/removed and /// rebuild the current bank. Called on route added/removed and
/// remote id changed. /// remote id changed.
void refresh_current_bank(); void refresh_current_bank();
// global buttons (ie button not part of strips) // global buttons (ie button not part of strips)
public: public:
// button-related signals // button-related signals
void notify_record_state_changed(); void notify_record_state_changed();
void notify_transport_state_changed(); void notify_transport_state_changed();
// mainly to pick up punch-in and punch-out // mainly to pick up punch-in and punch-out
void notify_parameter_changed( std::string const & ); void notify_parameter_changed(std::string const &);
void notify_solo_active_changed( bool ); void notify_solo_active_changed(bool);
/// Turn timecode on and beats off, or vice versa, depending /// Turn timecode on and beats off, or vice versa, depending
/// on state of _timecode_type /// on state of _timecode_type
void update_timecode_beats_led(); void update_timecode_beats_led();
/// this is called to generate the midi to send in response to a button press. /// this is called to generate the midi to send in response to a button press.
void update_led( Mackie::Button & button, Mackie::LedState ); void update_led(Mackie::Button & button, Mackie::LedState);
void update_global_button( const std::string & name, Mackie::LedState ); void update_global_button(const std::string & name, Mackie::LedState);
void update_global_led( const std::string & name, Mackie::LedState ); void update_global_led(const std::string & name, Mackie::LedState);
// transport button handler methods from MackieButtonHandler // transport button handler methods from MackieButtonHandler
virtual Mackie::LedState frm_left_press( Mackie::Button & ); virtual Mackie::LedState frm_left_press(Mackie::Button &);
virtual Mackie::LedState frm_left_release( Mackie::Button & ); virtual Mackie::LedState frm_left_release(Mackie::Button &);
virtual Mackie::LedState frm_right_press( Mackie::Button & ); virtual Mackie::LedState frm_right_press(Mackie::Button &);
virtual Mackie::LedState frm_right_release( Mackie::Button & ); virtual Mackie::LedState frm_right_release(Mackie::Button &);
virtual Mackie::LedState stop_press( Mackie::Button & ); virtual Mackie::LedState stop_press(Mackie::Button &);
virtual Mackie::LedState stop_release( Mackie::Button & ); virtual Mackie::LedState stop_release(Mackie::Button &);
virtual Mackie::LedState play_press( Mackie::Button & ); virtual Mackie::LedState play_press(Mackie::Button &);
virtual Mackie::LedState play_release( Mackie::Button & ); virtual Mackie::LedState play_release(Mackie::Button &);
virtual Mackie::LedState record_press( Mackie::Button & ); virtual Mackie::LedState record_press(Mackie::Button &);
virtual Mackie::LedState record_release( Mackie::Button & ); virtual Mackie::LedState record_release(Mackie::Button &);
virtual Mackie::LedState loop_press( Mackie::Button & ); virtual Mackie::LedState loop_press(Mackie::Button &);
virtual Mackie::LedState loop_release( Mackie::Button & ); virtual Mackie::LedState loop_release(Mackie::Button &);
virtual Mackie::LedState punch_in_press( Mackie::Button & ); virtual Mackie::LedState punch_in_press(Mackie::Button &);
virtual Mackie::LedState punch_in_release( Mackie::Button & ); virtual Mackie::LedState punch_in_release(Mackie::Button &);
virtual Mackie::LedState punch_out_press( Mackie::Button & ); virtual Mackie::LedState punch_out_press(Mackie::Button &);
virtual Mackie::LedState punch_out_release( Mackie::Button & ); virtual Mackie::LedState punch_out_release(Mackie::Button &);
virtual Mackie::LedState home_press( Mackie::Button & ); virtual Mackie::LedState home_press(Mackie::Button &);
virtual Mackie::LedState home_release( Mackie::Button & ); virtual Mackie::LedState home_release(Mackie::Button &);
virtual Mackie::LedState end_press( Mackie::Button & ); virtual Mackie::LedState end_press(Mackie::Button &);
virtual Mackie::LedState end_release( Mackie::Button & ); virtual Mackie::LedState end_release(Mackie::Button &);
virtual Mackie::LedState rewind_press( Mackie::Button & button ); virtual Mackie::LedState rewind_press(Mackie::Button & button);
virtual Mackie::LedState rewind_release( Mackie::Button & button ); virtual Mackie::LedState rewind_release(Mackie::Button & button);
virtual Mackie::LedState ffwd_press( Mackie::Button & button ); virtual Mackie::LedState ffwd_press(Mackie::Button & button);
virtual Mackie::LedState ffwd_release( Mackie::Button & button ); virtual Mackie::LedState ffwd_release(Mackie::Button & button);
// bank switching button handler methods from MackieButtonHandler // bank switching button handler methods from MackieButtonHandler
virtual Mackie::LedState left_press( Mackie::Button & ); virtual Mackie::LedState left_press(Mackie::Button &);
virtual Mackie::LedState left_release( Mackie::Button & ); virtual Mackie::LedState left_release(Mackie::Button &);
virtual Mackie::LedState right_press( Mackie::Button & ); virtual Mackie::LedState right_press(Mackie::Button &);
virtual Mackie::LedState right_release( Mackie::Button & ); virtual Mackie::LedState right_release(Mackie::Button &);
virtual Mackie::LedState channel_left_press( Mackie::Button & ); virtual Mackie::LedState channel_left_press(Mackie::Button &);
virtual Mackie::LedState channel_left_release( Mackie::Button & ); virtual Mackie::LedState channel_left_release(Mackie::Button &);
virtual Mackie::LedState channel_right_press( Mackie::Button & ); virtual Mackie::LedState channel_right_press(Mackie::Button &);
virtual Mackie::LedState channel_right_release( Mackie::Button & ); virtual Mackie::LedState channel_right_release(Mackie::Button &);
virtual Mackie::LedState clicking_press( Mackie::Button & ); virtual Mackie::LedState clicking_press(Mackie::Button &);
virtual Mackie::LedState clicking_release( Mackie::Button & ); virtual Mackie::LedState clicking_release(Mackie::Button &);
virtual Mackie::LedState global_solo_press( Mackie::Button & ); virtual Mackie::LedState global_solo_press(Mackie::Button &);
virtual Mackie::LedState global_solo_release( Mackie::Button & ); virtual Mackie::LedState global_solo_release(Mackie::Button &);
// function buttons // function buttons
virtual Mackie::LedState marker_press( Mackie::Button & ); virtual Mackie::LedState marker_press(Mackie::Button &);
virtual Mackie::LedState marker_release( Mackie::Button & ); virtual Mackie::LedState marker_release(Mackie::Button &);
virtual Mackie::LedState drop_press( Mackie::Button & ); virtual Mackie::LedState drop_press(Mackie::Button &);
virtual Mackie::LedState drop_release( Mackie::Button & ); virtual Mackie::LedState drop_release(Mackie::Button &);
virtual Mackie::LedState save_press( Mackie::Button & ); virtual Mackie::LedState save_press(Mackie::Button &);
virtual Mackie::LedState save_release( Mackie::Button & ); virtual Mackie::LedState save_release(Mackie::Button &);
virtual Mackie::LedState timecode_beats_press( Mackie::Button & ); virtual Mackie::LedState timecode_beats_press(Mackie::Button &);
virtual Mackie::LedState timecode_beats_release( Mackie::Button & ); virtual Mackie::LedState timecode_beats_release(Mackie::Button &);
// jog wheel states // jog wheel states
virtual Mackie::LedState zoom_press( Mackie::Button & ); virtual Mackie::LedState zoom_press(Mackie::Button &);
virtual Mackie::LedState zoom_release( Mackie::Button & ); virtual Mackie::LedState zoom_release(Mackie::Button &);
virtual Mackie::LedState scrub_press( Mackie::Button & ); virtual Mackie::LedState scrub_press(Mackie::Button &);
virtual Mackie::LedState scrub_release( Mackie::Button & ); virtual Mackie::LedState scrub_release(Mackie::Button &);
/// This is the main MCU port, ie not an extender port /// This is the main MCU port, ie not an extender port
/// Only for use by JogWheel /// Only for use by JogWheel
const Mackie::SurfacePort & mcu_port() const; const Mackie::SurfacePort & mcu_port() const;
Mackie::SurfacePort & mcu_port(); Mackie::SurfacePort & mcu_port();
@ -225,98 +228,83 @@ class MackieControlProtocol
void initialize_surface(); void initialize_surface();
// This sets up the notifications and sets the // This sets up the notifications and sets the
// controls to the correct values // controls to the correct values
void update_surface(); void update_surface();
// connects global (not strip) signals from the Session to here // connects global (not strip) signals from the Session to here
// so the surface can be notified of changes from the other UIs. // so the surface can be notified of changes from the other UIs.
void connect_session_signals(); void connect_session_signals();
// set all controls to their zero position // set all controls to their zero position
void zero_all(); void zero_all();
/** /**
Fetch the set of routes to be considered for control by the Fetch the set of routes to be considered for control by the
surface. Excluding master, hidden and control routes, and inactive routes surface. Excluding master, hidden and control routes, and inactive routes
*/ */
typedef std::vector<boost::shared_ptr<ARDOUR::Route> > Sorted; typedef std::vector<boost::shared_ptr<ARDOUR::Route> > Sorted;
Sorted get_sorted_routes(); Sorted get_sorted_routes();
// bank switching // bank switching
void switch_banks( int initial ); void switch_banks(int initial);
void prev_track(); void prev_track();
void next_track(); void next_track();
// delete all RouteSignal objects connecting Routes to Strips // delete all RouteSignal objects connecting Routes to Strips
void clear_route_signals(); void clear_route_signals();
typedef std::vector<Mackie::RouteSignal*> RouteSignals; typedef std::vector<Mackie::RouteSignal*> RouteSignals;
RouteSignals route_signals; RouteSignals route_signals;
// return which of the ports a particular route_table // return which of the ports a particular route_table
// index belongs to // index belongs to
Mackie::MackiePort & port_for_id( uint32_t index ); Mackie::MackiePort & port_for_id(uint32_t index);
/** /**
Handle a button press for the control and return whether Handle a button press for the control and return whether
the corresponding light should be on or off. the corresponding light should be on or off.
*/ */
bool handle_strip_button( Mackie::Control &, Mackie::ButtonState, boost::shared_ptr<ARDOUR::Route> ); bool handle_strip_button(Mackie::Control &, Mackie::ButtonState, boost::shared_ptr<ARDOUR::Route>);
/// thread started. Calls monitor_work. void add_port(MIDI::Port &, int number);
static void* _monitor_work (void* arg);
/// Polling midi port(s) for incoming messages
void* monitor_work ();
/// rebuild the set of ports for this surface
void update_ports();
/// Returns true if there is pending data, false otherwise
bool poll_ports();
/// Trigger the MIDI::Parser
void read_ports();
void add_port( MIDI::Port &, int number );
/** /**
Read session data and send to surface. Includes Read session data and send to surface. Includes
automation from the currently active routes and automation from the currently active routes and
timecode displays. timecode displays.
*/ */
void poll_session_data(); void poll_session_data();
// called from poll_automation to figure out which automations need to be sent // called from poll_automation to figure out which automations need to be sent
void update_automation( Mackie::RouteSignal & ); void update_automation(Mackie::RouteSignal &);
// also called from poll_automation to update timecode display // also called from poll_automation to update timecode display
void update_timecode_display(); void update_timecode_display();
std::string format_bbt_timecode (ARDOUR::nframes_t now_frame ); std::string format_bbt_timecode (ARDOUR::nframes_t now_frame);
std::string format_timecode_timecode (ARDOUR::nframes_t now_frame ); std::string format_timecode_timecode (ARDOUR::nframes_t now_frame);
/** /**
notification that the port is about to start it's init sequence. notification that the port is about to start it's init sequence.
We must make sure that before this exits, the port is being polled We must make sure that before this exits, the port is being polled
for new data. for new data.
*/ */
void handle_port_init( Mackie::SurfacePort * ); void handle_port_init(Mackie::SurfacePort *);
/// notification from a MackiePort that it's now active /// notification from a MackiePort that it's now active
void handle_port_active( Mackie::SurfacePort * ); void handle_port_active(Mackie::SurfacePort *);
/// notification from a MackiePort that it's now inactive /// notification from a MackiePort that it's now inactive
void handle_port_inactive( Mackie::SurfacePort * ); void handle_port_inactive(Mackie::SurfacePort *);
boost::shared_ptr<ARDOUR::Route> master_route(); boost::shared_ptr<ARDOUR::Route> master_route();
Mackie::Strip & master_strip(); Mackie::Strip & master_strip();
private: private:
boost::shared_ptr<Mackie::RouteSignal> master_route_signal; boost::shared_ptr<Mackie::RouteSignal> master_route_signal;
static const char * default_port_name; static const char * default_port_name;
/// The Midi port(s) connected to the units /// The Midi port(s) connected to the units
typedef std::vector<Mackie::MackiePort*> MackiePorts; typedef std::vector<Mackie::MackiePort*> MackiePorts;
MackiePorts _ports; MackiePorts _ports;
@ -324,18 +312,12 @@ class MackieControlProtocol
/// Sometimes the real port goes away, and we want to contain the breakage /// Sometimes the real port goes away, and we want to contain the breakage
Mackie::DummyPort _dummy_port; Mackie::DummyPort _dummy_port;
// the thread that polls the ports for incoming midi data
pthread_t thread;
/// The initial remote_id of the currently switched in bank. /// The initial remote_id of the currently switched in bank.
uint32_t _current_initial_bank; uint32_t _current_initial_bank;
/// protects the port list, and polling structures /// protects the port list
Glib::Mutex update_mutex; Glib::Mutex update_mutex;
/// Protects set_active, and allows waiting on the poll thread
Glib::Cond update_cond;
PBD::ScopedConnectionList session_connections; PBD::ScopedConnectionList session_connections;
PBD::ScopedConnectionList port_connections; PBD::ScopedConnectionList port_connections;
PBD::ScopedConnectionList route_connections; PBD::ScopedConnectionList route_connections;
@ -343,14 +325,6 @@ class MackieControlProtocol
/// The representation of the physical controls on the surface. /// The representation of the physical controls on the surface.
Mackie::Surface * _surface; Mackie::Surface * _surface;
/// If a port is opened or closed, this will be
/// true until the port configuration is updated;
bool _ports_changed;
bool _polling;
struct pollfd * pfd;
int nfds;
bool _transport_previously_rolling; bool _transport_previously_rolling;
// timer for two quick marker left presses // timer for two quick marker left presses
@ -358,9 +332,6 @@ class MackieControlProtocol
Mackie::JogWheel _jog_wheel; Mackie::JogWheel _jog_wheel;
// Timer for controlling midi bandwidth used by automation polls
Mackie::Timer _automation_last;
// last written timecode string // last written timecode string
std::string _timecode_last; std::string _timecode_last;

View file

@ -29,151 +29,14 @@ const char * MackieControlProtocol::default_port_name = "mcu";
bool MackieControlProtocol::probe() bool MackieControlProtocol::probe()
{ {
if ( MIDI::Manager::instance()->port( default_port_name ) == 0 ) if ( MIDI::Manager::instance()->port(default_port_name) == 0 ) {
{
info << "Mackie: No MIDI port called " << default_port_name << endmsg; info << "Mackie: No MIDI port called " << default_port_name << endmsg;
return false; return false;
} } else {
else
{
return true; return true;
} }
} }
void * MackieControlProtocol::monitor_work()
{
register_thread (X_("MCU"));
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
// read from midi ports
while ( _polling )
{
try
{
if ( poll_ports() )
{
try { read_ports(); }
catch ( exception & e ) {
cout << "MackieControlProtocol::poll_ports caught exception: " << e.what() << endl;
_ports_changed = true;
update_ports();
}
}
// poll for session data that needs to go to the unit
poll_session_data();
}
catch ( exception & e )
{
cout << "caught exception in MackieControlProtocol::monitor_work " << e.what() << endl;
}
}
// TODO ports and pfd and nfds should be in a separate class
delete[] pfd;
pfd = 0;
nfds = 0;
return (void*) 0;
}
void MackieControlProtocol::update_ports()
{
#ifdef DEBUG
cout << "MackieControlProtocol::update_ports" << endl;
#endif
if ( _ports_changed )
{
Glib::Mutex::Lock ul( update_mutex );
// yes, this is a double-test locking paradigm, or whatever it's called
// because we don't *always* need to acquire the lock for the first test
#ifdef DEBUG
cout << "MackieControlProtocol::update_ports lock acquired" << endl;
#endif
if ( _ports_changed )
{
// create new pollfd structures
delete[] pfd;
pfd = new pollfd[_ports.size()];
#ifdef DEBUG
cout << "pfd: " << pfd << endl;
#endif
nfds = 0;
for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
{
// add the port any handler
(*it)->connect_any();
#ifdef DEBUG
cout << "adding pollfd for port " << (*it)->port().name() << " to pollfd " << nfds << endl;
#endif
pfd[nfds].fd = (*it)->port().selectable();
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
++nfds;
}
_ports_changed = false;
}
#ifdef DEBUG
cout << "MackieControlProtocol::update_ports signal" << endl;
#endif
update_cond.signal();
}
#ifdef DEBUG
cout << "MackieControlProtocol::update_ports finish" << endl;
#endif
}
void MackieControlProtocol::read_ports()
{
/* now read any data on the ports */
Glib::Mutex::Lock lock( update_mutex );
for ( int p = 0; p < nfds; ++p )
{
// this will cause handle_midi_any in the MackiePort to be triggered
// for alsa/raw ports
// alsa/sequencer ports trigger the midi parser off poll
if ( (pfd[p].revents & POLLIN) > 0 )
{
// avoid deadlocking?
// doesn't seem to make a difference
//lock.release();
_ports[p]->read();
//lock.acquire();
}
}
}
bool MackieControlProtocol::poll_ports()
{
int timeout = 10; // milliseconds
int no_ports_sleep = 1000; // milliseconds
Glib::Mutex::Lock lock( update_mutex );
// if there are no ports
if ( nfds < 1 )
{
lock.release();
#ifdef DEBUG
cout << "poll_ports no ports" << endl;
#endif
usleep( no_ports_sleep * 1000 );
return false;
}
int retval = ::poll( pfd, nfds, timeout );
if ( retval < 0 )
{
// gdb at work, perhaps
if ( errno != EINTR )
{
error << string_compose(_("Mackie MIDI thread poll failed (%1)"), strerror( errno ) ) << endmsg;
}
return false;
}
return retval > 0;
}
void MackieControlProtocol::handle_port_inactive( SurfacePort * port ) void MackieControlProtocol::handle_port_inactive( SurfacePort * port )
{ {
// port gone away. So stop polling it ASAP // port gone away. So stop polling it ASAP
@ -187,9 +50,7 @@ void MackieControlProtocol::handle_port_inactive( SurfacePort * port )
_ports.erase( it ); _ports.erase( it );
} }
} }
_ports_changed = true;
update_ports();
// TODO all the rebuilding of surfaces and so on // TODO all the rebuilding of surfaces and so on
} }
@ -219,8 +80,6 @@ void MackieControlProtocol::handle_port_init (Mackie::SurfacePort *)
#ifdef DEBUG #ifdef DEBUG
cout << "MackieControlProtocol::handle_port_init" << endl; cout << "MackieControlProtocol::handle_port_init" << endl;
#endif #endif
_ports_changed = true;
update_ports();
#ifdef DEBUG #ifdef DEBUG
cout << "MackieControlProtocol::handle_port_init finish" << endl; cout << "MackieControlProtocol::handle_port_init finish" << endl;
#endif #endif

View file

@ -29,6 +29,8 @@
#include "midi++/types.h" #include "midi++/types.h"
#include "midi++/port.h" #include "midi++/port.h"
#include "ardour/debug.h"
#include "ardour/rc_configuration.h" #include "ardour/rc_configuration.h"
#include "i18n.h" #include "i18n.h"
@ -37,6 +39,7 @@
using namespace std; using namespace std;
using namespace Mackie; using namespace Mackie;
using namespace ARDOUR;
// The MCU sysex header // The MCU sysex header
MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 ); MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 );
@ -45,26 +48,20 @@ MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 );
MidiByteArray mackie_sysex_hdr_xt ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11 ); MidiByteArray mackie_sysex_hdr_xt ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11 );
MackiePort::MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int number, port_type_t port_type ) MackiePort::MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int number, port_type_t port_type )
: SurfacePort( port, number ) : SurfacePort( port, number )
, _mcp( mcp ) , _mcp( mcp )
, _port_type( port_type ) , _port_type( port_type )
, _emulation( none ) , _emulation( none )
, _initialising( true ) , _initialising( true )
{ {
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::MackiePort\n");
cout << "MackiePort::MackiePort" <<endl;
#endif
} }
MackiePort::~MackiePort() MackiePort::~MackiePort()
{ {
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::~MackiePort\n");
cout << "~MackiePort" << endl;
#endif
close(); close();
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "~MackiePort finished\n");
cout << "~MackiePort finished" << endl;
#endif
} }
int MackiePort::strips() const int MackiePort::strips() const
@ -91,29 +88,23 @@ int MackiePort::strips() const
// should really be in MackiePort // should really be in MackiePort
void MackiePort::open() void MackiePort::open()
{ {
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::open %1\n", *this));
cout << "MackiePort::open " << *this << endl;
#endif
port().input()->sysex.connect (sysex_connection, boost::bind (&MackiePort::handle_midi_sysex, this, _1, _2, _3));
port().input()->sysex.connect (sysex_connection, boost::bind (&MackiePort::handle_midi_sysex, this, _1, _2, _3));
// make sure the device is connected // make sure the device is connected
init(); init();
} }
void MackiePort::close() void MackiePort::close()
{ {
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::close\n");
cout << "MackiePort::close" << endl;
#endif
// disconnect signals // disconnect signals
any_connection.disconnect(); any_connection.disconnect();
sysex_connection.disconnect(); sysex_connection.disconnect();
// TODO emit a "closing" signal? // TODO emit a "closing" signal?
#ifdef PORT_DEBUG
cout << "MackiePort::close finished" << endl;
#endif
} }
const MidiByteArray & MackiePort::sysex_hdr() const const MidiByteArray & MackiePort::sysex_hdr() const
@ -149,9 +140,7 @@ MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiB
MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes ) MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
{ {
// handle host connection query // handle host connection query
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
cout << "host connection query: " << bytes << endl;
#endif
if ( bytes.size() != 18 ) if ( bytes.size() != 18 )
{ {
@ -172,9 +161,7 @@ MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
// not used right now // not used right now
MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes ) MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes )
{ {
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
cout << "host_connection_confirmation: " << bytes << endl;
#endif
// decode host connection confirmation // decode host connection confirmation
if ( bytes.size() != 14 ) if ( bytes.size() != 14 )
@ -213,15 +200,13 @@ void MackiePort::probe_emulation (const MidiByteArray &)
void MackiePort::init() void MackiePort::init()
{ {
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::init\n");
cout << "MackiePort::init" << endl;
#endif
init_mutex.lock(); init_mutex.lock();
_initialising = true; _initialising = true;
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::init lock acquired\n");
cout << "MackiePort::init lock acquired" << endl;
#endif
// emit pre-init signal // emit pre-init signal
init_event(); init_event();
@ -237,9 +222,8 @@ void MackiePort::init()
void MackiePort::finalise_init( bool yn ) void MackiePort::finalise_init( bool yn )
{ {
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init\n");
cout << "MackiePort::finalise_init" << endl;
#endif
bool emulation_ok = false; bool emulation_ok = false;
// probing doesn't work very well, so just use a config variable // probing doesn't work very well, so just use a config variable
@ -271,19 +255,18 @@ void MackiePort::finalise_init( bool yn )
SurfacePort::active( yn ); SurfacePort::active( yn );
if ( yn ) if (yn) {
{
active_event(); active_event();
// start handling messages from controls // start handling messages from controls
connect_any(); connect_any();
} }
_initialising = false; _initialising = false;
init_cond.signal(); init_cond.signal();
init_mutex.unlock(); init_mutex.unlock();
#ifdef PORT_DEBUG
cout << "MackiePort::finalise_init lock released" << endl; DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init lock released\n");
#endif
} }
void MackiePort::connect_any() void MackiePort::connect_any()
@ -296,44 +279,36 @@ void MackiePort::connect_any()
bool MackiePort::wait_for_init() bool MackiePort::wait_for_init()
{ {
Glib::Mutex::Lock lock( init_mutex ); Glib::Mutex::Lock lock( init_mutex );
while ( _initialising ) while (_initialising) {
{ DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active waiting\n");
#ifdef PORT_DEBUG
cout << "MackiePort::wait_for_active waiting" << endl;
#endif
init_cond.wait( init_mutex ); init_cond.wait( init_mutex );
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active released\n");
cout << "MackiePort::wait_for_active released" << endl;
#endif
} }
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active returning\n");
cout << "MackiePort::wait_for_active returning" << endl;
#endif
return SurfacePort::active(); return SurfacePort::active();
} }
void MackiePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count ) void MackiePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count )
{ {
MidiByteArray bytes( count, raw_bytes ); MidiByteArray bytes( count, raw_bytes );
#ifdef PORT_DEBUG DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
cout << "handle_midi_sysex: " << bytes << endl;
#endif
switch( bytes[5] ) switch( bytes[5] )
{ {
case 0x01: case 0x01:
// not used right now // not used right now
write_sysex( host_connection_query( bytes ) ); write_sysex (host_connection_query (bytes));
break; break;
case 0x03: case 0x03:
// not used right now // not used right now
write_sysex( host_connection_confirmation( bytes ) ); write_sysex (host_connection_confirmation (bytes));
break; break;
case 0x04: case 0x04:
inactive_event(); inactive_event ();
cout << "host connection error" << bytes << endl; cout << "host connection error" << bytes << endl;
break; break;
case 0x14: case 0x14:
probe_emulation( bytes ); probe_emulation (bytes);
break; break;
default: default:
cout << "unknown sysex: " << bytes << endl; cout << "unknown sysex: " << bytes << endl;
@ -345,10 +320,11 @@ Control & MackiePort::lookup_control( MIDI::byte * bytes, size_t count )
// Don't instantiate a MidiByteArray here unless it's needed for exceptions. // Don't instantiate a MidiByteArray here unless it's needed for exceptions.
// Reason being that this method is called for every single incoming // Reason being that this method is called for every single incoming
// midi event, and it needs to be as efficient as possible. // midi event, and it needs to be as efficient as possible.
Control * control = 0; Control * control = 0;
MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000 MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000
switch( midi_type )
{ switch (midi_type) {
// fader // fader
case MackieMidiBuilder::midi_fader_id: case MackieMidiBuilder::midi_fader_id:
{ {
@ -413,18 +389,16 @@ bool MackiePort::handle_control_timeout_event ( Control * control )
// because they have similar logic flows. // because they have similar logic flows.
void MackiePort::handle_midi_any (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count ) void MackiePort::handle_midi_any (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count )
{ {
#ifdef DEBUG
MidiByteArray bytes( count, raw_bytes ); MidiByteArray bytes( count, raw_bytes );
cout << "MackiePort::handle_midi_any " << bytes << endl; DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::handle_midi_any %1\n", bytes));
#endif
try try
{ {
// ignore sysex messages // ignore sysex messages
if ( raw_bytes[0] == MIDI::sysex ) return; if ( raw_bytes[0] == MIDI::sysex ) return;
// sanity checking // sanity checking
if ( count != 3 ) if (count != 3) {
{
ostringstream os; ostringstream os;
MidiByteArray mba( count, raw_bytes ); MidiByteArray mba( count, raw_bytes );
os << "MackiePort::handle_midi_any needs 3 bytes, but received " << mba; os << "MackiePort::handle_midi_any needs 3 bytes, but received " << mba;
@ -436,8 +410,7 @@ void MackiePort::handle_midi_any (MIDI::Parser &, MIDI::byte * raw_bytes, size_t
// This handles incoming bytes. Outgoing bytes // This handles incoming bytes. Outgoing bytes
// are sent by the signal handlers. // are sent by the signal handlers.
switch ( control.type() ) switch (control.type()) {
{
// fader // fader
case Control::type_fader: case Control::type_fader:
{ {
@ -484,17 +457,15 @@ void MackiePort::handle_midi_any (MIDI::Parser &, MIDI::byte * raw_bytes, size_t
// first disconnect any previous timeouts // first disconnect any previous timeouts
control.in_use_connection.disconnect(); control.in_use_connection.disconnect();
#if 0 // BOOSTSIGNALS
// now connect a new timeout to call handle_control_timeout_event // now connect a new timeout to call handle_control_timeout_event
sigc::slot<bool> timeout_slot = sigc::bind ( // XXX should this use the GUI event loop (default) or the
mem_fun( *this, &MackiePort::handle_control_timeout_event ) // MIDI UI event loop ?
, &control
); sigc::slot<bool> timeout_slot = sigc::bind
control.in_use_connection = Glib::signal_timeout().connect( (sigc::mem_fun( *this, &MackiePort::handle_control_timeout_event), &control);
timeout_slot
, control.in_use_timeout() control.in_use_connection = Glib::signal_timeout().connect (timeout_slot , control.in_use_timeout());
);
#endif
// emit the control event // emit the control event
control_event( *this, control, state ); control_event( *this, control, state );
break; break;
@ -503,12 +474,11 @@ void MackiePort::handle_midi_any (MIDI::Parser &, MIDI::byte * raw_bytes, size_t
cerr << "Do not understand control type " << control; cerr << "Do not understand control type " << control;
} }
} }
catch( MackieControlException & e )
{ catch( MackieControlException & e ) {
MidiByteArray bytes( count, raw_bytes ); MidiByteArray bytes( count, raw_bytes );
cout << bytes << ' ' << e.what() << endl; cout << bytes << ' ' << e.what() << endl;
} }
#ifdef DEBUG
cout << "finished MackiePort::handle_midi_any " << bytes << endl; DEBUG_TRACE (DEBUG::MackieControl, string_compose ("finished MackiePort::handle_midi_any %1\n", bytes));
#endif
} }