a) start at creating ControlProtocol objects
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 4 Apr 2006 03:26:08 +0000 (03:26 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 4 Apr 2006 03:26:08 +0000 (03:26 +0000)
b) basic support for Frontier Design Tranzport
c) probably broke some aspect of existing generic MIDI feedback

git-svn-id: svn://localhost/trunk/ardour2@441 d708f5d6-7413-0410-9779-e7cbd77b26cf

22 files changed:
SConstruct
ardour.rc.in
gtk2_ardour/SConscript
gtk2_ardour/ardour_ui.cc
gtk2_ardour/ardour_ui_ed.cc
gtk2_ardour/audio_time_axis.cc
gtk2_ardour/audio_time_axis.h
gtk2_ardour/editor_ops.cc
libs/ardour/SConscript
libs/ardour/ardour/configuration_vars.h
libs/ardour/ardour/control_protocol.h [new file with mode: 0644]
libs/ardour/ardour/generic_midi_control_protocol.h [new file with mode: 0644]
libs/ardour/ardour/session.h
libs/ardour/ardour/tranzport_control_protocol.h [new file with mode: 0644]
libs/ardour/generic_midi_control_protocol.cc [new file with mode: 0644]
libs/ardour/globals.cc
libs/ardour/session.cc
libs/ardour/session_control.cc [new file with mode: 0644]
libs/ardour/session_feedback.cc
libs/ardour/session_midi.cc
libs/ardour/session_state.cc
libs/ardour/tranzport_control_protocol.cc [new file with mode: 0644]

index aab1b830b5009168ac8131556b4f80808fffe8fa..0806ac4d26243960c4058f244ffb5c2a8b69ed31 100644 (file)
@@ -389,6 +389,8 @@ libraries['libgnomecanvas2'].ParseConfig ('pkg-config --cflags --libs libgnomeca
 libraries['glade2'] = LibraryInfo()
 libraries['glade2'].ParseConfig ('pkg-config --cflags --libs libglade-2.0')
 
+libraries['usb'] = LibraryInfo (LIBS='usb')
+
 #libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas')
 
 libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
index 55948d4f74a7990fdd86259b3152ce919bf9cd15..96e0fe0a5ba4634f03927971e923c16962102387 100644 (file)
@@ -26,6 +26,7 @@
     <Option name="auditioner-right-out" value="%JACK_INPUT%2"/>
     <Option name="quieten-at-speed" value="1.000000"/>
     <Option name="use-vst" value="yes"/>
+    <Option name="use-tranzport" value="yes"/>
   </Config>
   <extra>
     <Keyboard edit-button="3" edit-modifier="4" delete-button="3" delete-modifier="1" snap-modifier="32"/>
index 38cac52b40ccea01370b3d41fd8622398ffad3e5..0ca9def5fe4f42f7340be3fc165c47fff7836ceb 100644 (file)
@@ -24,6 +24,7 @@ gtkardour.Append(POTFILE=domain + '.pot')
 
 gtkardour.Merge ([
     libraries['ardour'],
+    libraries['usb'],
     libraries['gtkmm2ext'],
 #    libraries['flowcanvas'],
     libraries['midi++2'],
index 2b7b87f93b2b6b26020d71312ba4b32051035f59..79c81992dcc6573d4a163f3ca87507519b7f07c0 100644 (file)
@@ -1260,14 +1260,6 @@ ARDOUR_UI::map_transport_state ()
        }
 }
 
-void
-ARDOUR_UI::send_all_midi_feedback ()
-{
-       if (session) {
-               session->send_all_midi_feedback();
-       }
-}
-
 void
 ARDOUR_UI::allow_local_only ()
 {
index 9c1ae4c45fe61215d6cce91370d4bfdd386b5e91..e658aa23e93d76c17390a84c575e488aff19f343 100644 (file)
@@ -286,8 +286,6 @@ ARDOUR_UI::install_actions ()
        act = ActionManager::register_toggle_action (transport_actions, X_("ToggleTimeMaster"), _("Time\nmaster"), mem_fun(*this, &ARDOUR_UI::toggle_time_master));
        ActionManager::session_sensitive_actions.push_back (act);
 
-       act = ActionManager::register_action (common_actions, X_("SendAllMidiFeedback"), _("Send All Midi Feedback"), mem_fun(*this, &ARDOUR_UI::send_all_midi_feedback));
-       ActionManager::session_sensitive_actions.push_back (act);
        act = ActionManager::register_action (common_actions, X_("ToggleRecordEnableTrack1"), _("Toggle Record Enable Track1"), bind (mem_fun(*this, &ARDOUR_UI::toggle_record_enable),  0U));
        ActionManager::session_sensitive_actions.push_back (act);
        act = ActionManager::register_action (common_actions, X_("ToggleRecordEnableTrack2"), _("Toggle Record Enable Track2"), bind (mem_fun(*this, &ARDOUR_UI::toggle_record_enable),  1U));
index b4a5413d04f6153148fbb184875445fb0c7d08dc..6bb6fc45ceb4ac02227d333eb3217d0e2589e1f0 100644 (file)
@@ -141,6 +141,15 @@ AudioTimeAxisView::AudioTimeAxisView (PublicEditor& ed, Session& sess, Route& rt
 
        _route.panner().Changed.connect (mem_fun(*this, &AudioTimeAxisView::update_pans));
 
+       solo_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+       mute_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+       rec_enable_button->signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+       playlist_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+       automation_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+       size_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+       visual_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+       hide_button.signal_button_press_event().connect (mem_fun (*this, &AudioTimeAxisView::select_me), false);
+
        solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
        solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
        mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
@@ -1960,3 +1969,10 @@ AudioTimeAxisView::color_handler (ColorID id, uint32_t val)
                break;
        }
 }
+
+bool
+AudioTimeAxisView::select_me (GdkEventButton* ev)
+{
+       editor.get_selection().add (this);
+       return false;
+}
index 0e2eab66c9a1c8881cc6f84b86a73c0460f477de..d1fbde3e9cf6ffd95d6704958aa3748c58850a41 100644 (file)
@@ -315,6 +315,7 @@ class AudioTimeAxisView : public RouteUI, public TimeAxisView
        void map_frozen ();
 
        void color_handler (ColorID, uint32_t);
+       bool select_me (GdkEventButton*);
 };
 
 #endif /* __ardour_trackview_h__ */
