Fix off-by-one in body_range().
[ardour.git] / libs / ardour / session_transport.cc
index 48b84c492c84ff50ffc1ecc65f3d8538a58cfdc5..5fef402d9fc9597a50f0691d1a1a9e62652da98c 100644 (file)
 
 */
 
-#include <cmath>
-#include <cerrno>
-#include <unistd.h>
-
 #ifdef WAF_BUILD
 #include "libardour-config.h"
 #endif
 
+#include <cmath>
+#include <cerrno>
+#include <unistd.h>
 
 #include "pbd/undo.h"
 #include "pbd/error.h"
 #include "midi++/port.h"
 #include "midi++/manager.h"
 
-#include "ardour/ardour.h"
 #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 +195,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 */
@@ -425,6 +424,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 ();
 }
 
 
@@ -458,7 +463,6 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
                auditioner->cancel_audition ();
        }
 
-       clear_clicks();
        cumulative_rf_motion = 0;
        reset_rf_scale (0);
 
@@ -483,7 +487,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 ();
@@ -528,7 +532,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;
@@ -544,7 +548,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 {
@@ -563,24 +567,26 @@ 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);
                        }
-               } 
+               }
 
        }
 
+       clear_clicks();
+
        /* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode.
        */
 
@@ -608,11 +614,13 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
 
        have_looped = false;
 
-       send_full_time_code (_transport_frame);
-
-       if (!dynamic_cast<MTC_Slave*>(_slave)) {
-               MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
-               send_mmc_locate (_transport_frame);
+       if (!_engine.freewheeling()) {
+               send_full_time_code (_transport_frame);
+               
+               if (!dynamic_cast<MTC_Slave*>(_slave)) {
+                       MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+                       send_mmc_locate (_transport_frame);
+               }
        }
 
        if ((ptw & PostTransportLocate) && get_record_enabled()) {
@@ -676,7 +684,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) {
@@ -698,7 +706,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"
@@ -706,7 +714,7 @@ Session::set_play_loop (bool yn)
                        << endmsg;
                return;
        }
-       
+
        if (yn) {
 
                play_loop = true;
@@ -735,13 +743,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.
                        */
 
@@ -860,7 +868,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
          *           !(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 {
@@ -874,7 +882,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 ();
@@ -951,12 +959,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
@@ -979,7 +993,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;
@@ -989,7 +1003,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) {
@@ -1007,7 +1021,7 @@ 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) {
@@ -1035,7 +1049,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)) {
@@ -1058,7 +1072,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state)
                        add_post_transport_work (todo);
                        _butler->schedule_transport_work ();
                }
-               
+
                TransportStateChange (); /* EMIT SIGNAL */
        }
 }
@@ -1090,6 +1104,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);
@@ -1130,7 +1149,7 @@ Session::start_transport ()
 
        _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
@@ -1168,10 +1187,12 @@ Session::start_transport ()
                (*i)->automation_snapshot (_transport_frame, true);
        }
 
-       Timecode::Time time;
-       timecode_time_subframes (_transport_frame, time);
-       if (!dynamic_cast<MTC_Slave*>(_slave)) {
-               MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+       if (!_engine.freewheeling()) {
+               Timecode::Time time;
+               timecode_time_subframes (_transport_frame, time);
+               if (!dynamic_cast<MTC_Slave*>(_slave)) {
+                       MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+               }
        }
 
        TransportStateChange (); /* EMIT SIGNAL */
@@ -1247,6 +1268,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);
@@ -1302,7 +1325,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;
                }
@@ -1319,7 +1342,7 @@ Session::switch_to_sync_source (SyncSource src)
 
                new_slave = new JACK_Slave (_engine.jack());
                break;
-               
+
        default:
                new_slave = 0;
                break;
@@ -1328,13 +1351,6 @@ Session::switch_to_sync_source (SyncSource src)
        request_sync_source (new_slave);
 }
 
-void
-Session::reverse_track_buffers ()
-{
-       add_post_transport_work (PostTransportReverse);
-       _butler->schedule_transport_work ();
-}
-
 void
 Session::set_track_speed (Track* track, double speed)
 {
@@ -1361,7 +1377,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()
                 */
@@ -1379,45 +1395,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. */
 
@@ -1427,7 +1443,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 ();
 }
 
@@ -1490,6 +1506,10 @@ Session::xrun_recovery ()
 void
 Session::route_processors_changed (RouteProcessorChange c)
 {
+       if (ignore_route_processor_changes) {
+               return;
+       }
+
        if (c.type == RouteProcessorChange::MeterPointChange) {
                return;
        }
@@ -1533,9 +1553,11 @@ Session::maybe_stop (framepos_t limit)
 void
 Session::send_mmc_locate (framepos_t t)
 {
-       Timecode::Time time;
-       timecode_time_subframes (t, time);
-       MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+       if (!_engine.freewheeling()) {
+               Timecode::Time time;
+               timecode_time_subframes (t, time);
+               MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+       }
 }
 
 /** Ask the transport to not send timecode until further notice.  The suspension