Add some comments.
[ardour.git] / libs / ardour / session_transport.cc
index 2f9f8257f9344d3d8e3f2e38515861ec6e18cfa7..d032e31b93febad22c2b8b90147888ab71d19ea4 100644 (file)
@@ -15,7 +15,6 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    $Id$
 */
 
 #include <cmath>
@@ -27,8 +26,9 @@
 
 #include <pbd/undo.h>
 #include <pbd/error.h>
-#include <pbd/lockmonitor.h>
+#include <glibmm/thread.h>
 #include <pbd/pthread_utils.h>
+#include <pbd/memento_command.h>
 
 #include <midi++/mmc.h>
 #include <midi++/port.h>
@@ -36,7 +36,7 @@
 #include <ardour/ardour.h>
 #include <ardour/audioengine.h>
 #include <ardour/session.h>
-#include <ardour/diskstream.h>
+#include <ardour/audio_diskstream.h>
 #include <ardour/auditioner.h>
 #include <ardour/slave.h>
 #include <ardour/location.h>
 using namespace std;
 using namespace ARDOUR;
 using namespace sigc;
+using namespace PBD;
 
 void
 Session::request_input_change_handling ()
 {
-       Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0);
-       queue_event (ev);
+       if (!(_state_of_the_state & (InitialConnecting|Deletion))) {
+               Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0);
+               queue_event (ev);
+       }
 }
 
 void
-Session::request_slave_source (SlaveSource src, jack_nframes_t pos)
+Session::request_slave_source (SlaveSource src)
 {
-       Event* ev = new Event (Event::SetSlaveSource, Event::Add, Event::Immediate, pos, 0.0);
+       Event* ev = new Event (Event::SetSlaveSource, Event::Add, Event::Immediate, 0, 0.0);
 
-       if (src == Session::JACK) {
-         /* could set_seamless_loop() be disposed of entirely?*/
-         set_seamless_loop (false);
+       if (src == JACK) {
+               /* could set_seamless_loop() be disposed of entirely?*/
+               Config->set_seamless_loop (false);
        } else {
-
-         set_seamless_loop (true);
+               Config->set_seamless_loop (true);
        }
        ev->slave = src;
        queue_event (ev);
@@ -78,7 +80,7 @@ Session::request_transport_speed (float speed)
 }
 
 void
-Session::request_diskstream_speed (DiskStream& ds, float speed)
+Session::request_diskstream_speed (Diskstream& ds, float speed)
 {
        Event* ev = new Event (Event::SetDiskstreamSpeed, Event::Add, Event::Immediate, 0, speed);
        ev->set_ptr (&ds);
@@ -93,21 +95,21 @@ Session::request_stop (bool abort)
 }
 
 void
-Session::request_locate (jack_nframes_t target_frame, bool with_roll)
+Session::request_locate (nframes_t target_frame, bool with_roll)
 {
        Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, false);
        queue_event (ev);
 }
 
 void
-Session::force_locate (jack_nframes_t target_frame, bool with_roll)
+Session::force_locate (nframes_t target_frame, bool with_roll)
 {
        Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, true);
        queue_event (ev);
 }
 
 void
