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
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;
}
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 ();
/* only one of each of these events can be queued at any one time */
StopOnce,
- AutoLoop
+ AutoLoop,
+ AutoLoopDeclick,
};
enum Action {
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) {
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);
}
switch (ev->type) {
case SessionEvent::AutoLoop:
+ case SessionEvent::AutoLoopDeclick:
case SessionEvent::StopOnce:
_clear_event_type (ev->type);
break;
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;
/* 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.
*/
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;
}
}
{
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 ();
}
}
- /* 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.
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);