index f78ecbbcbeb32452bf5b2c8028de70bb1aca9e60..03ea5ac4568fa478d2652aabb35f78f71ffd4c02 100644 (file)
@@ -1394,10 +1394,6 @@ Editor::set_selection_from_loop()
 void
 Editor::set_selection_from_range (Location& loc)
 {
-        if (clicked_trackview == 0) {
-               return;
-       }
-       
        begin_reversible_command (_("set selection from range"));
        selection->set (0, loc.start(), loc.end());
        commit_reversible_command ();
@@ -1408,13 +1404,8 @@ Editor::set_selection_from_range (Location& loc)
 void
 Editor::select_all_selectables_using_time_selection ()
 {
-
        list<Selectable *> touched;
 
-       if (clicked_trackview == 0) {
-               return;
-       }
-
        if (selection->time.empty()) {
                return;
        }
@@ -1432,10 +1423,10 @@ Editor::select_all_selectables_using_time_selection ()
                }
                (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
        }
+
        begin_reversible_command (_("select all from range"));
        selection->set (touched);
        commit_reversible_command ();
-
 }
 
 
@@ -1491,18 +1482,19 @@ Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
        list<Selectable *> touched;
 
        if (after) {
-         begin_reversible_command (_("select all after cursor"));
-         start = cursor->current_frame ;
-         end = session->current_end_frame();
+               begin_reversible_command (_("select all after cursor"));
+               start = cursor->current_frame ;
+               end = session->current_end_frame();
        } else {
-         if (cursor->current_frame > 0) {
-           begin_reversible_command (_("select all before cursor"));
-           start = 0;
-           end = cursor->current_frame - 1;
-         } else {
-           return;
-         }
+               if (cursor->current_frame > 0) {
+                       begin_reversible_command (_("select all before cursor"));
+                       start = 0;
+                       end = cursor->current_frame - 1;
+               } else {
+                       return;
+               }
        }