-Session::request_auto_loop (bool yn)
+Session::request_play_loop (bool yn)
 {
        Event* ev;      
        Location *location = _locations.auto_loop_location();
@@ -121,25 +123,10 @@ Session::request_auto_loop (bool yn)
        ev = new Event (Event::SetLoop, Event::Add, Event::Immediate, 0, 0.0, yn);
        queue_event (ev);
 
-       if (!yn && seamless_loop && transport_rolling()) {
+       if (!yn && Config->get_seamless_loop() && transport_rolling()) {
                // request an immediate locate to refresh the diskstreams
                // after disabling looping
-               request_locate (_transport_frame-1, true);
-       }
-}
-
-void
-Session::set_seamless_loop (bool yn)
-{
-       if (seamless_loop != yn) {
-               seamless_loop = yn;
-
-               if (auto_loop && transport_rolling()) {
-                       // to reset diskstreams etc
-                       request_auto_loop (true);
-               }
-               
-               ControlChanged (SeamlessLoop); /* EMIT */
+               request_locate (_transport_frame-1, false);
        }
 }
 
@@ -147,6 +134,11 @@ void
 Session::realtime_stop (bool abort)
 {
        /* assume that when we start, we'll be moving forwards */
+       
+       // FIXME: where should this really be? [DR]
+       //send_full_time_code();
+       deliver_mmc (MIDI::MachineControl::cmdStop, _transport_frame);
+       deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
 
        if (_transport_speed < 0.0f) {
                post_transport_work = PostTransportWork (post_transport_work | PostTransportStop | PostTransportReverse);
@@ -165,22 +157,11 @@ Session::realtime_stop (bool abort)
                decrement_transport_position (_worst_output_latency);
 #endif
 
-               if (_transport_frame > current_end_frame()) {
-                       
-                       /* first capture resets end location; later captures can only extend the length */
-
-                       if (_end_location_is_free) {
-                               end_location->set_end (_transport_frame);
-                               _end_location_is_free = false;
-                       } else if (_transport_frame > end_location->start()) {
-                               end_location->set_end (_transport_frame);
-                       }
+               /* the duration change is not guaranteed to have happened, but is likely */
 
-                       post_transport_work = PostTransportWork (post_transport_work | PostTransportDuration);
-               }
+               post_transport_work = PostTransportWork (post_transport_work | PostTransportDuration);
        }
 
-
        if (abort) {
                post_transport_work = PostTransportWork (post_transport_work | PostTransportAbort);
        }
@@ -195,23 +176,32 @@ Session::realtime_stop (bool abort)
                
        _transport_speed = 0;
 
-       transport_sub_state = (auto_return ? AutoReturning : 0);
+       if (Config->get_use_video_sync()) {
+               waiting_for_sync_offset = true;
+       }
+
+       transport_sub_state = ((Config->get_slave_source() == None && Config->get_auto_return()) ? AutoReturning : 0);
 }
 
 void
 Session::butler_transport_work ()
 {
-       RWLockMonitor rm (route_lock, false, __LINE__, __FILE__);
-       RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
-               
+  restart:
+       bool finished;
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+       int on_entry = g_atomic_int_get (&butler_should_do_transport_work);
+       finished = true;
+
        if (post_transport_work & PostTransportCurveRealloc) {
-               for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                        (*i)->curve_reallocate();
                }
        }
 
        if (post_transport_work & PostTransportInputChange) {
-               for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                        (*i)->non_realtime_input_change ();
                }
        }
@@ -227,65 +217,91 @@ Session::butler_transport_work ()
                cumulative_rf_motion = 0;
                reset_rf_scale (0);
 
-               for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
-                       if (!(*i)->hidden()) {
-                               if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) {
-                                       (*i)->seek ((jack_nframes_t) (_transport_frame * (double) (*i)->speed()));
+               /* don't seek if locate will take care of that in non_realtime_stop() */
+
+               if (!(post_transport_work & PostTransportLocate)) {
+                       
+                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+                               if (!(*i)->hidden()) {
+                                       if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) {
+                                               (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed()));
+                                       }
+                                       else {
+                                               (*i)->seek (_transport_frame);
+                                       }
                                }
-                               else {
-                                       (*i)->seek (_transport_frame);
+                               if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
+                                       /* new request, stop seeking, and start again */
+                                       g_atomic_int_dec_and_test (&butler_should_do_transport_work);
+                                       goto restart;
                                }
                        }
                }
        }
 
        if (post_transport_work & (PostTransportStop|PostTransportLocate)) {
-               non_realtime_stop (post_transport_work & PostTransportAbort);
+               non_realtime_stop (post_transport_work & PostTransportAbort, on_entry, finished);
+               if (!finished) {
+                       g_atomic_int_dec_and_test (&butler_should_do_transport_work);
+                       goto restart;
+               }
        }
 
        if (post_transport_work & PostTransportOverWrite) {
-               non_realtime_overwrite ();
+               non_realtime_overwrite (on_entry, finished);
+               if (!finished) {
+                       g_atomic_int_dec_and_test (&butler_should_do_transport_work);
+                       goto restart;
+               }
        }
 
        if (post_transport_work & PostTransportAudition) {
                non_realtime_set_audition ();
        }
