diff --git a/src/engine/controls/loopingcontrol.cpp b/src/engine/controls/loopingcontrol.cpp index 13a392c72f3..a5797eceaf6 100644 --- a/src/engine/controls/loopingcontrol.cpp +++ b/src/engine/controls/loopingcontrol.cpp @@ -1546,14 +1546,28 @@ void LoopingControl::slotBeatLoopSizeChangeRequest(double beats) { } void LoopingControl::slotBeatLoopToggle(double pressed) { - if (pressed > 0) { - if (m_bLoopingEnabled) { + if (pressed <= 0) { + return; + } + + if (m_bLoopingEnabled) { + // If we're in a rolling loop quit slip mode and adopt it as regular loop. + // Use case is to have a looproll button pressed, then press loop_activate + // and nothing should happen when releasing the looproll button. + if (m_bLoopRollActive) { + m_bLoopRollActive = false; + m_activeLoopRolls.clear(); + // Reset the previous loop info so restoreloopInfo() does not + // reset/disable the loop we want to keep. + m_prevLoopInfo.setValue(LoopInfo{}); + getEngineBuffer()->slipQuitAndAdopt(); + } else { // Deactivate the loop if we're already looping setLoopingEnabled(false); - } else { - // Create a loop at current position - slotBeatLoop(m_pCOBeatLoopSize->get()); } + } else { + // Create a loop at current position + slotBeatLoop(m_pCOBeatLoopSize->get()); } } diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 97874b0e946..a23b7270995 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -87,6 +87,7 @@ EngineBuffer::EngineBuffer(const QString& group, m_iSeekPhaseQueued(0), m_iEnableSyncQueued(SYNC_REQUEST_NONE), m_iSyncModeQueued(static_cast(SyncMode::Invalid)), + m_slipQuitAndAdopt(0), m_bPlayAfterLoading(false), m_pCrossfadeBuffer(SampleUtil::alloc(MAX_BUFFER_LEN)), m_bCrossfadeReady(false), @@ -814,6 +815,11 @@ void EngineBuffer::slotKeylockEngineChanged(double dIndex) { } } +void EngineBuffer::slipQuitAndAdopt() { + m_slipQuitAndAdopt.storeRelease(1); + m_pSlipButton->set(0); +} + void EngineBuffer::processTrackLocked( CSAMPLE* pOutput, const int iBufferSize, mixxx::audio::SampleRate sampleRate) { ScopedTimer t("EngineBuffer::process_pauselock"); @@ -1193,8 +1199,12 @@ void EngineBuffer::processSlip(int iBufferSize) { m_slipPos = m_playPos; m_dSlipRate = m_rate_old; } else { - // TODO(owen) assuming that looping will get canceled properly - seekExact(m_slipPos.toNearestFrameBoundary()); + // If m_slipQuitAndAdopt is 1 we've already quit slip mode + // but we don't seek.in that case. + if (m_slipQuitAndAdopt.fetchAndStoreAcquire(0) == 0) { + // TODO(owen) assuming that looping will get canceled properly + seekExact(m_slipPos.toNearestFrameBoundary()); + } m_slipPos = mixxx::audio::kStartFramePos; } } diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index b0de905ebbc..aa8efc0d315 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -197,6 +197,8 @@ class EngineBuffer : public EngineObject { void seekAbs(mixxx::audio::FramePos); void seekExact(mixxx::audio::FramePos); + void slipQuitAndAdopt(); + public slots: void slotControlPlayRequest(double); void slotControlPlayFromStart(double); @@ -421,6 +423,7 @@ class EngineBuffer : public EngineObject { ControlValueAtomic m_queuedSeek; bool m_previousBufferSeek = false; + QAtomicInt m_slipQuitAndAdopt; /// Indicates that no seek is queued static constexpr QueuedSeek kNoQueuedSeek = {mixxx::audio::kInvalidFramePos, SEEK_NONE}; QAtomicPointer m_pChannelToCloneFrom;