force IFS=/ when calling path_expand, so that whitespace in paths doesn't cause worde...
[ardour.git] / libs / ardour / session_transport.cc
index ac29509f08eb7677beb789a20b36e108da8fa03d..de295a8e8a39dbc8073e64e6e12252205d9b6f8c 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour/audioengine.h"
 #include "ardour/auditioner.h"
 #include "ardour/butler.h"
+#include "ardour/click.h"
 #include "ardour/debug.h"
 #include "ardour/location.h"
 #include "ardour/session.h"
@@ -196,7 +197,7 @@ Session::request_play_range (list<AudioRange>* range, bool leave_rolling)
 void
 Session::realtime_stop (bool abort, bool clear_state)
 {
-       DEBUG_TRACE (DEBUG::Transport, "realtime stop\n");
+       DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1\n", _transport_frame));
        PostTransportWork todo = PostTransportWork (0);
 
        /* assume that when we start, we'll be moving forwards */
@@ -270,6 +271,15 @@ Session::realtime_stop (bool abort, bool clear_state)
        transport_sub_state = 0;
 }
 
+void
+Session::realtime_locate ()
+{
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               (*i)->realtime_locate ();
+       }
+}
+
 void
 Session::butler_transport_work ()
 {
@@ -416,6 +426,12 @@ Session::non_realtime_locate ()
                        tr->non_realtime_locate (_transport_frame);
                }
        }
+
+       /* XXX: it would be nice to generate the new clicks here (in the non-RT thread)
+          rather than clearing them so that the RT thread has to spend time constructing
+          them (in Session::click).
+        */
+       clear_clicks ();
 }
 
 
@@ -474,7 +490,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
        }
 
        if (abort && did_record) {
-               _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);                
+               _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
        }
 
        boost::shared_ptr<RouteList> r = routes.reader ();
@@ -519,7 +535,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
                        if (_requested_return_frame >= 0) {
 
                                /* explicit return request pre-queued in event list. overrides everything else */
-                               
+
                                cerr << "explicit auto-return to " << _requested_return_frame << endl;
 
                                _transport_frame = _requested_return_frame;
@@ -535,7 +551,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
                                                if (!synced_to_jack()) {
 
                                                        Location *location = _locations->auto_loop_location();
-                                                       
+
                                                        if (location != 0) {
                                                                _transport_frame = location->start();
                                                        } else {
@@ -554,21 +570,21 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
                                                }
 
                                        } else {
-                                               
+
                                                /* regular auto-return */
-                                               
+
                                                _transport_frame = _last_roll_location;
                                                do_locate = true;
                                        }
-                               } 
+                               }
                        }
 
-                       _requested_return_frame = -1;                   
+                       _requested_return_frame = -1;
 
                        if (do_locate) {
                                _engine.transport_locate (_transport_frame);
                        }
-               } 
+               }
 
        }
 
@@ -667,7 +683,7 @@ Session::unset_play_loop ()
 {
        play_loop = false;
        clear_events (SessionEvent::AutoLoop);
-       
+
        // set all tracks to NOT use internal looping
        boost::shared_ptr<RouteList> rl = routes.reader ();
        for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
@@ -689,7 +705,7 @@ Session::set_play_loop (bool yn)
                /* nothing to do, or can't change loop status while recording */
                return;
        }
-       
+
        if (yn && Config->get_seamless_loop() && synced_to_jack()) {
                warning << string_compose (
                        _("Seamless looping cannot be supported while %1 is using JACK transport.\n"
@@ -697,7 +713,7 @@ Session::set_play_loop (bool yn)
                        << endmsg;
                return;
        }
-       
+
        if (yn) {
 
                play_loop = true;
@@ -726,13 +742,13 @@ Session::set_play_loop (bool yn)
                                        }
                                }
                        }
-                       
+
                        /* put the loop event into the event list */
-                       
+
                        SessionEvent* event = new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f);
                        merge_event (event);
 
-                       /* locate to start of loop and roll. If doing seamless loop, force a 
+                       /* locate to start of loop and roll. If doing seamless loop, force a
                           locate+buffer refill even if we are positioned there already.
                        */
 
@@ -843,8 +859,20 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
        outbound_mtc_timecode_frame = _transport_frame;
        next_quarter_frame_to_send = 0;
 
+       /* do "stopped" stuff if:
+        *
+        * we are rolling AND
+        *    no autoplay in effect AND
+         *       we're not going to keep rolling after the locate AND
+         *           !(playing a loop with JACK sync)
+         *
+        */
+
        if (transport_rolling() && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) {
                realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
+       } else {
+               /* otherwise tell the world that we located */
+               realtime_locate ();
        }
 
        if (force || !with_loop || loop_changing) {
@@ -853,7 +881,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
 
                if (with_roll) {
                        todo = PostTransportWork (todo | PostTransportRoll);
-               } 
+               }
 
                add_post_transport_work (todo);
                _butler->schedule_transport_work ();
@@ -930,12 +958,18 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
 void
 Session::set_transport_speed (double speed, bool abort, bool clear_state)
 {
-       DEBUG_TRACE (DEBUG::Transport, string_compose ("Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n", speed, abort, clear_state, _transport_speed));
+       DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n", 
+                                                      speed, abort, clear_state, _transport_speed, _transport_frame));
 
        if (_transport_speed == speed) {
                return;
        }
 