+
        for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
                if ((*iter)->hidden()) {
                        continue;
@@ -1520,19 +1512,21 @@ Editor::select_all_selectables_between_cursors (Cursor *cursor, Cursor *other_cu
        jack_nframes_t end;
        list<Selectable *> touched;
        bool  other_cursor_is_first = cursor->current_frame > other_cursor->current_frame;
+
        if (cursor->current_frame == other_cursor->current_frame) {
-         return;
+               return;
        }
-       begin_reversible_command (_("select all between cursors"));
-       if ( other_cursor_is_first) {
-           start = other_cursor->current_frame;
-           end = cursor->current_frame - 1;
 
+       begin_reversible_command (_("select all between cursors"));
+       if (other_cursor_is_first) {
+               start = other_cursor->current_frame;
+               end = cursor->current_frame - 1;
+               
        } else {
-           start = cursor->current_frame;
-           end = other_cursor->current_frame - 1;
+               start = cursor->current_frame;
+               end = other_cursor->current_frame - 1;
        }
-
+       
        for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
                if ((*iter)->hidden()) {
                        continue;
index dd9de55929e2b19b3113c9979cad52357ef951e9..8bf62b9c0147b985ee0d697080305baae1f3a766 100644 (file)
@@ -31,6 +31,7 @@ automation.cc
 automation_event.cc
 configuration.cc
 connection.cc
+control_protocol.cc
 crossfade.cc
 curve.cc
 cycle_timer.cc
@@ -41,6 +42,7 @@ externalsource.cc
 filesource.cc
 gain.cc
 gdither.cc
+generic_midi_control_protocol.cc
 globals.cc
 import.cc
 insert.cc
@@ -67,6 +69,7 @@ send.cc
 session.cc
 session_butler.cc
 session_click.cc
+session_control.cc
 session_events.cc
 session_export.cc
 session_feedback.cc
@@ -82,6 +85,7 @@ source.cc
 state_manager.cc
 stateful.cc
 tempo.cc
+tranzport_control_protocol.cc
 utils.cc
 version.cc
 mix.cc
@@ -178,7 +182,7 @@ ardour.Merge ([
             libraries['sigc2'],
             libraries['pbd3'],
             libraries['soundtouch'],
-            libraries['midi++2']
+            libraries['midi++2'],
             ])
 
 
index 7d3a9db8b87e2bd65d1cd8ad6a894bd576a11ed7..d54264c1c01075767765d127c377d746feaf8220 100644 (file)
@@ -33,11 +33,12 @@ CONFIG_VARIABLE(bool, timecode_source_is_synced, "timecode-source-is-synced", tr
 CONFIG_VARIABLE(bool, latched_record_enable, "latched-record-enable", false)
 CONFIG_VARIABLE(bool, use_vst, "use-vst", true)
 CONFIG_VARIABLE(bool, quieten_at_speed, "quieten-at-speed", true)
-CONFIG_VARIABLE(uint32_t, midi_feedback_interval_ms,  "midi-feedback-interval-ms", 100)
+CONFIG_VARIABLE(uint32_t, feedback_interval_ms,  "feedback-interval-ms", 100)
 CONFIG_VARIABLE(uint32_t, disk_choice_space_threshold,  "disk-choice-space-threshold", 57600000)
 CONFIG_VARIABLE(uint32_t, destructive_xfade_msecs,  "destructive-xfade-msecs", 2)
 CONFIG_VARIABLE(SampleFormat, native_file_data_format,  "native-file-data-format", ARDOUR::FormatFloat)
 CONFIG_VARIABLE(HeaderFormat, native_file_header_format,  "native-file-header-format", ARDOUR::WAVE)
+CONFIG_VARIABLE(bool, use_tranzport,  "use-tranzport", false)
 
 /* these variables have custom set() methods */
 
diff --git a/libs/ardour/ardour/control_protocol.h b/libs/ardour/ardour/control_protocol.h
new file mode 100644 (file)
index 0000000..c0869fa
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef ardour_control_protocols_h
+#define ardour_control_protocols_h
+
+#include <string>
+#include <list>
+#include <sigc++/sigc++.h>
+
+namespace ARDOUR {
+
+class Route;
+class Session;
+
+class ControlProtocol : sigc::trackable {
+  public:
+       ControlProtocol (Session&, std::string name);
+       virtual ~ControlProtocol();
+
+       virtual int init () { return 0; }
+       virtual bool active() const = 0;
+
+       enum SendWhat {
+               SendRoute,
+               SendGlobal
+       };
+
+       std::string name() const { return _name; }
+
+       void set_send (SendWhat);
+
+       bool send() const { return _send != 0; }
+       bool send_route_feedback () const { return _send & SendRoute; }
+       bool send_global_feedback () const { return _send & SendGlobal; }
+
+       virtual void send_route_feedback (std::list<Route*>&) {}
+       virtual void send_global_feedback () {}
+
+  protected:
+       ARDOUR::Session& session;
+       SendWhat _send;
+       std::string _name;
+};
+
+}
+
+#endif // ardour_control_protocols_h
diff --git a/libs/ardour/ardour/generic_midi_control_protocol.h b/libs/ardour/ardour/generic_midi_control_protocol.h
new file mode 100644 (file)
index 0000000..75b514f
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef ardour_generic_midi_control_protocol_h
+#define ardour_generic_midi_control_protocol_h
+
+#include <ardour/control_protocol.h>
+
+namespace MIDI {
+       class Port;
+}
+
+namespace ARDOUR {
+
+class GenericMidiControlProtocol : public ControlProtocol {
+  public:
+       GenericMidiControlProtocol (Session&);
+       virtual ~GenericMidiControlProtocol();
+
+       bool active() const;
+
+       void set_port (MIDI::Port*);
+       MIDI::Port* port () const { return _port; }
+
+       void send_route_feedback (std::list<Route*>&);
+       
+  private:
+       void route_feedback (ARDOUR::Route&, bool);
+       MIDI::Port* _port;
+};
+
+}
+
+#endif // ardour_generic_midi_control_protocol_h
index 1e25c5f38bdf66d0f3c1cfc4de7892eb6bd68670..1f6f464d24516ea73efe37ccbb5805916bbb2765 100644 (file)
 #define __ardour_session_h__
 
 #include <string>
-#if __GNUC__ >= 3
-#include <ext/slist>
-using __gnu_cxx::slist;
-#else
-#include <slist.h>
-#endif
+#include <list>
 #include <map>
 #include <vector>
 #include <set>
@@ -84,6 +79,8 @@ class AudioRegion;
 class Region;
 class Playlist;
 class VSTPlugin;
+class ControlProtocol;
+class GenericMidiControlProtocol;
 
 struct AudioExportSpecification;
 struct RouteGroup;
@@ -283,7 +280,7 @@ class Session : public sigc::trackable, public Stateful
        void foreach_diskstream (void (DiskStream::*func)(void));
        template<class T> void foreach_diskstream (T *obj, void (T::*func)(DiskStream&));
 
-       typedef slist<Route *> RouteList;
+       typedef list<Route *> RouteList;
 
        RouteList get_routes() const {
                RWLockMonitor rlock (route_lock, false, __LINE__, __FILE__);
@@ -303,6 +300,7 @@ class Session : public sigc::trackable, public Stateful
        template<class T, class A> void foreach_route (T *obj, void (T::*func)(Route&, A), A arg);
 
        Route *route_by_name (string);
+       Route *route_by_remote_id (uint32_t id);
 
        bool route_name_unique (string) const;
 
@@ -407,7 +405,9 @@ class Session : public sigc::trackable, public Stateful
                CrossfadingModel,
                SeamlessLoop,
                MidiFeedback,
-               MidiControl
+               MidiControl,
+               TranzportControl,
+               Feedback
        };
 
        sigc::signal<void,ControlType> ControlChanged;
@@ -428,6 +428,7 @@ class Session : public sigc::trackable, public Stateful
        void set_do_not_record_plugins (bool yn);
        void set_crossfades_active (bool yn);
        void set_seamless_loop (bool yn);
+       void set_feedback (bool yn);
 
        bool get_auto_play () const { return auto_play; }
        bool get_auto_input () const { return auto_input; }
@@ -444,6 +445,8 @@ class Session : public sigc::trackable, public Stateful
        bool get_midi_control () const;
        bool get_do_not_record_plugins () const { return do_not_record_plugins; }
        bool get_crossfades_active () const { return crossfades_active; }
+       bool get_feedback() const;
+       bool get_tranzport_control() const;
 
        bool get_input_auto_connect () const;
        AutoConnectOption get_output_auto_connect () const { return output_auto_connect; }
@@ -815,7 +818,6 @@ class Session : public sigc::trackable, public Stateful
        bool get_trace_midi_output(MIDI::Port *port = 0);
        
        void send_midi_message (MIDI::Port * port, MIDI::eventType ev, MIDI::channel_t, MIDI::EventTwoBytes);
-       void send_all_midi_feedback ();
 
        void deliver_midi (MIDI::Port*, MIDI::byte*, int32_t size);
 
@@ -1133,9 +1135,9 @@ class Session : public sigc::trackable, public Stateful
        bool send_mtc;
        bool send_mmc;
        bool mmc_control;
-       bool midi_feedback;
        bool midi_control;
-       
+       bool midi_feedback;
+
        RingBuffer<Event*> pending_events;
 
        void hookup_io ();
@@ -1598,9 +1600,9 @@ class Session : public sigc::trackable, public Stateful
 
        /* INSERT AND SEND MANAGEMENT */
        
-       slist<PortInsert *>   _port_inserts;
-       slist<PluginInsert *> _plugin_inserts;
-       slist<Send *>         _sends;
+       list<PortInsert *>   _port_inserts;
+       list<PluginInsert *> _plugin_inserts;
+       list<Send *>         _sends;
        uint32_t          send_cnt;
        uint32_t          insert_cnt;
 
@@ -1768,6 +1770,12 @@ class Session : public sigc::trackable, public Stateful
 
        LayerModel layer_model;
        CrossfadeModel xfade_model;
+
+       /* control protocols */
+
+       vector<ControlProtocol*> control_protocols;
+       GenericMidiControlProtocol* generic_midi_control_protocol;
+       void initialize_control ();
 };
 
 }; /* namespace ARDOUR */
diff --git a/libs/ardour/ardour/tranzport_control_protocol.h b/libs/ardour/ardour/tranzport_control_protocol.h
new file mode 100644 (file)
index 0000000..448d803
--- /dev/null
@@ -0,0 +1,142 @@
+#ifndef ardour_tranzport_control_protocol_h
+#define ardour_tranzport_control_protocol_h
+
+#include <pthread.h>
+#include <usb.h>
+#include <ardour/control_protocol.h>
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+class TranzportControlProtocol : public ControlProtocol {
+  public:
+       TranzportControlProtocol (Session&);
+       virtual ~TranzportControlProtocol();
+
+       int init ();
+       bool active() const;
+
+       void send_route_feedback (std::list<Route*>&);
+       void send_global_feedback ();
+
+  private:
+       static const int VENDORID = 0x165b;
+       static const int PRODUCTID = 0x8101;
+       static const int READ_ENDPOINT  = 0x81;
+       static const int WRITE_ENDPOINT = 0x02;
+       const static int STATUS_OFFLINE  = 0xff;
+       const static int STATUS_ONLINE = 0x01;
+
+       enum LightID {
+               LightRecord = 0,
+               LightTrackrec,
+               LightTrackmute,
+               LightTracksolo,
+               LightAnysolo,
+               LightLoop,
+               LightPunch
+       };
+
+       enum ButtonID {
+               ButtonBattery = 0x00004000,
+               ButtonBacklight = 0x00008000,
+               ButtonTrackLeft = 0x04000000,
+               ButtonTrackRight = 0x40000000,
+               ButtonTrackRec = 0x00040000,
+               ButtonTrackMute = 0x00400000,
+               ButtonTrackSolo = 0x00000400,
+               ButtonUndo = 0x80000000,
+               ButtonIn = 0x02000000,
+               ButtonOut = 0x20000000,
+               ButtonPunch = 0x00800000,
+               ButtonLoop = 0x00080000,
+               ButtonPrev = 0x00020000,
+               ButtonAdd = 0x00200000,
+               ButtonNext = 0x00000200,
+               ButtonRewind = 0x01000000,
+               ButtonFastForward = 0x10000000,
+               ButtonStop = 0x00010000,
+               ButtonPlay = 0x00100000,
+               ButtonRecord = 0x00000100,
+               ButtonShift = 0x08000000
+       };
+               
+       pthread_t       thread;
+       uint32_t        buttonmask;
+       uint32_t        timeout;
+       uint8_t        _datawheel;
+       uint8_t        _device_status;
+       usb_dev_handle* udev;
+       Route*          current_route;
+       uint32_t        current_track_id;
+
+       bool     last_negative;
+       uint32_t last_hrs;
+       uint32_t last_mins;
+       uint32_t last_secs;
+       uint32_t last_frames;
+       jack_nframes_t last_where;
+
+       int open ();
+       int read ();
+       int write (uint8_t* cmd);
+       int close ();
+
+       int open_core (struct usb_device*);
+
+       void lcd_clear ();
+       int  lcd_write (uint8_t cell, const char *text);
+
+       int  light_on (LightID);
+       int  light_off (LightID);
+
+       void show_current_track ();
+
+       static void* _thread_work (void* arg);
+       void* thread_work ();
+
+       void button_event_battery_press (bool shifted);
+       void button_event_battery_release (bool shifted);
+       void button_event_backlight_press (bool shifted);
+       void button_event_backlight_release (bool shifted);
+       void button_event_trackleft_press (bool shifted);
+       void button_event_trackleft_release (bool shifted);
+       void button_event_trackright_press (bool shifted);
+       void button_event_trackright_release (bool shifted);
+       void button_event_trackrec_press (bool shifted);
+       void button_event_trackrec_release (bool shifted);
+       void button_event_trackmute_press (bool shifted);
+       void button_event_trackmute_release (bool shifted);
+       void button_event_tracksolo_press (bool shifted);
+       void button_event_tracksolo_release (bool shifted);
+       void button_event_undo_press (bool shifted);
+       void button_event_undo_release (bool shifted);
+       void button_event_in_press (bool shifted);
+       void button_event_in_release (bool shifted);
+       void button_event_out_press (bool shifted);
+       void button_event_out_release (bool shifted);
+       void button_event_punch_press (bool shifted);
+       void button_event_punch_release (bool shifted);
+       void button_event_loop_press (bool shifted);
+       void button_event_loop_release (bool shifted);
+       void button_event_prev_press (bool shifted);
+       void button_event_prev_release (bool shifted);
+       void button_event_add_press (bool shifted);
+       void button_event_add_release (bool shifted);
+       void button_event_next_press (bool shifted);
+       void button_event_next_release (bool shifted);
+       void button_event_rewind_press (bool shifted);
+       void button_event_rewind_release (bool shifted);
+       void button_event_fastforward_press (bool shifted);
+       void button_event_fastforward_release (bool shifted);
+       void button_event_stop_press (bool shifted);
+       void button_event_stop_release (bool shifted);
+       void button_event_play_press (bool shifted);
+       void button_event_play_release (bool shifted);
+       void button_event_record_press (bool shifted);
+       void button_event_record_release (bool shifted);
+};
+
+} // namespace
+
+#endif // ardour_tranzport_control_protocol_h
diff --git a/libs/ardour/generic_midi_control_protocol.cc b/libs/ardour/generic_midi_control_protocol.cc
new file mode 100644 (file)
index 0000000..f9b9750
--- /dev/null
@@ -0,0 +1,54 @@
+#include <ardour/generic_midi_control_protocol.h>
+#include <ardour/route.h>
+#include <ardour/session.h>
+
+using namespace ARDOUR;
+
+#include "i18n.h"
+
+GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
+       : ControlProtocol  (s, _("GenericMIDI"))
+{
+       _port = 0;
+}
+
+GenericMidiControlProtocol::~GenericMidiControlProtocol ()
+{
+}
+
+void
+GenericMidiControlProtocol::set_port (MIDI::Port* p)
+{
+       _port = p;
+}
+
+void 
+GenericMidiControlProtocol::send_route_feedback (list<Route*>& routes)
+{
+       if (_port != 0) {
+
+               const int32_t bufsize = 16 * 1024;
+               int32_t bsize = bufsize;
+               MIDI::byte* buf = new MIDI::byte[bufsize];
+               MIDI::byte* end = buf;
+               
+               for (list<Route*>::iterator r = routes.begin(); r != routes.end(); ++r) {
+               end = (*r)->write_midi_feedback (end, bsize);
+               }
+               
+               if (end == buf) {
+                       delete [] buf;
+                       return;
+               } 
+               
+               session.deliver_midi (_port, buf, (int32_t) (end - buf));
+               //cerr << "MIDI feedback: wrote " << (int32_t) (end - buf) << " to midi port\n";
+       }
+}
+
+bool
+GenericMidiControlProtocol::active() const
+{
+       return _port && send();
+}
+
index 5cfe83e9448af1bf79476f5a8edfb5b10884b602..7210fcf9f5efd03d956a0b52168bfb2188f9f118 100644 (file)
@@ -336,7 +336,7 @@ find_file (string name, string dir, string subdir = "")
        string path;
        char* envvar = getenv("ARDOUR_PATH");
 
-       /* stop A: any directory in ARDOUR_PATH */
+       /* 1st attempt: any directory in ARDOUR_PATH */
        
        if (envvar != 0) {
 
@@ -354,7 +354,7 @@ find_file (string name, string dir, string subdir = "")
                }
        }
 
-       /* stop B: ~/.ardour/ */
+       /* 2nd attempt: ~/.ardour/ */
 
        path = get_user_ardour_path();
                
@@ -367,7 +367,7 @@ find_file (string name, string dir, string subdir = "")
                return path;
        }
 