-
-       atomic_dec (&butler_should_do_transport_work);
+       
+       g_atomic_int_dec_and_test (&butler_should_do_transport_work);
 }
 
 void
 Session::non_realtime_set_speed ()
 {
-       RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
+       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
 
-       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                (*i)->non_realtime_set_speed ();
        }
 }
 
 void
-Session::non_realtime_overwrite ()
+Session::non_realtime_overwrite (int on_entry, bool& finished)
 {
-       RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
+       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
 
-       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                if ((*i)->pending_overwrite) {
                        (*i)->overwrite_existing_buffers ();
                }
+               if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
+                       finished = false;
+                       return;
+               }
        }
 }
 
 void
-Session::non_realtime_stop (bool abort)
+Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
 {
        struct tm* now;
        time_t     xnow;
        bool       did_record;
-       
+       bool       saved;
+
        did_record = false;
+       saved = false;
+
+       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
        
-       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                if ((*i)->get_captured_frames () != 0) {
                        did_record = true;
                        break;
@@ -307,49 +323,66 @@ Session::non_realtime_stop (bool abort)
 
        if (did_record) {
                begin_reversible_command ("capture");
-
+               
                Location* loc = _locations.end_location();
+               bool change_end = false;
+               
+               if (_transport_frame < loc->end()) {
 
-               if (loc && !_have_captured) {
-                               
-                       /* first capture.
-                          
-                          note: later captures that extend the session length get
-                          handled because of playlist length changes.
-                       */
+                       /* stopped recording before current end */
+
+                       if (_end_location_is_free) {
+
+                               /* first capture for this session, move end back to where we are */
+
+                               change_end = true;
+                       } 
+
+               } else if (_transport_frame > loc->end()) {
                        
-                 add_undo (sigc::retype_return<void>(sigc::bind (mem_fun (*loc, &Location::set_end), loc->end())));
-                 add_redo (sigc::retype_return<void>(sigc::bind (mem_fun (*loc, &Location::set_end), _transport_frame)));
+                       /* stopped recording after the current end, extend it */
+
+                       change_end = true;
+               }
+               
+               if (change_end) {
+                        XMLNode &before = loc->get_state();
+                        loc->set_end(_transport_frame);
+                        XMLNode &after = loc->get_state();
+                        add_command (new MementoCommand<Location>(*loc, &before, &after));
                }
 
+               _end_location_is_free = false;
                _have_captured = true;
        }
 
-       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                (*i)->transport_stopped (*now, xnow, abort);
        }
        
-       for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (!(*i)->hidden()) {
                        (*i)->set_pending_declick (0);
                }
        }
-
+       
        if (did_record) {
                commit_reversible_command ();
        }       
-
+       
        if (_engine.running()) {
                update_latency_compensation (true, abort);
        }
 
-       if (auto_return || (post_transport_work & PostTransportLocate) || synced_to_jack()) {
-
+       if ((Config->get_slave_source() == None && Config->get_auto_return()) || (post_transport_work & PostTransportLocate) || synced_to_jack()) {
+               
                if (pending_locate_flush) {
                        flush_all_redirects ();
                }
-
-               if ((auto_return || synced_to_jack()) && !(post_transport_work & PostTransportLocate)) {
+               
+               if (((Config->get_slave_source() == None && Config->get_auto_return()) || synced_to_jack()) && !(post_transport_work & PostTransportLocate)) {
 
                        _transport_frame = last_stop_frame;
 
@@ -362,40 +395,39 @@ Session::non_realtime_stop (bool abort)
        }
 #endif
 
-               for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                        if (!(*i)->hidden()) {
                                if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) {
-                                       (*i)->seek ((jack_nframes_t) (_transport_frame * (double) (*i)->speed()));
+                                       (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed()));
                                }
                                else {
                                        (*i)->seek (_transport_frame);
                                }
                        }
+                       if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
+                               finished = false;
+                               /* we will be back */
+                               return;
+                       }
                }
 
