Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable beatloop activation directly after activating a hotcue #2190

Closed
wants to merge 8 commits into from
3 changes: 1 addition & 2 deletions src/engine/controls/enginecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,8 @@ void EngineControl::seek(double sample) {
}
}

void EngineControl::notifySeek(double dNewPlaypos, bool adjustingPhase) {
void EngineControl::notifySeek(double dNewPlaypos) {
Q_UNUSED(dNewPlaypos);
Q_UNUSED(adjustingPhase);
}

EngineBuffer* EngineControl::pickSyncTarget() {
Expand Down
2 changes: 1 addition & 1 deletion src/engine/controls/enginecontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class EngineControl : public QObject {
}

// Called whenever a seek occurs to allow the EngineControl to respond.
virtual void notifySeek(double dNewPlaypo, bool adjustingPhase);
virtual void notifySeek(double dNewPlaypos);
virtual void trackLoaded(TrackPointer pNewTrack);

protected:
Expand Down
70 changes: 63 additions & 7 deletions src/engine/controls/loopingcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
#include <QtDebug>

#include "control/controlobject.h"
#include "preferences/usersettings.h"
#include "control/controlpushbutton.h"
#include "engine/controls/loopingcontrol.h"
#include "engine/controls/bpmcontrol.h"
#include "engine/controls/enginecontrol.h"
#include "engine/controls/loopingcontrol.h"
#include "engine/enginebuffer.h"
#include "preferences/usersettings.h"
#include "util/compatibility.h"
#include "util/math.h"
#include "util/sample.h"
Expand Down Expand Up @@ -768,22 +769,21 @@ void LoopingControl::slotLoopEndPos(double pos) {
}

// This is called from the engine thread
void LoopingControl::notifySeek(double dNewPlaypos, bool adjustingPhase) {
void LoopingControl::notifySeek(double dNewPlaypos) {
LoopSamples loopSamples = m_loopSamples.getValue();
double currentSample = m_currentSample.getValue();
if (m_bLoopingEnabled && !adjustingPhase) {
if (m_bLoopingEnabled) {
// Disable loop when we jumping out, or over a catching loop,
// using hot cues or waveform overview.
// Do not jump out of a loop if we adjust a phase (lp1743010)
if (currentSample >= loopSamples.start &&
currentSample <= loopSamples.end &&
dNewPlaypos < loopSamples.start) {
// jumping out of loop in backwards
setLoopingEnabled(false);
}
if (currentSample <= loopSamples.end &&
dNewPlaypos > loopSamples.end) {
// jumping out a loop or over a catching loop forward
dNewPlaypos >= loopSamples.end) {
// jumping out or to the exact end of a loop or over a catching loop forward
setLoopingEnabled(false);
}
}
Expand All @@ -806,6 +806,55 @@ bool LoopingControl::isLoopingEnabled() {
return m_bLoopingEnabled;
}

double LoopingControl::getSyncPositionInsideLoop(double dRequestedPlaypos, double dSyncedPlayPos) {
// no loop, no adjustment
if (!m_bLoopingEnabled) {
return dSyncedPlayPos;
}

LoopSamples loopSamples = m_loopSamples.getValue();

// if the request itself is outside loop do nothing
// loop will be disabled later by notifySeek(...)
Copy link
Member

Choose a reason for hiding this comment

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

or does it make sense to have the loop disable logic here?

if (dRequestedPlaypos < loopSamples.start || dRequestedPlaypos >= loopSamples.end) {
return dSyncedPlayPos;
}

// the requested position is inside the loop (e.g hotcue at start)
double loopSize = loopSamples.end - loopSamples.start;

// the synced position is in front of the loop
// adjust the synced position to same amount in front of the loop end
if (dSyncedPlayPos <= loopSamples.start) {
Copy link
Member

Choose a reason for hiding this comment

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

I think it should be just <

double adjustment = loopSamples.start - dSyncedPlayPos;

// if the adjustment is larger then the loop subtract N times the loopsize
// prevents jumping in front of the loop
// works like modulo on doubles
int numLoopsInAdjustment = adjustment / loopSize;
adjustment = adjustment - (numLoopsInAdjustment * loopSize);

return loopSamples.end - adjustment;
}

// the synced position is behind the loop
// adjust the synced position to same amount behind the loop start
if (dSyncedPlayPos >= loopSamples.end) {
double adjustment = dSyncedPlayPos - loopSamples.end;

// if the adjustment is larger then the loop subtract N times the loopsize
// prevents jumping behind the loop
// works like modulo on doubles
int numLoopsInAdjustment = adjustment / loopSize;
adjustment = adjustment - (numLoopsInAdjustment * loopSize);

return loopSamples.start + adjustment;
}

// both, requested and synced position are inside the loop -> do nothing
return dSyncedPlayPos;
}

void LoopingControl::trackLoaded(TrackPointer pNewTrack) {
if (m_pTrack) {
disconnect(m_pTrack.get(), &Track::beatsUpdated,
Expand Down Expand Up @@ -958,6 +1007,13 @@ void LoopingControl::updateBeatLoopingControls() {
}

void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable) {
// if a seek was queued in the engine buffer move the current sample to its position
double p_seekPosition = 0;
if (getEngineBuffer()->isSeekQueued(&p_seekPosition)) {
// seek position is already quantized if quantization is enabled
m_currentSample.setValue(p_seekPosition);
}

double maxBeatSize = s_dBeatSizes[sizeof(s_dBeatSizes)/sizeof(s_dBeatSizes[0]) - 1];
double minBeatSize = s_dBeatSizes[0];
if (beats < 0) {
Expand Down
3 changes: 2 additions & 1 deletion src/engine/controls/loopingcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ class LoopingControl : public EngineControl {
// sample, if set.
void hintReader(HintVector* pHintList) override;

void notifySeek(double dNewPlaypos, bool adjustingPhase) override;
void notifySeek(double dNewPlaypos) override;

bool isLoopingEnabled();
double getSyncPositionInsideLoop(double dRequestedPlaypos, double dSyncedPlayPos);

public slots:
void slotLoopIn(double pressed);
Expand Down
3 changes: 1 addition & 2 deletions src/engine/controls/ratecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,6 @@ void RateControl::resetRateTemp(void)
setRateTemp(0.0);
}

void RateControl::notifySeek(double playPos, bool adjustingPhase) {
Q_UNUSED(adjustingPhase);
void RateControl::notifySeek(double playPos) {
m_pScratchController->notifySeek(playPos);
}
2 changes: 1 addition & 1 deletion src/engine/controls/ratecontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class RateControl : public EngineControl {
// Set Rate Ramp Sensitivity
static void setRateRampSensitivity(int);
static int getRateRampSensitivity();
void notifySeek(double dNewPlaypos, bool adjustingPhase) override;
void notifySeek(double dNewPlaypos) override;

public slots:
void slotReverseRollActivate(double);
Expand Down
22 changes: 15 additions & 7 deletions src/engine/enginebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ void EngineBuffer::seekCloneBuffer(EngineBuffer* pOtherBuffer) {

// WARNING: This method is not thread safe and must not be called from outside
// the engine callback!
void EngineBuffer::setNewPlaypos(double newpos, bool adjustingPhase) {
void EngineBuffer::setNewPlaypos(double newpos) {
//qDebug() << m_group << "engine new pos " << newpos;

m_filepos_play = newpos;
Expand All @@ -449,7 +449,7 @@ void EngineBuffer::setNewPlaypos(double newpos, bool adjustingPhase) {

// Must hold the engineLock while using m_engineControls
for (const auto& pControl: qAsConst(m_engineControls)) {
pControl->notifySeek(m_filepos_play, adjustingPhase);
pControl->notifySeek(m_filepos_play);
}

verifyPlay(); // verify or update play button and indicator
Expand Down Expand Up @@ -1125,7 +1125,7 @@ void EngineBuffer::processSeek(bool paused) {
// call anyway again.

SeekRequests seekType = static_cast<SeekRequest>(
m_iSeekQueued.fetchAndStoreRelease(SEEK_NONE));
m_iSeekQueued.loadAcquire());
double position = m_queuedSeekPosition.getValue();

// Don't allow the playposition to go past the end.
Expand All @@ -1138,14 +1138,12 @@ void EngineBuffer::processSeek(bool paused) {
seekType |= SEEK_PHASE;
}

bool adjustingPhase = false;
switch (seekType) {
case SEEK_NONE:
return;
case SEEK_PHASE:
// only adjust phase
position = m_filepos_play;
adjustingPhase = true;
break;
case SEEK_STANDARD:
if (m_pQuantize->toBool()) {
Expand All @@ -1164,11 +1162,16 @@ void EngineBuffer::processSeek(bool paused) {
}

if (!paused && (seekType & SEEK_PHASE)) {
position = m_pBpmControl->getNearestPositionInPhase(position, true, true);
double syncPosition = position;
double requestedPosition = position;
syncPosition = m_pBpmControl->getNearestPositionInPhase(position, true, true);
position = m_pLoopingControl->getSyncPositionInsideLoop(requestedPosition, syncPosition);
}

if (position != m_filepos_play) {
setNewPlaypos(position, adjustingPhase);
setNewPlaypos(position);
}
m_iSeekQueued.storeRelease(SEEK_NONE);
}

void EngineBuffer::postProcess(const int iBufferSize) {
Expand Down Expand Up @@ -1284,6 +1287,11 @@ bool EngineBuffer::isTrackLoaded() {
return false;
}

bool EngineBuffer::isSeekQueued(double* pSeekPosition) {
*pSeekPosition = m_queuedSeekPosition.getValue();
return m_iSeekQueued.load() != SEEK_NONE;
}

void EngineBuffer::slotEjectTrack(double v) {
if (v > 0) {
// Don't allow rejections while playing a track. We don't need to lock to
Expand Down
3 changes: 2 additions & 1 deletion src/engine/enginebuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class EngineBuffer : public EngineObject {

QString getGroup();
bool isTrackLoaded();
bool isSeekQueued(double* pSeekPosition);
TrackPointer getLoadedTrack() const;

double getExactPlayPos();
Expand Down Expand Up @@ -230,7 +231,7 @@ class EngineBuffer : public EngineObject {
void seekCloneBuffer(EngineBuffer* pOtherBuffer);

// Reset buffer playpos and set file playpos.
void setNewPlaypos(double playpos, bool adjustingPhase);
void setNewPlaypos(double playpos);

void processSyncRequests();
void processSeek(bool paused);
Expand Down
2 changes: 1 addition & 1 deletion src/engine/readaheadmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ double ReadAheadManager::getFilePlaypositionFromLog(
// (Not looping control)
if (shouldNotifySeek) {
if (m_pRateControl) {
m_pRateControl->notifySeek(entry.virtualPlaypositionStart, false);
m_pRateControl->notifySeek(entry.virtualPlaypositionStart);
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/test/readaheadmanager_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ class StubLoopControl : public LoopingControl {
Q_UNUSED(pHintList);
}

void notifySeek(double dNewPlaypos, bool adjustingPhase) override {
void notifySeek(double dNewPlaypos) override {
Q_UNUSED(dNewPlaypos);
Q_UNUSED(adjustingPhase);
}

void trackLoaded(TrackPointer pTrack) override {
Expand Down