Declick before the end of seamless loops, not after the end, so that loops are render...
[ardour.git] / libs / ardour / session_transport.cc
index 5fc073d93eb3a9306029ad7309211e9fbbc45824..ace5e8e3e1ff41785b4a386a0d28528645b988eb 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"
@@ -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);
 
@@ -581,6 +585,8 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
 
        }
 
+       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()) {
@@ -654,7 +662,7 @@ Session::check_declick_out ()
 
        /* this is called after a process() iteration. if PendingDeclickOut was set,
           it means that we were waiting to declick the output (which has just been
-          done) before doing something else. this is where we do that "something else".
+          done) before maybe doing something else. this is where we do that "something else".
 
           note: called from the audio thread.
        */
@@ -668,6 +676,10 @@ Session::check_declick_out ()
                        stop_transport (pending_abort);
                        transport_sub_state &= ~(PendingDeclickOut|PendingLocate);
                }
+
+       } else if (transport_sub_state & PendingLoopDeclickOut) {
+               /* Nothing else to do here; we've declicked, and the loop event will be along shortly */
+               transport_sub_state &= ~PendingLoopDeclickOut;
        }
 }
 
@@ -676,6 +688,7 @@ Session::unset_play_loop ()
 {
        play_loop = false;
        clear_events (SessionEvent::AutoLoop);
+       clear_events (SessionEvent::AutoLoopDeclick);
 
        // set all tracks to NOT use internal looping
        boost::shared_ptr<RouteList> rl = routes.reader ();
@@ -736,10 +749,16 @@ Session::set_play_loop (bool yn)
                                }
                        }
 
-                       /* put the loop event into the event list */
+                       /* Put the delick and loop events in into the event list.  The declick event will
+                          cause a de-clicking fade-out just before the end of the loop, and it will also result
+                          in a fade-in when the loop restarts.  The AutoLoop event will peform the actual loop.
+                       */
 
-                       SessionEvent* event = new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f);
-                       merge_event (event);
+                       framepos_t dcp;
+                       framecnt_t dcl;
+                       auto_loop_declick_range (loc, dcp, dcl);
+                       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 doing seamless loop, force a
                           locate+buffer refill even if we are positioned there already.
@@ -833,8 +852,11 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool
                return;
        }
 
-       if (_transport_speed) {
-               /* schedule a declick. we'll be called again when its done */
+       if (_transport_speed && !with_loop) {
+               /* Schedule a declick.  We'll be called again when its done.
+                  We only do it this way for ordinary locates, not those
+                  due to loops.
+               */
 
                if (!(transport_sub_state & PendingDeclickOut)) {
                        transport_sub_state |= (PendingDeclickOut|PendingLocate);
@@ -958,6 +980,11 @@ Session::set_transport_speed (double speed, bool abort, bool clear_state)
                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
@@ -1174,10 +1201,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 */
@@ -1336,13 +1365,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)
 {
@@ -1468,9 +1490,11 @@ Session::engine_halted ()
           the picture.
        */
 
-       g_atomic_int_set (&_butler->should_do_transport_work, 0);
-       set_post_transport_work (PostTransportWork (0));
-       _butler->stop ();
+       if (_butler) {
+               g_atomic_int_set (&_butler->should_do_transport_work, 0);
+               set_post_transport_work (PostTransportWork (0));
+               _butler->stop ();
+       }
 
        realtime_stop (false, true);
        non_realtime_stop (false, 0, ignored);
@@ -1498,6 +1522,10 @@ Session::xrun_recovery ()
 void
 Session::route_processors_changed (RouteProcessorChange c)
 {
+       if (ignore_route_processor_changes) {
+               return;
+       }
+
        if (c.type == RouteProcessorChange::MeterPointChange) {
                return;
        }
@@ -1541,9 +1569,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