Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 55 additions & 51 deletions src/engine/bpmcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ double BpmControl::calcSyncedRate(double userTweak) {

// Now that we have our beat distance we can also check how large the
// current loop is. If we are in a <1 beat loop, don't worry about offset.
const bool loop_enabled = m_pLoopEnabled->get() > 0.0;
const bool loop_enabled = m_pLoopEnabled->toBool();
const double loop_size = (m_pLoopEndPosition->get() -
m_pLoopStartPosition->get()) /
dBeatLength;
Expand Down Expand Up @@ -567,14 +567,14 @@ bool BpmControl::getBeatContextNoLookup(
return true;
}

double BpmControl::getPhaseOffset(double dThisPosition) {
double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectLoops) {
// Without a beatgrid, we don't know the phase offset.
if (!m_pBeats) {
return 0;
return dThisPosition;
}
// Master buffer is always in sync!
if (getSyncMode() == SYNC_MASTER) {
return 0;
return dThisPosition;
}

// Get the current position of this deck.
Expand All @@ -588,13 +588,13 @@ double BpmControl::getPhaseOffset(double dThisPosition) {
if (!getBeatContext(m_pBeats, dThisPosition,
&dThisPrevBeat, &dThisNextBeat,
&dThisBeatLength, NULL)) {
return 0;
return dThisPosition;
}
} else {
if (!getBeatContextNoLookup(dThisPosition,
dThisPrevBeat, dThisNextBeat,
&dThisBeatLength, NULL)) {
return 0;
return dThisPosition;
}
}

Expand All @@ -606,15 +606,15 @@ double BpmControl::getPhaseOffset(double dThisPosition) {
// If not, we have to figure it out
EngineBuffer* pOtherEngineBuffer = pickSyncTarget();
if (pOtherEngineBuffer == NULL) {
return 0;
return dThisPosition;
}

TrackPointer otherTrack = pOtherEngineBuffer->getLoadedTrack();
BeatsPointer otherBeats = otherTrack ? otherTrack->getBeats() : BeatsPointer();

// If either track does not have beats, then we can't adjust the phase.
if (!otherBeats) {
return 0;
return dThisPosition;
}

double dOtherLength = ControlObject::getControl(
Expand All @@ -624,7 +624,7 @@ double BpmControl::getPhaseOffset(double dThisPosition) {

if (!BpmControl::getBeatContext(otherBeats, dOtherPosition,
NULL, NULL, NULL, &dOtherBeatFraction)) {
return 0.0;
return dThisPosition;
}
}

Expand Down Expand Up @@ -658,51 +658,55 @@ double BpmControl::getPhaseOffset(double dThisPosition) {
dNewPlaypos += dThisPrevBeat;
}

// We might be seeking outside the loop.
const bool loop_enabled = m_pLoopEnabled->get() > 0.0;
const double loop_start_position = m_pLoopStartPosition->get();
const double loop_end_position = m_pLoopEndPosition->get();

// Cases for sanity:
//
// CASE 1
// Two identical 1-beat loops, out of phase by X samples.
// Other deck is at its loop start.
// This deck is half way through. We want to jump forward X samples to the loop end point.
//
// Two identical 1-beat loop, out of phase by X samples.
// Other deck is

// If sync target is 50% through the beat,
// If we are at the loop end point and hit sync, jump forward X samples.


// TODO(rryan): Revise this with something that keeps a broader number of
// cases in sync. This at least prevents breaking out of the loop.
if (loop_enabled) {
const double loop_length = loop_end_position - loop_start_position;
if (loop_length <= 0.0) {
return false;
}

// TODO(rryan): If loop_length is not a multiple of dThisBeatLength should
// we bail and not sync phase?

// Syncing to after the loop end.
double end_delta = dNewPlaypos - loop_end_position;
if (end_delta > 0) {
int i = end_delta / loop_length;
dNewPlaypos = loop_start_position + end_delta - i * loop_length;
}

// Syncing to before the loop beginning.
double start_delta = loop_start_position - dNewPlaypos;
if (start_delta > 0) {
int i = start_delta / loop_length;
dNewPlaypos = loop_end_position - start_delta + i * loop_length;
if (respectLoops) {
// We might be seeking outside the loop.
const bool loop_enabled = m_pLoopEnabled->toBool();
const double loop_start_position = m_pLoopStartPosition->get();
const double loop_end_position = m_pLoopEndPosition->get();

// Cases for sanity:
//
// CASE 1
// Two identical 1-beat loops, out of phase by X samples.
// Other deck is at its loop start.
// This deck is half way through. We want to jump forward X samples to the loop end point.
//
// Two identical 1-beat loop, out of phase by X samples.
// Other deck is

// If sync target is 50% through the beat,
// If we are at the loop end point and hit sync, jump forward X samples.


// TODO(rryan): Revise this with something that keeps a broader number of
// cases in sync. This at least prevents breaking out of the loop.
if (loop_enabled &&
dThisPosition <= loop_end_position) {
const double loop_length = loop_end_position - loop_start_position;
const double end_delta = dNewPlaypos - loop_end_position;

// Syncing to after the loop end.
if (end_delta > 0 && loop_length > 0.0) {
int i = end_delta / loop_length;
dNewPlaypos = loop_start_position + end_delta - i * loop_length;

// Move new position after loop jump into phase as well.
// This is a recursive call, called only twice because of
// respectLoops = false
dNewPlaypos = getNearestPositionInPhase(dNewPlaypos, false);
}

// Note: Syncing to before the loop beginning is allowed, because
// loops are catching
}
}

return dNewPlaypos;
}

double BpmControl::getPhaseOffset(double dThisPosition) {
// This does not respect looping
double dNewPlaypos = getNearestPositionInPhase(dThisPosition, false);
return dNewPlaypos - dThisPosition;
}

Expand Down
3 changes: 2 additions & 1 deletion src/engine/bpmcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class BpmControl : public EngineControl {
// out of sync.
double calcSyncedRate(double userTweak);
// Get the phase offset from the specified position.
double getPhaseOffset(double reference_position);
double getNearestPositionInPhase(double dThisPosition, bool respectLoops = true);
double getPhaseOffset(double dThisPosition);
double getBeatDistance(double dThisPosition) const;
double getPreviousSample() const { return m_dPreviousSample; }

Expand Down
2 changes: 1 addition & 1 deletion src/engine/enginebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,7 @@ void EngineBuffer::processSeek(bool paused) {
}

if ((seekType & SEEK_PHASE) && !paused && m_pQuantize->toBool()) {
position += m_pBpmControl->getPhaseOffset(position);
position = m_pBpmControl->getNearestPositionInPhase(position);
}

double newPlayFrame = position / kSamplesPerFrame;
Expand Down
4 changes: 3 additions & 1 deletion src/engine/loopingcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,9 @@ void LoopingControl::slotLoopEndPos(double pos) {
void LoopingControl::notifySeek(double dNewPlaypos) {
LoopSamples loopSamples = m_loopSamples.getValue();
if (m_bLoopingEnabled) {
if (dNewPlaypos < loopSamples.start || dNewPlaypos > loopSamples.end) {
// Disable loop when we jump after it, using hot cues or waveform overview
// If we jump before, the loop it is kept enabled as catching loop
if (dNewPlaypos > loopSamples.end) {
Copy link
Copy Markdown
Owner

@Be-ing Be-ing May 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand why LoopingControl::process() doesn't jump to the loop without setting m_bReloopCatchUpcomingLoop to true here, but it works somehow.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, m_bReloopCatchUpcomingLoop is already true because I pressed reloop_toggle.

setLoopingEnabled(false);
}
}
Expand Down