+       if (actively_recording() && speed != 1.0 && speed != 0.0) {
+               /* no varispeed during recording */
+               return;
+       }
+
        _target_transport_speed = fabs(speed);
 
        /* 8.0 max speed is somewhat arbitrary but based on guestimates regarding disk i/o capability
@@ -958,7 +992,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state)
 
                if (synced_to_jack ()) {
                        if (clear_state) {
-                               /* do this here because our response to the slave won't 
+                               /* do this here because our response to the slave won't
                                   take care of it.
                                */
                                _play_range = false;
@@ -968,7 +1002,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state)
                } else {
                        stop_transport (abort);
                }
-               
+
                unset_play_loop ();
 
        } else if (transport_stopped() && speed == 1.0) {
@@ -987,6 +1021,8 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state)
 
        } else {
 
+               /* not zero, not 1.0 ... varispeed */
+
                if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) {
                        warning << string_compose (
                                _("Global varispeed cannot be supported while %1 is connected to JACK transport control"),
@@ -1012,7 +1048,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state)
                /* if we are reversing relative to the current speed, or relative to the speed
                   before the last stop, then we have to do extra work.
                */
-               
+
                PostTransportWork todo = PostTransportWork (0);
 
                if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0f && speed < 0.0f)) {
@@ -1035,6 +1071,8 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state)
                        add_post_transport_work (todo);
                        _butler->schedule_transport_work ();
                }
+
+               TransportStateChange (); /* EMIT SIGNAL */
        }
 }
 
@@ -1065,6 +1103,11 @@ Session::stop_transport (bool abort, bool clear_state)
                   and then we'll really be stopped.
                */
 
+               DEBUG_TRACE (DEBUG::Transport, string_compose ("stop transport requested @ %1, scheduled for + %2 - %3 = %4, abort = %5\n",
+                                                              _transport_frame, _worst_input_latency, current_block_size,
+                                                              _transport_frame - _worst_input_latency - current_block_size,
+                                                              abort));
+
                SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace,
                                                     _transport_frame + _worst_input_latency - current_block_size,
                                                     0, 0, abort);
@@ -1101,9 +1144,11 @@ Session::stop_transport (bool abort, bool clear_state)
 void
 Session::start_transport ()
 {
+       DEBUG_TRACE (DEBUG::Transport, "start_transport\n");
+
        _last_roll_location = _transport_frame;
        _last_roll_or_reversal_location = _transport_frame;
-       
+
        have_looped = false;
 
        /* if record status is Enabled, move it to Recording. if its
@@ -1220,6 +1265,8 @@ Session::use_sync_source (Slave* new_slave)
        delete _slave;
        _slave = new_slave;
 
+       send_full_time_code (_transport_frame);
+
        boost::shared_ptr<RouteList> rl = routes.reader();
        for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
@@ -1275,7 +1322,7 @@ Session::switch_to_sync_source (SyncSource src)
                try {
                        new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24);
                }
-               
+
                catch (failed_constructor& err) {
                        return;
                }
@@ -1292,7 +1339,7 @@ Session::switch_to_sync_source (SyncSource src)
 
                new_slave = new JACK_Slave (_engine.jack());
                break;
-               
+
        default:
                new_slave = 0;
                break;
@@ -1334,7 +1381,7 @@ Session::set_play_range (list<AudioRange>& range, bool leave_rolling)
        /* Called from event-processing context */
 
        unset_play_range ();
-       
+
        if (range.empty()) {
                /* _play_range set to false in unset_play_range()
                 */
@@ -1352,45 +1399,45 @@ Session::set_play_range (list<AudioRange>& range, bool leave_rolling)
        unset_play_loop ();
 
        list<AudioRange>::size_type sz = range.size();
-       
+
        if (sz > 1) {
-               
-               list<AudioRange>::iterator i = range.begin(); 
+
+               list<AudioRange>::iterator i = range.begin();
                list<AudioRange>::iterator next;
-               
+
                while (i != range.end()) {
-                       
+
                        next = i;
                        ++next;
-                       
+
                        /* locating/stopping is subject to delays for declicking.
                         */
-                       
+
                        framepos_t requested_frame = i->end;
-                       
+
                        if (requested_frame > current_block_size) {
                                requested_frame -= current_block_size;
                        } else {
                                requested_frame = 0;
                        }
-                       
+
                        if (next == range.end()) {
                                ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, requested_frame, 0, 0.0f);
                        } else {
                                ev = new SessionEvent (SessionEvent::RangeLocate, SessionEvent::Add, requested_frame, (*next).start, 0.0f);
                        }
-                       
+
                        merge_event (ev);
-                       
+
                        i = next;
                }
-               
+
        } else if (sz == 1) {
 
                ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, range.front().end, 0, 0.0f);
                merge_event (ev);
-               
-       } 
+
+       }
 
        /* save range so we can do auto-return etc. */
 
@@ -1400,7 +1447,7 @@ Session::set_play_range (list<AudioRange>& range, bool leave_rolling)
 
        ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false);
        merge_event (ev);
-       
+
        TransportStateChange ();
 }