From 7a76e8ae96c20b270dd3104328f8bee199c9a770 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 20 Jun 2012 18:46:05 +0000 Subject: Declick before the end of seamless loops, not after the end, so that loops are rendered accurately (#4213, #4593). git-svn-id: svn://localhost/ardour2/branches/3.0@12801 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/session.h | 20 ++++++++++++++++---- libs/ardour/ardour/session_event.h | 3 ++- libs/ardour/session.cc | 19 +++++++++++++++++++ libs/ardour/session_events.cc | 1 + libs/ardour/session_process.cc | 11 +++++++++++ libs/ardour/session_transport.cc | 26 ++++++++++++++++++++------ 6 files changed, 69 insertions(+), 11 deletions(-) (limited to 'libs/ardour') diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index afb1c37d54..e75c7aa206 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -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 (); diff --git a/libs/ardour/ardour/session_event.h b/libs/ardour/ardour/session_event.h index 932ddc9176..d5a1b0c255 100644 --- a/libs/ardour/ardour/session_event.h +++ b/libs/ardour/ardour/session_event.h @@ -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 { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 72c2a8654f..9eb4c223d0 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -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); } diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc index 84b1b75b12..6c828ac6f0 100644 --- a/libs/ardour/session_events.cc +++ b/libs/ardour/session_events.cc @@ -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; diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index f92ca9f1a7..3936423b14 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -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; diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 7c23e2aee7..ace5e8e3e1 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -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 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); -- cgit v1.2.3