diff --git a/src/engine/controls/enginecontrol.cpp b/src/engine/controls/enginecontrol.cpp index a70c49a26407..ddacfb7583df 100644 --- a/src/engine/controls/enginecontrol.cpp +++ b/src/engine/controls/enginecontrol.cpp @@ -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() { diff --git a/src/engine/controls/enginecontrol.h b/src/engine/controls/enginecontrol.h index 3fbfa1a4a14b..2e2d3ce06f39 100644 --- a/src/engine/controls/enginecontrol.h +++ b/src/engine/controls/enginecontrol.h @@ -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: diff --git a/src/engine/controls/loopingcontrol.cpp b/src/engine/controls/loopingcontrol.cpp index f39249d4fe15..a704fdd74857 100644 --- a/src/engine/controls/loopingcontrol.cpp +++ b/src/engine/controls/loopingcontrol.cpp @@ -5,11 +5,12 @@ #include #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" @@ -768,13 +769,12 @@ 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) { @@ -782,8 +782,8 @@ void LoopingControl::notifySeek(double dNewPlaypos, bool adjustingPhase) { 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); } } @@ -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(...) + 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) { + 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, @@ -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) { diff --git a/src/engine/controls/loopingcontrol.h b/src/engine/controls/loopingcontrol.h index 3623e53d990d..847b8c96c746 100644 --- a/src/engine/controls/loopingcontrol.h +++ b/src/engine/controls/loopingcontrol.h @@ -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); diff --git a/src/engine/controls/ratecontrol.cpp b/src/engine/controls/ratecontrol.cpp index 9faa6422a5ae..75c7327b10c2 100644 --- a/src/engine/controls/ratecontrol.cpp +++ b/src/engine/controls/ratecontrol.cpp @@ -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); } diff --git a/src/engine/controls/ratecontrol.h b/src/engine/controls/ratecontrol.h index 8bbc7acb55fd..635ae15b2e37 100644 --- a/src/engine/controls/ratecontrol.h +++ b/src/engine/controls/ratecontrol.h @@ -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); diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 36e5409d19bc..3c25fa4932af 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -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; @@ -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 @@ -1125,7 +1125,7 @@ void EngineBuffer::processSeek(bool paused) { // call anyway again. SeekRequests seekType = static_cast( - m_iSeekQueued.fetchAndStoreRelease(SEEK_NONE)); + m_iSeekQueued.loadAcquire()); double position = m_queuedSeekPosition.getValue(); // Don't allow the playposition to go past the end. @@ -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()) { @@ -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) { @@ -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 diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index def10c646f93..08a5562acc28 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -141,6 +141,7 @@ class EngineBuffer : public EngineObject { QString getGroup(); bool isTrackLoaded(); + bool isSeekQueued(double* pSeekPosition); TrackPointer getLoadedTrack() const; double getExactPlayPos(); @@ -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); diff --git a/src/engine/readaheadmanager.cpp b/src/engine/readaheadmanager.cpp index a70473bb8923..be4ba1324871 100644 --- a/src/engine/readaheadmanager.cpp +++ b/src/engine/readaheadmanager.cpp @@ -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); } } diff --git a/src/test/readaheadmanager_test.cpp b/src/test/readaheadmanager_test.cpp index 4dd065190262..219422556c9f 100644 --- a/src/test/readaheadmanager_test.cpp +++ b/src/test/readaheadmanager_test.cpp @@ -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 {