Declick before the end of seamless loops, not after the end, so that loops are render...
authorCarl Hetherington <carl@carlh.net>
Wed, 20 Jun 2012 18:46:05 +0000 (18:46 +0000)
committerCarl Hetherington <carl@carlh.net>
Wed, 20 Jun 2012 18:46:05 +0000 (18:46 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@12801 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/session.h
libs/ardour/ardour/session_event.h
libs/ardour/session.cc
libs/ardour/session_events.cc
libs/ardour/session_process.cc
libs/ardour/session_transport.cc

index afb1c37d54cd55c4bc7518a4ba1223cc2250fa59..e75c7aa2062b96bef61091a1cb4c19a0964507e3 100644 (file)
@@ -871,10 +871,12 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
        void destroy ();
 
        enum SubState {
-               PendingDeclickIn   = 0x1,
-               PendingDeclickOut  = 0x2,
-               StopPendingCapture = 0x4,
-               PendingLocate      = 0x20,
+               PendingDeclickIn      = 0x1,  ///< pending de-click fade-in for start
+               PendingDeclickOut     = 0x2,  ///< pending de-click fade-out for stop
+               StopPendingCapture    = 0x4,
+               PendingLoopDeclickIn  = 0x8,  ///< pending de-click fade-in at the start of a loop
+               PendingLoopDeclickOut = 0x10, ///< pending de-click fade-out at the end of a loop
+               PendingLocate         = 0x20,
        };
 
        /* stuff used in process() should be close together to
@@ -999,7 +1001,16 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
                        transport_sub_state &= ~PendingDeclickIn;
                        return 1;
                } else if (transport_sub_state & PendingDeclickOut) {
+                       /* XXX: not entirely sure why we don't clear this */
                        return -1;
+               } else if (transport_sub_state & PendingLoopDeclickOut) {
+                       /* Return the declick out first ... */
+                       transport_sub_state &= ~PendingLoopDeclickOut;
+                       return -1;
+               } else if (transport_sub_state & PendingLoopDeclickIn) {
+                       /* ... then the declick in on the next call */
+                       transport_sub_state &= ~PendingLoopDeclickIn;
+                       return 1;
                } else {
                        return 0;
                }
@@ -1089,6 +1100,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
 
        PBD::ScopedConnectionList loop_connections;
        void             auto_loop_changed (Location *);
+       void             auto_loop_declick_range (Location *, framepos_t &, framepos_t &);
 
        void first_stage_init (std::string path, std::string snapshot_name);
        int  second_stage_init ();
index 932ddc917650040ab5958c53146f90e16b237db4..d5a1b0c25553942d41b94977c7c27a4d822e1f9b 100644 (file)
@@ -42,7 +42,8 @@ public:
                /* only one of each of these events can be queued at any one time */
 
                StopOnce,
-               AutoLoop
+               AutoLoop,
+               AutoLoopDeclick,
        };
 
        enum Action {
index 72c2a8654f9dfd86004caf8dac8fe837606747ba..9eb4c223d06b144243da380b902b3187f1e0489e 100644 (file)
@@ -929,10 +929,25 @@ Session::auto_punch_changed (Location* location)
        replace_event (SessionEvent::PunchOut, when_to_stop);
 }
 
+/** @param loc A loop location.
+ *  @param pos Filled in with the start time of the required fade-out (in session frames).
+ *  @param length Filled in with the length of the required fade-out.
+ */
+void
+Session::auto_loop_declick_range (Location* loc, framepos_t & pos, framepos_t & length)
+{
+       pos = max (loc->start(), loc->end() - 64);
+       length = loc->end() - pos;
+}
+
 void
 Session::auto_loop_changed (Location* location)
 {
        replace_event (SessionEvent::AutoLoop, location->end(), location->start());
+       framepos_t dcp;
+       framecnt_t dcl;
+       auto_loop_declick_range (location, dcp, dcl);
+       replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl);
 
        if (transport_rolling() && play_loop) {
 
@@ -1010,6 +1025,10 @@ Session::set_auto_loop_location (Location* location)
                loop_connections.drop_connections ();
                existing->set_auto_loop (false, this);
                remove_event (existing->end(), SessionEvent::AutoLoop);
+               framepos_t dcp;
+               framecnt_t dcl;
+               auto_loop_declick_range (existing, dcp, dcl);
+               remove_event (dcp, SessionEvent::AutoLoopDeclick);
                auto_loop_location_changed (0);
        }
 
index 84b1b75b125e6a6002a8311316975811bac126ab..6c828ac6f0d490f0217c69171aa02d833974595e 100644 (file)
@@ -173,6 +173,7 @@ SessionEventManager::merge_event (SessionEvent* ev)
 
        switch (ev->type) {
        case SessionEvent::AutoLoop:
+       case SessionEvent::AutoLoopDeclick:
        case SessionEvent::StopOnce:
                _clear_event_type (ev->type);
                break;
index f92ca9f1a7c36995fbaa57494a6e57a0bf3cd3a9..3936423b141a98e46ffc0cb09504e5bbe0bfd894 100644 (file)
@@ -1021,6 +1021,17 @@ Session::process_event (SessionEvent* ev)
                del = false;
                break;
 
+       case SessionEvent::AutoLoopDeclick:
+               if (play_loop) {
+                       /* Request a declick fade-out and a fade-in; the fade-out will happen
+                          at the end of the loop, and the fade-in at the start.
+                       */
+                       transport_sub_state |= (PendingLoopDeclickOut | PendingLoopDeclickIn);
+               }
+               remove = false;
+               del = false;
+               break;
+
        case SessionEvent::Locate:
                if (ev->yes_or_no) {
                        // cerr << "forced locate to " << ev->target_frame << endl;
index 7c23e2aee796d87f5b02c5c50879304762c459dd..ace5e8e3e1ff41785b4a386a0d28528645b988eb 100644 (file)
@@ -662,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.
        */
@@ -676,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;
        }
 }
 
@@ -684,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 ();
@@ -744,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.
@@ -841,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);