Skip to content

Commit

Permalink
Merge pull request #7 from daschuer/beatloop_size
Browse files Browse the repository at this point in the history
Removing Random jump into Loop when qantisation is enabeled.
  • Loading branch information
Be-ing authored May 20, 2017
2 parents 9bd5ff8 + 50546a4 commit 0dbdf38
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 54 deletions.
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) {
setLoopingEnabled(false);
}
}
Expand Down

0 comments on commit 0dbdf38

Please sign in to comment.