-       /* C: dir/... */
+       /* 3rd attempt: dir/... */
        
        path = dir;
        path += "/ardour2/";
index 5e9a196c9a49e117dc8efa2df1594263b5b8018d..22686c6cbe844ca841d2cdee8c3e06032c013e85 100644 (file)
@@ -767,7 +767,7 @@ Session::when_engine_running ()
 
        insert_cnt = 0;
        
-       for (slist<PortInsert*>::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) {
+       for (list<PortInsert*>::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) {
                uint32_t id;
 
                if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) {
@@ -779,7 +779,7 @@ Session::when_engine_running ()
 
        send_cnt = 0;
 
-       for (slist<Send*>::iterator i = _sends.begin(); i != _sends.end(); ++i) {
+       for (list<Send*>::iterator i = _sends.begin(); i != _sends.end(); ++i) {
                uint32_t id;
                
                if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) {
@@ -2211,6 +2211,20 @@ Session::route_by_name (string name)
        return 0;
 }
 
+Route *
+Session::route_by_remote_id (uint32_t id)
+{
+       RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
+
+       for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+               if ((*i)->remote_control_id() == id) {
+                       return* i;
+               }
+       }
+
+       return 0;
+}
+
 void
 Session::find_current_end ()
 {
diff --git a/libs/ardour/session_control.cc b/libs/ardour/session_control.cc
new file mode 100644 (file)
index 0000000..8e0a3e9
--- /dev/null
@@ -0,0 +1,30 @@
+
+#include <ardour/session.h>
+#include <ardour/control_protocol.h>
+#include <ardour/generic_midi_control_protocol.h>
+#include <ardour/tranzport_control_protocol.h>
+
+using namespace ARDOUR;
+
+void
+Session::initialize_control ()
+{
+       GenericMidiControlProtocol* midi_protocol = new GenericMidiControlProtocol (*this);
+
+       if (midi_protocol->init() == 0) {
+               control_protocols.push_back (midi_protocol);
+       }
+
+       if (Config->get_use_tranzport()) {
+               cerr << "Creating new tranzport control" << endl;
+
+               TranzportControlProtocol* tranzport_protocol = new TranzportControlProtocol (*this);
+
+               cerr << "Initializing new tranzport control" << endl;
+
+               if (tranzport_protocol->init() == 0) {
+                       control_protocols.push_back (tranzport_protocol);
+               }
+       }
+}
+
index 840cb6a97dca180dc0f3e1e5983da40df80d0a10..09e9021461c4f97b71e3cf371aa5439bb5f01b68 100644 (file)
@@ -37,6 +37,7 @@
 #include <ardour/session.h>
 #include <ardour/audio_track.h>
 #include <ardour/diskstream.h>
+#include <ardour/control_protocol.h>
 
 #include "i18n.h"
 
@@ -65,11 +66,6 @@ Session::init_feedback ()
        }
 
        active_feedback = 0;
-       midi_feedback = false;
-
-       /* add possible feedback functions here */
-       
-       feedback_functions.push_back (mem_fun (*this, &Session::feedback_generic_midi_function));
 
        if (pthread_create_and_store ("feedback", &feedback_thread, 0, _feedback_thread_work, this)) {
                error << _("Session: could not create feedback thread") << endmsg;
@@ -98,6 +94,28 @@ Session::stop_feedback ()
        return poke_feedback (FeedbackRequest::Stop);
 }
 
+void
+Session::set_feedback (bool yn)
+{
+       set_dirty();
+
+       if (yn) {
+               /* make sure the feedback thread is alive */
+               start_feedback ();
+       } else {
+               /* maybe put the feedback thread to sleep */
+               stop_feedback ();
+       }
+
+       ControlChanged (Feedback); /* EMIT SIGNAL */
+}
+
+bool
+Session::get_feedback() const
+{
+       return active_feedback > 0;
+}
+
 void
 Session::terminate_feedback ()
 {
@@ -123,8 +141,7 @@ Session::feedback_thread_work ()
        pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
 
        if (active_feedback) {
-               /* XXX use Config->feedback_interval_usecs()*/;
-               timeout = max (5, (int) Config->get_midi_feedback_interval_ms());
+               timeout = max (5, (int) Config->get_feedback_interval_ms());
        } else {
                timeout = -1;
        }
@@ -162,7 +179,7 @@ Session::feedback_thread_work ()
                                        switch ((FeedbackRequest::Type) req) {
                                        
                                        case FeedbackRequest::Start:
-                                               timeout = max (5, (int) Config->get_midi_feedback_interval_ms());
+                                               timeout = max (5, (int) Config->get_feedback_interval_ms());
                                                active_feedback++;
                                                break;
                                                
@@ -197,50 +214,29 @@ Session::feedback_thread_work ()
                        continue;
                }
 
-               for (list<FeedbackFunctionPtr>::iterator i = feedback_functions.begin(); i != feedback_functions.end(); ) {
-                       
-                       list<FeedbackFunctionPtr>::iterator tmp;
-                       
-                       tmp = i;
-                       ++tmp;
-                       
-                       if ((*i) ()) {
-                               feedback_functions.erase (i);
+               bool send = false;
+
+               for (vector<ControlProtocol*>::iterator i = control_protocols.begin(); i != control_protocols.end(); ++i) {
+                       if ((*i)->send()) {
+                               send = true;
+                               break;
                        }
-                       
-                       i = tmp;
                }
-       }
-       
-       return 0;
-}
+               
+               if (send) {
 
-int
-Session::feedback_generic_midi_function ()
-{
-       const int32_t bufsize = 16 * 1024;
-       int32_t bsize = bufsize;
-       MIDI::byte* buf = new MIDI::byte[bufsize];
-       MIDI::byte* end = buf;
+                       RouteList routes = get_routes(); /* copies the routes */
 
-       {
-               RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
-               
-               for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
-                       end = (*i)->write_midi_feedback (end, bsize);
+                       for (vector<ControlProtocol*>::iterator i = control_protocols.begin(); i != control_protocols.end(); ++i) {
+                               if ((*i)->send_route_feedback ()) {
+                                       (*i)->send_route_feedback (routes);
+                               }
+                               (*i)->send_global_feedback ();
+                       } 
                }
        }
-
-       if (end == buf) {
-               delete [] buf;
-               return 0;
-       } 
        
-       deliver_midi (_midi_port, buf, (int32_t) (end - buf));
-
-       //cerr << "MIDI feedback: wrote " << (int32_t) (end - buf) << " to midi port\n";
-               
-
        return 0;
 }
+
        
index 1ac7d9e300e2b3c57043c7e2c533781b21ed6207..af08d3cc3e875ac5ca5257cb120c6ab2e3aaad72 100644 (file)
@@ -40,6 +40,7 @@
 #include <ardour/diskstream.h>
 #include <ardour/slave.h>
 #include <ardour/cycles.h>
+#include <ardour/generic_midi_control_protocol.h>
 
 #include "i18n.h"
 
@@ -166,6 +167,24 @@ Session::set_send_mmc (bool yn)
        ControlChanged (SendMMC); /* EMIT SIGNAL */
 }
 
+void
+Session::set_midi_feedback (bool yn)
+{
+       if (generic_midi_control_protocol) {
+               if (yn) {
+                       generic_midi_control_protocol->set_send (ControlProtocol::SendRoute);
+               } else {
+                       generic_midi_control_protocol->set_send (ControlProtocol::SendWhat (0));
+               }
+       }
+}
+
+bool
+Session::get_midi_feedback () const
+{
+       return generic_midi_control_protocol && generic_midi_control_protocol->active();
+}
+
 bool
 Session::get_send_mtc () const
 {
@@ -308,6 +327,10 @@ Session::set_midi_port (string port_tag)
        }
 
        _midi_port = port;
+       
+       if (generic_midi_control_protocol) {
+               generic_midi_control_protocol->set_port (port);
+       }
 
        Config->set_midi_port_name (port_tag);
 
@@ -449,42 +472,6 @@ Session::get_trace_midi_output(MIDI::Port *port)
 
 }
 
-
-void
-Session::set_midi_feedback (bool yn)
-{
-       if (_midi_port == 0) {
-               return;
-       }
-       
-       midi_feedback = yn;
-       set_dirty();
-
-       if (yn) {
-               /* make sure the feedback thread is alive */
-               start_feedback ();
-       } else {
-               /* maybe put the feedback thread to sleep */
-               stop_feedback ();
-       }
-
-       ControlChanged (MidiFeedback); /* EMIT SIGNAL */
-
-       send_all_midi_feedback ();
-}
-
-void
-Session::send_all_midi_feedback ()
-{
-       if (midi_feedback) {
-               // send out current state of all routes
-               RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
-               for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
-                       (*i)->send_all_midi_feedback ();
-               }
-       }
-}
-
 void
 Session::setup_midi_control ()
 {
@@ -1483,14 +1470,7 @@ Session::get_mmc_control () const
 {
        return mmc_control;
 }
-bool
-Session::get_midi_feedback () const
-{
-       /* since this a "write" function we have to check the port as well 
-          as the control toggle.
-       */
-       return _midi_port && midi_feedback;
-}
+
 bool
 Session::get_midi_control () const
 {
index 5301019fa0338674e0410a4f3c853b7ff08a1ad8..5decba1d0928d986226e8d6b4739fdcda71f92fb 100644 (file)
@@ -165,6 +165,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        butler_mixdown_buffer = 0;
        butler_gain_buffer = 0;
        auditioner = 0;
+       generic_midi_control_protocol = 0;
        mmc_control = false;
        midi_feedback = false;
        midi_control = true;
@@ -301,6 +302,8 @@ Session::second_stage_init (bool new_session)
                return -1;
        }
 
+       initialize_control();
+       
        if (init_feedback ()) {
                return -1;
        }
@@ -344,7 +347,8 @@ Session::second_stage_init (bool new_session)
        _engine.transport_locate (0);
        deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
        deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
-       send_all_midi_feedback();
+
+       // XXX need to poke the feedback thread to send full state
 
        if (new_session) {
                _end_location_is_free = true;
diff --git a/libs/ardour/tranzport_control_protocol.cc b/libs/ardour/tranzport_control_protocol.cc
new file mode 100644 (file)
index 0000000..9640ea4
--- /dev/null
@@ -0,0 +1,718 @@
+#include <iostream>
+
+#include <pbd/pthread_utils.h>
+
+#include <ardour/tranzport_control_protocol.h>
+#include <ardour/route.h>
+#include <ardour/session.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+#include "i18n.h"
+
+TranzportControlProtocol::TranzportControlProtocol (Session& s)
+       : ControlProtocol  (s, _("Tranzport"))
+{
+       timeout = 60000;
+       buttonmask = 0;
+       _datawheel = 0;
+       _device_status = STATUS_OFFLINE;
+       udev = 0;
+       current_route = 0;
+       current_track_id = 0;
+       last_where = max_frames;
+}
+
+TranzportControlProtocol::~TranzportControlProtocol ()
+{
+       if (udev) {
+               pthread_cancel_one (thread);
+               close ();
+       }
+}
+
+int
+TranzportControlProtocol::init ()
+{
+       if (open ()) {
+               return -1;
+       }
+
+       pthread_create_and_store (X_("Tranzport"), &thread, 0, _thread_work, this);
+
+       return 0;
+}
+
+bool
+TranzportControlProtocol::active() const
+{
+       return true;
+}
+               
+void
+TranzportControlProtocol::send_route_feedback (list<Route*>& routes)
+{
+}
+
+void
+TranzportControlProtocol::send_global_feedback ()
+{
+       jack_nframes_t where = session.transport_frame();
+
+       if (where != last_where) {
+
+               char clock_label[16];
+               SMPTE_Time smpte;
+               char* ptr = clock_label;
+
+               session.smpte_time (where, smpte);
+               memset (clock_label, ' ', sizeof (clock_label));
+               
+               if (smpte.negative) {
+                       sprintf (ptr, "-%02ld:", smpte.hours);
+               } else {
+                       sprintf (ptr, " %02ld:", smpte.hours);
+               }
+               ptr += 4;
+
+               sprintf (ptr, "%02ld:", smpte.minutes);
+               ptr += 3;
+
+               sprintf (ptr, "%02ld:", smpte.seconds);
+               ptr += 3;
+
+               sprintf (ptr, "%02ld", smpte.frames);
+               ptr += 2;
+               
+               lcd_write (7, &clock_label[0]);
+               lcd_write (8, &clock_label[4]);
+               lcd_write (9, &clock_label[8]);
+
+               last_where = where;
+       }
+}
+
+void*
+TranzportControlProtocol::_thread_work (void* arg)
+{
+       return static_cast<TranzportControlProtocol*>(arg)->thread_work ();
+}
+
+void*
+TranzportControlProtocol::thread_work ()
+{
+       cerr << "tranzport thread here, sending message to LCD\n";
+
+       while (true) {
+               if (read()) {
+                       return 0;
+               }
+               switch (_device_status) {
+               case STATUS_OFFLINE:
+                       cerr << "offline\n";
+                       break;
+               case STATUS_ONLINE:
+                       cerr << "online\n";
+                       break;
+               default:
+                       cerr << "unknown status\n";
+                       break;
+               }
+
+               if (_device_status == STATUS_ONLINE) {
+                       break;
+               }
+       }
+
+       lcd_write (0, "    ");
+       lcd_write (1, "WELC");
+       lcd_write (2, "OME ");
+       lcd_write (3, "TO  ");
+       lcd_write (4, "    ");
+       lcd_write (5, "    ");
+       lcd_write (6, "    ");
+       lcd_write (7, "ARDO");
+       lcd_write (8, "UR  ");
+       lcd_write (9, "    ");
+
+       while (true) {
+               if (read ()) {
+                       cerr << "Tranzport command received\n";
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+int
+TranzportControlProtocol::open ()
+{
+       struct usb_bus *bus;
+       struct usb_device *dev;
+
+       usb_init();
+       usb_find_busses();
+       usb_find_devices();
+
+       cerr << "checking busses\n";
+       
+       for (bus = usb_busses; bus; bus = bus->next) {
+
+               cerr << "checking devices\n";
+
+               for(dev = bus->devices; dev; dev = dev->next) {
+                       cerr << "Checking " << dev->descriptor.idVendor << '/' << dev->descriptor.idProduct << endl;
+                       if (dev->descriptor.idVendor != VENDORID)
+                               continue;
+                       if (dev->descriptor.idProduct != PRODUCTID)
+                               continue;
+                       cerr << "Open this one" << endl;
+                       return open_core (dev);
+               }
+       }
+
+       error << _("Tranzport: no device detected") << endmsg;
+       return -1;
+}
+
+int
+TranzportControlProtocol::open_core (struct usb_device* dev)
+{
+       if (!(udev = usb_open (dev))) {
+               error << _("Tranzport: cannot open USB transport") << endmsg;
+               return -1;
+       }
+        
+       if (usb_claim_interface (udev, 0) < 0) {
+               error << _("Tranzport: cannot claim USB interface") << endmsg;
+               usb_close (udev);
+               udev = 0;
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+TranzportControlProtocol::close ()
+{
+       int ret = 0;
+
+       if (udev == 0) {
+               return 0;
+       }
+
+       if (usb_release_interface (udev, 0) < 0) {
+               error << _("Tranzport: cannot release interface") << endmsg;
+               ret = -1;
+       }
+
+       if (usb_close (udev)) {
+               error << _("Tranzport: cannot close device") << endmsg;
+               ret = 0;
+       }
+
+       return ret;
+}
+       
+int
+TranzportControlProtocol::write (uint8_t* cmd)
+{
+       int val;
+       val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout);
+       if (val < 0)
+               return val;
+       if (val != 8)
+               return -1;
+       return 0;
+
+}      
+
+void
+TranzportControlProtocol::lcd_clear ()
+{
+       lcd_write (0, "    ");
+       lcd_write (1, "    ");
+       lcd_write (2, "    ");
+       lcd_write (3, "    ");
+       lcd_write (4, "    ");
+       lcd_write (5, "    ");
+       lcd_write (6, "    ");
+       lcd_write (7, "    ");
+       lcd_write (8, "    ");
+       lcd_write (9, "    ");
+}
+
+int
+TranzportControlProtocol::lcd_write (uint8_t cell, const char* text)       
+{
+       uint8_t cmd[8];
+       
+       if (cell > 9) {
+               return -1;
+       }
+
+       cmd[0] = 0x00;
+       cmd[1] = 0x01;
+       cmd[2] = cell;
+       cmd[3] = text[0];
+       cmd[4] = text[1];
+       cmd[5] = text[2];
+       cmd[6] = text[3];
+       cmd[7] = 0x00;
+
+       return write (cmd);
+}
+
+int
+TranzportControlProtocol::light_on (LightID light)
+{
+       uint8_t cmd[8];
+
+       cmd[0] = 0x00;
+       cmd[1] = 0x00;
+       cmd[2] = light;
+       cmd[3] = 0x01;
+       cmd[4] = 0x00;
+       cmd[5] = 0x00;
+       cmd[6] = 0x00;
+       cmd[7] = 0x00;
+
+       return write (cmd);
+}
+
+int
+TranzportControlProtocol::light_off (LightID light)
+{
+       uint8_t cmd[8];
+
+       cmd[0] = 0x00;
+       cmd[1] = 0x00;
+       cmd[2] = light;
+       cmd[3] = 0x00;
+       cmd[4] = 0x00;
+       cmd[5] = 0x00;
+       cmd[6] = 0x00;
+       cmd[7] = 0x00;
+
+       return write (cmd);
+}
+
+int
+TranzportControlProtocol::read ()
+{
+       uint8_t buf[8];
+       int val;
+
+       memset(buf, 0, 8);
+       val = usb_interrupt_read(udev, READ_ENDPOINT, (char*) buf, 8, timeout);
+       if (val < 0) {
+               cerr << "Tranzport read error, val = " << val << endl;
+               return val;
+       }
+       if (val != 8) {
+               cerr << "Tranzport short read, val = " << val << endl;
+               return -1;
+       }
+
+       /*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
+
+       uint32_t this_button_mask;
+       uint32_t button_changes;
+
+       _device_status = buf[1];
+       this_button_mask = 0;
+       this_button_mask |= buf[2] << 24;
+       this_button_mask |= buf[3] << 16;
+       this_button_mask |= buf[4] << 8;
+       this_button_mask |= buf[5];
+       _datawheel = buf[6];
+
+       button_changes = (this_button_mask ^ buttonmask);
+       buttonmask = this_button_mask;
+
+       if (button_changes & ButtonBattery) {
+               if (buttonmask & ButtonBattery) {
+                       button_event_battery_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_battery_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonBacklight) {
+               if (buttonmask & ButtonBacklight) {
+                       button_event_backlight_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_backlight_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonTrackLeft) {
+               if (buttonmask & ButtonTrackLeft) {
+                       button_event_trackleft_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_trackleft_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonTrackRight) {
+               if (buttonmask & ButtonTrackRight) {
+                       button_event_trackright_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_trackright_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonTrackRec) {
+               if (buttonmask & ButtonTrackRec) {
+                       button_event_trackrec_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_trackrec_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonTrackMute) {
+               if (buttonmask & ButtonTrackMute) {
+                       button_event_trackmute_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_trackmute_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonTrackSolo) {
+               if (buttonmask & ButtonTrackSolo) {
+                       button_event_tracksolo_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_tracksolo_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonUndo) {
+               if (buttonmask & ButtonUndo) {
+                       button_event_undo_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_undo_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonIn) {
+               if (buttonmask & ButtonIn) {
+                       button_event_in_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_in_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonOut) {
+               if (buttonmask & ButtonOut) {
+                       button_event_out_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_out_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonPunch) {
+               if (buttonmask & ButtonPunch) {
+                       button_event_punch_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_punch_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonLoop) {
+               if (buttonmask & ButtonLoop) {
+                       button_event_loop_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_loop_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonPrev) {
+               if (buttonmask & ButtonPrev) {
+                       button_event_prev_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_prev_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonAdd) {
+               if (buttonmask & ButtonAdd) {
+                       button_event_add_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_add_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonNext) {
+               if (buttonmask & ButtonNext) {
+                       button_event_next_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_next_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonRewind) {
+               if (buttonmask & ButtonRewind) {
+                       button_event_rewind_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_rewind_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonFastForward) {
+               if (buttonmask & ButtonFastForward) {
+                       button_event_fastforward_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_fastforward_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonStop) {
+               if (buttonmask & ButtonStop) {
+                       button_event_stop_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_stop_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonPlay) {
+               if (buttonmask & ButtonPlay) {
+                       button_event_play_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_play_release (buttonmask&ButtonShift);
+               }
+       }
+       if (button_changes & ButtonRecord) {
+               if (buttonmask & ButtonRecord) {
+                       button_event_record_press (buttonmask&ButtonShift);
+               } else {
+                       button_event_record_release (buttonmask&ButtonShift);
+               }
+       }
+               
+       return 0;
+}
+
+void
+TranzportControlProtocol::show_current_track ()
+{
+       current_route = session.route_by_remote_id (current_track_id);
+       
+       if (current_route == 0) {
+               char buf[5];
+               lcd_clear ();
+               lcd_write (0, "NO T");
+               lcd_write (1, "RACK");
+               lcd_write (2, " ID ");
+               snprintf (buf, sizeof (buf), "%4d", current_track_id);
+               lcd_write (3, buf);
+               return;
+       }
+
+       string name = current_route->name();
+
+       lcd_write (0, name.substr (0, 4).c_str());
+       lcd_write (1, name.substr (4, 4).c_str());
+}
+       
+void
+TranzportControlProtocol::button_event_battery_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_battery_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_backlight_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_backlight_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackleft_press (bool shifted)
+{
+       if (current_track_id == 0) {
+               current_track_id = session.nroutes() - 1;
+       } else {
+               current_track_id--;
+       }
+       
+       show_current_track ();
+}
+
+void
+TranzportControlProtocol::button_event_trackleft_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackright_press (bool shifted)
+{
+       if (current_track_id == session.nroutes()) {
+               current_track_id = 0;
+       } else {
+               current_track_id++;
+       }
+       
+       show_current_track ();
+}
+
+void
+TranzportControlProtocol::button_event_trackright_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackrec_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackrec_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackmute_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackmute_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_tracksolo_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_tracksolo_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_undo_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_undo_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_in_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_in_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_out_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_out_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_punch_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_punch_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_loop_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_loop_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_prev_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_prev_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_add_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_add_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_next_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_next_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_rewind_press (bool shifted)
+{
+       session.request_transport_speed (-2.0f);
+}
+
+void
+TranzportControlProtocol::button_event_rewind_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_fastforward_press (bool shifted)
+{
+       session.request_transport_speed (2.0f);
+}
+
+void
+TranzportControlProtocol::button_event_fastforward_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_stop_press (bool shifted)
+{
+       session.request_transport_speed (0.0);
+}
+
+void
+TranzportControlProtocol::button_event_stop_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_play_press (bool shifted)
+{
+       session.request_transport_speed (1.0);
+}
+
+void
+TranzportControlProtocol::button_event_play_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_record_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_record_release (bool shifted)
+{
+}