new transport option, "loop-is-mode" which optionally changes the role of the "play...
[ardour.git] / libs / ardour / session_transport.cc
index ab6d68fd674138bb529a6a15bb01d7351a1b8e94..ea9f5d28dd6ee97e2beebb8e8cf65f8a95920c43 100644 (file)
@@ -30,6 +30,7 @@
 #include "pbd/enumwriter.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
 
 #include "midi++/mmc.h"
 #include "midi++/port.h"
@@ -40,6 +41,7 @@
 #include "ardour/click.h"
 #include "ardour/debug.h"
 #include "ardour/location.h"
+#include "ardour/profile.h"
 #include "ardour/session.h"
 #include "ardour/slave.h"
 #include "ardour/operations.h"
@@ -86,7 +88,7 @@ Session::request_sync_source (Slave* new_slave)
 
        seamless = Config->get_seamless_loop ();
 
-       if (dynamic_cast<JACK_Slave*>(new_slave)) {
+       if (dynamic_cast<Engine_Slave*>(new_slave)) {
                /* JACK cannot support seamless looping at present */
                Config->set_seamless_loop (false);
        } else {
@@ -158,10 +160,11 @@ Session::force_locate (framepos_t target_frame, bool with_roll)
 }
 
 void
-Session::request_play_loop (bool yn, bool leave_rolling)
+Session::request_play_loop (bool yn, bool change_transport_roll)
 {
        SessionEvent* ev;
        Location *location = _locations->auto_loop_location();
+       double target_speed;
 
        if (location == 0 && yn) {
                error << _("Cannot loop - no loop range defined")
@@ -169,14 +172,44 @@ Session::request_play_loop (bool yn, bool leave_rolling)
                return;
        }
 
-       ev = new SessionEvent (SessionEvent::SetLoop, SessionEvent::Add, SessionEvent::Immediate, 0, (leave_rolling ? 1.0 : 0.0), yn);
-       DEBUG_TRACE (DEBUG::Transport, string_compose ("Request set loop = %1, leave rolling ? %2\n", yn, leave_rolling));
+       if (change_transport_roll) {
+               if (transport_rolling()) {
+                       /* start looping at current speed */
+                       target_speed = transport_speed ();
+               } else {
+                       /* currently stopped */
+                       if (yn) {
+                               /* start looping at normal speed */
+                               target_speed = 1.0;
+                       } else {
+                               target_speed = 0.0;
+                       }
+               }
+       } else {
+               /* leave the speed alone */
+               target_speed = transport_speed ();
+       }
+
+       ev = new SessionEvent (SessionEvent::SetLoop, SessionEvent::Add, SessionEvent::Immediate, 0, target_speed, yn);
+       DEBUG_TRACE (DEBUG::Transport, string_compose ("Request set loop = %1, change roll state ? %2\n", yn, change_transport_roll));
        queue_event (ev);
 
-       if (!leave_rolling && !yn && Config->get_seamless_loop() && transport_rolling()) {
-               // request an immediate locate to refresh the tracks
-               // after disabling looping
-               request_locate (_transport_frame-1, false);
+       if (yn) {
+               if (!change_transport_roll) {
+                       if (!transport_rolling()) {
+                               /* we're not changing transport state, but we do want
+                                  to set up position for the new loop. Don't
+                                  do this if we're rolling already.
+                               */
+                               request_locate (location->start(), false);
+                       }
+               }
+       } else {
+               if (!change_transport_roll && Config->get_seamless_loop() && transport_rolling()) {
+                       // request an immediate locate to refresh the tracks
+                       // after disabling looping
+                       request_locate (_transport_frame-1, false);
+               }
        }
 }
 
@@ -514,13 +547,13 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
        if (auto_return_enabled ||
            (ptw & PostTransportLocate) ||
            (_requested_return_frame >= 0) ||
-           synced_to_jack()) {
+           synced_to_engine()) {
 
                if (pending_locate_flush) {
                        flush_all_inserts ();
                }
 
-               if ((auto_return_enabled || synced_to_jack() || _requested_return_frame >= 0) &&
+               if ((auto_return_enabled || synced_to_engine() || _requested_return_frame >= 0) &&
                    !(ptw & PostTransportLocate)) {
 
                        /* no explicit locate queued */
@@ -531,8 +564,6 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
 
                                /* 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;
                                do_locate = true;
 
@@ -543,7 +574,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
 
                                                /* don't try to handle loop play when synced to JACK */
 
-                                               if (!synced_to_jack()) {
+                                               if (!synced_to_engine()) {
 
                                                        Location *location = _locations->auto_loop_location();
 
@@ -589,8 +620,10 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
        */
 
        if (ptw & PostTransportClearSubstate) {
-               _play_range = false;
-               unset_play_loop ();
+               unset_play_range ();
+               if (!Config->get_loop_is_mode()) {
+                       unset_play_loop ();
+               }
        }
 
        /* this for() block can be put inside the previous if() and has the effect of ... ??? what */
@@ -657,8 +690,10 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
        }
 
        if (ptw & PostTransportStop) {
-               _play_range = false;
-               play_loop = false;
+               unset_play_range ();
+               if (!Config->get_loop_is_mode()) {
+                       unset_play_loop ();
+               }
        }
 
        PositionChanged (_transport_frame); /* EMIT SIGNAL */
@@ -723,7 +758,7 @@ Session::unset_play_loop ()
 }
 
 void
-Session::set_play_loop (bool yn)
+Session::set_play_loop (bool yn, double speed)
 {
        /* Called from event-handling context */
 
@@ -734,7 +769,7 @@ Session::set_play_loop (bool yn)
                return;
        }
 
-       if (yn && Config->get_seamless_loop() && synced_to_jack()) {
+       if (yn && Config->get_seamless_loop() && synced_to_engine()) {
                warning << string_compose (
                        _("Seamless looping cannot be supported while %1 is using JACK transport.\n"
                          "Recommend changing the configured options"), PROGRAM_NAME)
@@ -782,12 +817,24 @@ Session::set_play_loop (bool yn)
                        merge_event (new SessionEvent (SessionEvent::AutoLoopDeclick, SessionEvent::Replace, dcp, dcl, 0.0f));
                        merge_event (new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f));
 
-                       /* locate to start of loop and roll. 
+                       /* if requested to roll, locate to start of loop and
+                        * roll but ONLY if we're not already rolling.
 
                           args: positition, roll=true, flush=true, with_loop=false, force buffer refill if seamless looping
                        */
 
-                       start_locate (loc->start(), true, true, false, Config->get_seamless_loop());
+                       if (Config->get_loop_is_mode()) {
+                               /* loop IS a transport mode: if already
+                                  rolling, do not locate to loop start.
+                               */
+                               if (!transport_rolling() && (speed != 0.0)) {
+                                       start_locate (loc->start(), true, true, false, Config->get_seamless_loop());
+                               }
+                       } else {
+                               if (speed != 0.0) {
+                                       start_locate (loc->start(), true, true, false, Config->get_seamless_loop());
+                               }
+                       }
                }
 
        } else {
@@ -811,7 +858,7 @@ Session::flush_all_inserts ()
 void
 Session::start_locate (framepos_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force)
 {
-       if (synced_to_jack()) {
+       if (synced_to_engine()) {
 
                double sp;
                framepos_t pos;
@@ -926,7 +973,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
 
        bool transport_was_stopped = !transport_rolling();
 
-       if (transport_was_stopped && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) {
+       if (transport_was_stopped && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_engine() && play_loop)) {
                realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
                transport_was_stopped = true;
        } else {
@@ -983,7 +1030,9 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
 
                                // located outside the loop: cancel looping directly, this is called from event handling context
 
-                               set_play_loop (false);
+                               if (!Config->get_loop_is_mode()) {
+                                       set_play_loop (false, _transport_speed);
+                               }
                                
                        } else if (_transport_frame == al->start()) {
 
@@ -1067,7 +1116,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
                        set_track_monitor_input_status (true);
                }
 
-               if (synced_to_jack ()) {
+               if (synced_to_engine ()) {
                        if (clear_state) {
                                /* do this here because our response to the slave won't
                                   take care of it.
@@ -1080,17 +1129,32 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
                        stop_transport (abort);
                }
 
-               unset_play_loop ();
+               if (!Config->get_loop_is_mode()) {
+                       unset_play_loop ();
+               }
 
        } else if (transport_stopped() && speed == 1.0) {
 
                /* we are stopped and we want to start rolling at speed 1 */
 
+               if (Config->get_loop_is_mode() && play_loop) {
+
+                       Location *location = _locations->auto_loop_location();
+                       
+                       if (location != 0) {
+                               if (_transport_frame != location->start()) {
+                                       /* jump to start and then roll from there */
+                                       request_locate (location->start(), true);
+                                       return;
+                               }
+                       }
+               }
+
                if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
                        set_track_monitor_input_status (false);
                }
 
-               if (synced_to_jack()) {
+               if (synced_to_engine()) {
                        _engine.transport_start ();
                } else {
                        start_transport ();
@@ -1100,7 +1164,7 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state, bool a
 
                /* not zero, not 1.0 ... varispeed */
 
-               if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) {
+               if ((synced_to_engine()) && speed != 0.0 && speed != 1.0) {
                        warning << string_compose (
                                _("Global varispeed cannot be supported while %1 is connected to JACK transport control"),
                                PROGRAM_NAME)
@@ -1429,8 +1493,8 @@ Session::switch_to_sync_source (SyncSource src)
                }
                break;
 
-       case JACK:
-               if (_slave && dynamic_cast<JACK_Slave*>(_slave)) {
+       case Engine:
+               if (_slave && dynamic_cast<Engine_Slave*>(_slave)) {
                        return;
                }
 
@@ -1438,7 +1502,7 @@ Session::switch_to_sync_source (SyncSource src)
                        return;
                }
 
-               new_slave = new JACK_Slave (*AudioEngine::instance());
+               new_slave = new Engine_Slave (*AudioEngine::instance());
                break;
 
        default:
@@ -1632,9 +1696,9 @@ bool
 Session::maybe_stop (framepos_t limit)
 {
        if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) {
-               if (synced_to_jack () && config.get_jack_time_master ()) {
+               if (synced_to_engine () && config.get_jack_time_master ()) {
                        _engine.transport_stop ();
-               } else if (!synced_to_jack ()) {
+               } else if (!synced_to_engine ()) {
                        stop_transport ();
                }
                return true;