-               deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
-
 #ifdef LEAVE_TRANSPORT_UNADJUSTED
        }
 #endif
 
        last_stop_frame = _transport_frame;
 
-       send_full_time_code ();
-       deliver_mmc (MIDI::MachineControl::cmdStop, 0);
-       deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
-
        if (did_record) {
 
                /* XXX its a little odd that we're doing this here
                   when realtime_stop(), which has already executed,
                   will have done this.
                */
-
+               
                if (!Config->get_latched_record_enable()) {
-                       atomic_set (&_record_status, Disabled);
+                       g_atomic_int_set (&_record_status, Disabled);
                } else {
-                       atomic_set (&_record_status, Enabled);
+                       g_atomic_int_set (&_record_status, Enabled);
                }
                RecordStateChanged (); /* emit signal */
        }
@@ -403,6 +435,7 @@ Session::non_realtime_stop (bool abort)
        if ((post_transport_work & PostTransportLocate) && get_record_enabled()) {
                /* capture start has been changed, so save pending state */
                save_state ("", true);
+               saved = true;
        }
 
         /* always try to get rid of this */
@@ -411,7 +444,7 @@ Session::non_realtime_stop (bool abort)
        
        /* save the current state of things if appropriate */
 
-       if (did_record) {
+       if (did_record && !saved) {
                save_state (_current_snapshot_name);
        }
 
@@ -423,7 +456,7 @@ Session::non_realtime_stop (bool abort)
                _play_range = false;
 
                /* do not turn off autoloop on stop */
-
+               
        }
 
        PositionChanged (_transport_frame); /* EMIT SIGNAL */
@@ -431,7 +464,7 @@ Session::non_realtime_stop (bool abort)
 
        /* and start it up again if relevant */
 
-       if ((post_transport_work & PostTransportLocate) && _slave_type == None && pending_locate_roll) {
+       if ((post_transport_work & PostTransportLocate) && Config->get_slave_source() == None && pending_locate_roll) {
                request_transport_speed (1.0);
                pending_locate_roll = false;
        }
@@ -462,7 +495,7 @@ Session::check_declick_out ()
 }
 
 void
-Session::set_auto_loop (bool yn)
+Session::set_play_loop (bool yn)
 {
        /* Called from event-handling context */
        
@@ -472,7 +505,7 @@ Session::set_auto_loop (bool yn)
        
        set_dirty();
 
-       if (yn && seamless_loop && synced_to_jack()) {
+       if (yn && Config->get_seamless_loop() && synced_to_jack()) {
                warning << _("Seamless looping cannot be supported while Ardour is using JACK transport.\n"
                             "Recommend changing the configured options")
                        << endmsg;
@@ -480,16 +513,17 @@ Session::set_auto_loop (bool yn)
        }
 
        
-       if ((auto_loop = yn)) {
+       if ((play_loop = yn)) {
 
                Location *loc;
 
                
                if ((loc = _locations.auto_loop_location()) != 0) {
 
-                       if (seamless_loop) {
+                       if (Config->get_seamless_loop()) {
                                // set all diskstreams to use internal looping
-                               for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+                               boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+                               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                                        if (!(*i)->hidden()) {
                                                (*i)->set_loop (loc);
                                        }
@@ -497,7 +531,8 @@ Session::set_auto_loop (bool yn)
                        }
                        else {
                                // set all diskstreams to NOT use internal looping
-                               for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+                               boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+                               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                                        if (!(*i)->hidden()) {
                                                (*i)->set_loop (0);
                                        }
@@ -527,32 +562,33 @@ Session::set_auto_loop (bool yn)
                clear_events (Event::AutoLoop);
 
                // set all diskstreams to NOT use internal looping
-               for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+               boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                        if (!(*i)->hidden()) {
                                (*i)->set_loop (0);
                        }
                }
                
        }
-       
-       ControlChanged (AutoLoop); /* EMIT SIGNAL */
 }
 
 void
 Session::flush_all_redirects ()
 {
-       for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                (*i)->flush_redirects ();
        }
 }
 
 void
-Session::start_locate (jack_nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
+Session::start_locate (nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
 {
        if (synced_to_jack()) {
 
                float sp;
-               jack_nframes_t pos;
+               nframes_t pos;
 
                _slave->speed_and_position (sp, pos);
 
@@ -577,7 +613,7 @@ Session::start_locate (jack_nframes_t target_frame, bool with_roll, bool with_fl
 }
 
 void
-Session::locate (jack_nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
+Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
 {
        if (actively_recording()) {
                return;
@@ -591,7 +627,12 @@ Session::locate (jack_nframes_t target_frame, bool with_roll, bool with_flush, b
                return;
        }
 
+       // Update SMPTE time
+       // [DR] FIXME: find out exactly where this should go below
        _transport_frame = target_frame;
+       smpte_time(_transport_frame, transmitting_smpte_time);
+       outbound_mtc_smpte_frame = _transport_frame;
+       next_quarter_frame_to_send = 0;
 
        if (_transport_speed && (!with_loop || loop_changing)) {
                /* schedule a declick. we'll be called again when its done */
@@ -605,7 +646,7 @@ Session::locate (jack_nframes_t target_frame, bool with_roll, bool with_flush, b
                } 
        }
 
-       if (transport_rolling() && !auto_play && !with_roll && !(synced_to_jack() && auto_loop)) {
+       if (transport_rolling() && (!auto_play_legal || !Config->get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) {
                realtime_stop (false);
        } 
 
@@ -623,7 +664,7 @@ Session::locate (jack_nframes_t target_frame, bool with_roll, bool with_flush, b
 
                /* this is functionally what clear_clicks() does but with a tentative lock */
 
-               TentativeRWLockMonitor clickm (click_lock, true, __LINE__, __FILE__);
+               Glib::RWLock::WriterLock clickm (click_lock, Glib::TRY_LOCK);
        
                if (clickm.locked()) {
                        
@@ -637,28 +678,23 @@ Session::locate (jack_nframes_t target_frame, bool with_roll, bool with_flush, b
 
        if (with_roll) {
                /* switch from input if we're going to roll */
-               if (Config->get_use_hardware_monitoring()) {
-                       /* Even though this is called from RT context we are using
-                          a non-tentative rwlock here,  because the action must occur.
-                          The rarity and short potential lock duration makes this "OK"
-                       */
-                       RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
-                       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+               if (Config->get_monitoring_model() == HardwareMonitoring) {
+
+                       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                                if ((*i)->record_enabled ()) {
                                        //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
-                                       (*i)->monitor_input (!auto_input);
+                                       (*i)->monitor_input (!Config->get_auto_input());
                                }
                        }
                }
        } else {
                /* otherwise we're going to stop, so do the opposite */
-               if (Config->get_use_hardware_monitoring()) {
-                       /* Even though this is called from RT context we are using
-                          a non-tentative rwlock here,  because the action must occur.
-                          The rarity and short potential lock duration makes this "OK"
-                       */
-                       RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
-                       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+               if (Config->get_monitoring_model() == HardwareMonitoring) {
+                       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                                if ((*i)->record_enabled ()) {
                                        //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
                                        (*i)->monitor_input (true);
@@ -667,19 +703,25 @@ Session::locate (jack_nframes_t target_frame, bool with_roll, bool with_flush, b
                }
        }
 
-       /* cancel autoloop if transport pos outside of loop range */
-       if (auto_loop) {
+       /* cancel looped playback if transport pos outside of loop range */
+       if (play_loop) {
                Location* al = _locations.auto_loop_location();
                
                if (al && (_transport_frame < al->start() || _transport_frame > al->end())) {
                        // cancel looping directly, this is called from event handling context
-                       set_auto_loop(false);
+                       set_play_loop (false);
                }
        }
        
        loop_changing = false;
+
+       _send_smpte_update = true;
 }
 
+/** Set the transport speed.
+ * @param speed New speed
+ * @param abort
+ */
 void
 Session::set_transport_speed (float speed, bool abort)
 {
@@ -695,14 +737,13 @@ Session::set_transport_speed (float speed, bool abort)
 
        if (transport_rolling() && speed == 0.0) {
 
-               if (Config->get_use_hardware_monitoring())
+               /* we are rolling and we want to stop */
+
+               if (Config->get_monitoring_model() == HardwareMonitoring)
                {
-                       /* Even though this is called from RT context we are using
-                          a non-tentative rwlock here,  because the action must occur.
-                          The rarity and short potential lock duration makes this "OK"
-                       */
-                       RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
-                       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+                       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                                if ((*i)->record_enabled ()) {
                                        //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
                                        (*i)->monitor_input (true);     
@@ -715,17 +756,21 @@ Session::set_transport_speed (float speed, bool abort)
                } else {
                        stop_transport (abort);
                }
-
+               
        } else if (transport_stopped() && speed == 1.0) {
 
-               if (Config->get_use_hardware_monitoring()) {
-                       /* Even though this is called from RT context we are using
-                          a non-tentative rwlock here,  because the action must occur.
-                          The rarity and short potential lock duration makes this "OK"
-                       */
-                       RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
-                       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
-                               if (auto_input && (*i)->record_enabled ()) {
+               /* we are stopped and we want to start rolling at speed 1 */
+
+               if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) {
+                       return;
+               }
+
+               if (Config->get_monitoring_model() == HardwareMonitoring) {
+
+                       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+                               if (Config->get_auto_input() && (*i)->record_enabled ()) {
                                        //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
                                        (*i)->monitor_input (false);    
                                }
@@ -740,6 +785,10 @@ Session::set_transport_speed (float speed, bool abort)
 
        } else {
 
+               if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) {
+                       return;
+               }
+
                if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) {
                        warning << _("Global varispeed cannot be supported while Ardour is connected to JACK transport control")
                                << endmsg;
@@ -764,14 +813,15 @@ Session::set_transport_speed (float speed, bool abort)
                   before the last stop, then we have to do extra work.
                */
 
-               if ((_transport_speed && speed * _transport_speed < 0.0f) || (_last_transport_speed * speed < 0.0f)) {
+               if ((_transport_speed && speed * _transport_speed < 0.0f) || (_last_transport_speed * speed < 0.0f) || (_last_transport_speed == 0.0f && speed < 0.0f)) {
                        post_transport_work = PostTransportWork (post_transport_work | PostTransportReverse);
                }
                
                _last_transport_speed = _transport_speed;
                _transport_speed = speed;
                
-               for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+               boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                        if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
                                post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
                        }
@@ -783,13 +833,15 @@ Session::set_transport_speed (float speed, bool abort)
        }
 }
 
+
+/** Stop the transport.  */
 void
 Session::stop_transport (bool abort)
 {
        if (_transport_speed == 0.0f) {
                return;
        }
-
+       
        if (actively_recording() && !(transport_sub_state & StopPendingCapture) && 
            _worst_output_latency > current_block_size) 
        {
@@ -812,9 +864,11 @@ Session::stop_transport (bool abort)
                return;
        } 
 
+
        if ((transport_sub_state & PendingDeclickOut) == 0) {
                transport_sub_state |= PendingDeclickOut;
                /* we'll be called again after the declick */
+               pending_abort = abort;
                return;
        }
 
@@ -833,7 +887,7 @@ Session::start_transport ()
 
        switch (record_status()) {
        case Enabled:
-               if (!punch_in) {
+               if (!Config->get_punch_in()) {
                        enable_record ();
                }
                break;
@@ -861,15 +915,19 @@ Session::actually_start_transport ()
        transport_sub_state |= PendingDeclickIn;
        _transport_speed = 1.0;
        
-       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                (*i)->realtime_set_speed ((*i)->speed(), true);
        }
 
-       send_mmc_in_another_thread (MIDI::MachineControl::cmdDeferredPlay, 0);
-       
+       deliver_mmc(MIDI::MachineControl::cmdDeferredPlay, _transport_frame);
+
        TransportStateChange (); /* EMIT SIGNAL */
 }
 
+/** Do any transport work in the audio thread that needs to be done after the
+ * transport thread is finished.  Audio thread, realtime safe.
+ */
 void
 Session::post_transport ()
 {
@@ -888,8 +946,7 @@ Session::post_transport ()
 
        if (post_transport_work & PostTransportLocate) {
 
-               if ((auto_play && !_exporting) || (post_transport_work & PostTransportRoll)) {
-                       
+               if (((Config->get_slave_source() == None && (auto_play_legal && Config->get_auto_play())) && !_exporting) || (post_transport_work & PostTransportRoll)) {
                        start_transport ();
                        
                } else {
@@ -903,15 +960,7 @@ Session::post_transport ()
 }
 
 void
-Session::set_rf_speed (float speed)
-{
-       rf_speed = speed;
-       cumulative_rf_motion = 0;
-       reset_rf_scale (0);
-}
-
-void
-Session::reset_rf_scale (jack_nframes_t motion)
+Session::reset_rf_scale (nframes_t motion)
 {
        cumulative_rf_motion += motion;
 
@@ -930,31 +979,24 @@ Session::reset_rf_scale (jack_nframes_t motion)
        }
 }
 
-int
-Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
+void
+Session::set_slave_source (SlaveSource src)
 {
        bool reverse = false;
        bool non_rt_required = false;
 
        if (_transport_speed) {
                error << _("please stop the transport before adjusting slave settings") << endmsg;
-               /* help out non-MVC friendly UI's by telling them the slave type changed */
-                ControlChanged (SlaveType); /* EMIT SIGNAL */
-               return 0;
+               return;
        }
 
-       if (src == _slave_type) {
-               return 0;
-       } 
-
 //     if (src == JACK && Config->get_jack_time_master()) {
-//             return -1;
+//             return;
 //     }
        
        if (_slave) {
                delete _slave;
                _slave = 0;
-               _slave_type = None;
        }
 
        if (_transport_speed < 0.0) {
@@ -965,7 +1007,7 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
        case None:
                stop_transport ();
                break;
-
+               
        case MTC:
                if (_mtc_port) {
                        try {
@@ -973,12 +1015,12 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
                        }
 
                        catch (failed_constructor& err) {
-                               return -1;
+                               return;
                        }
 
                } else {
                        error << _("No MTC port defined: MTC slaving is impossible.") << endmsg;
-                       return -1;
+                       return;
                }
                _desired_transport_speed = _transport_speed;
                break;
@@ -988,10 +1030,11 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
                _desired_transport_speed = _transport_speed;
                break;
        };
-       
-       _slave_type = src;
 
-       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+       Config->set_slave_source (src);
+       
+       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                if (!(*i)->hidden()) {
                        if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
                                non_rt_required = true;
@@ -1010,9 +1053,6 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
        }
 
        set_dirty();
-       ControlChanged (SlaveType); /* EMIT SIGNAL */
-       
-       return 0;
 }
 
 void
@@ -1023,7 +1063,7 @@ Session::reverse_diskstream_buffers ()
 }
 
 void
-Session::set_diskstream_speed (DiskStream* stream, float speed)
+Session::set_diskstream_speed (Diskstream* stream, float speed)
 {
        if (stream->realtime_set_speed (speed, false)) {
                post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
@@ -1061,8 +1101,6 @@ Session::set_play_range (bool yn)
                        Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0f, false);
                        merge_event (ev);
                }
-
-                ControlChanged (PlayRange); /* EMIT SIGNAL */
        }
 }
 
@@ -1095,7 +1133,7 @@ Session::setup_auto_play ()
                        /* locating/stopping is subject to delays for declicking.
                         */
                        
-                       jack_nframes_t requested_frame = (*i).end;
+                       nframes_t requested_frame = (*i).end;
                        
                        if (requested_frame > current_block_size) {
                                requested_frame -= current_block_size;
@@ -1128,10 +1166,10 @@ Session::setup_auto_play ()
 }
 
 void
-Session::request_bounded_roll (jack_nframes_t start, jack_nframes_t end)
+Session::request_bounded_roll (nframes_t start, nframes_t end)
 {
        request_stop ();
-       Event *ev = new Event (Event::StopOnce, Event::Replace, Event::Immediate, end, 0.0);
+       Event *ev = new Event (Event::StopOnce, Event::Replace, end, Event::Immediate, 0.0);
        queue_event (ev);
        request_locate (start, true);
 }
@@ -1139,6 +1177,8 @@ Session::request_bounded_roll (jack_nframes_t start, jack_nframes_t end)
 void
 Session::engine_halted ()
 {
+       bool ignored;
+
        /* there will be no more calls to process(), so
           we'd better clean up for ourselves, right now.
 
@@ -1146,12 +1186,12 @@ Session::engine_halted ()
           the picture.
        */
 
-       atomic_set (&butler_should_do_transport_work, 0);
+       g_atomic_int_set (&butler_should_do_transport_work, 0);
        post_transport_work = PostTransportWork (0);
        stop_butler ();
        
        realtime_stop (false);
-       non_realtime_stop (false);
+       non_realtime_stop (false, 0, ignored);
        transport_sub_state = 0;
 
        TransportStateChange (); /* EMIT SIGNAL */
@@ -1182,18 +1222,18 @@ Session::update_latency_compensation (bool with_stop, bool abort)
                return;
        }
 
-       RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
-       RWLockMonitor lm2 (diskstream_lock, false, __LINE__, __FILE__);
        _worst_track_latency = 0;
 
-       for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (with_stop) {
-                       (*i)->transport_stopped (abort, (post_transport_work & PostTransportLocate), 
-                                                (!(post_transport_work & PostTransportLocate) || pending_locate_flush));
+                       (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate), 
+                                                       (!(post_transport_work & PostTransportLocate) || pending_locate_flush));
                }
 
-               jack_nframes_t old_latency = (*i)->signal_latency ();
-               jack_nframes_t track_latency = (*i)->update_total_latency ();
+               nframes_t old_latency = (*i)->signal_latency ();
+               nframes_t track_latency = (*i)->update_total_latency ();
 
                if (old_latency != track_latency) {
                        update_jack = true;
@@ -1204,7 +1244,7 @@ Session::update_latency_compensation (bool with_stop, bool abort)
                }
        }
 
-       for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                (*i)->set_latency_delay (_worst_track_latency);
        }
 
@@ -1214,12 +1254,14 @@ Session::update_latency_compensation (bool with_stop, bool abort)
                _engine.update_total_latencies ();
        }
 
-       set_worst_io_latencies (false);
+       set_worst_io_latencies ();
 
        /* reflect any changes in latencies into capture offsets
        */
        
-       for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
                (*i)->set_capture_offset ();
        }
 }
@@ -1229,3 +1271,19 @@ Session::update_latency_compensation_proxy (void* ignored)
 {
        update_latency_compensation (false, false);
 }
+
+void
+Session::allow_auto_play (bool yn)
+{
+       auto_play_legal = yn;
+}
+
+void
+Session::reset_jack_connection (jack_client_t* jack)
+{
+       JACK_Slave* js;
+
+       if (_slave && ((js = dynamic_cast<JACK_Slave*> (_slave)) != 0)) {
+               js->reset_client (jack);
+       }
+}