diff --git a/src/engine/controls/loopingcontrol.cpp b/src/engine/controls/loopingcontrol.cpp index 02f9e10fb4cd..245201df7631 100644 --- a/src/engine/controls/loopingcontrol.cpp +++ b/src/engine/controls/loopingcontrol.cpp @@ -53,7 +53,7 @@ LoopingControl::LoopingControl(QString group, m_loopSamples.setValue(m_oldLoopSamples); m_currentSample.setValue(0.0); m_pActiveBeatLoop = NULL; - + m_pRateControl = NULL; //Create loop-in, loop-out, loop-exit, and reloop/exit ControlObjects m_pLoopInButton = new ControlPushButton(ConfigKey(group, "loop_in")); connect(m_pLoopInButton, &ControlObject::valueChanged, @@ -682,6 +682,10 @@ void LoopingControl::setLoopOutToCurrentPosition() { m_loopSamples.setValue(loopSamples); } +void LoopingControl::setRateControl(RateControl* rateControl) { + m_pRateControl = rateControl; +} + void LoopingControl::slotLoopOut(double pressed) { if (m_pTrack == nullptr) { return; @@ -1060,16 +1064,19 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable newloopSamples.start = currentSample; } } else { - // loop_in is set to the previous beat if quantize is on. The - // closest beat might be ahead of play position which would cause a seek. - // TODO: If in reverse, should probably choose nextBeat. + // loop_in is set to the closest beat if quantize is on and the loop size is >= 1 beat. + // The closest beat might be ahead of play position and will cause a catching loop. double prevBeat; double nextBeat; pBeats->findPrevNextBeats(currentSample, &prevBeat, &nextBeat); if (m_pQuantizeEnabled->toBool() && prevBeat != -1) { + double beatLength = nextBeat - prevBeat; + double loopLength = beatLength * beats; + + double closestBeat = pBeats->findClosestBeat(currentSample); if (beats >= 1.0) { - newloopSamples.start = prevBeat; + newloopSamples.start = closestBeat; } else { // In case of beat length less then 1 beat: // (| - beats, ^ - current track's position): @@ -1078,15 +1085,29 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable // // If we press 1/2 beatloop we want loop from 50% to 100%, // If I press 1/4 beatloop, we want loop from 50% to 75% etc - double beat_len = nextBeat - prevBeat; - double loops_per_beat = 1.0 / beats; - double beat_pos = currentSample - prevBeat; - int beat_frac = - static_cast(floor((beat_pos / beat_len) * - loops_per_beat)); - newloopSamples.start = prevBeat + beat_len / loops_per_beat * beat_frac; + double samplesSinceLastBeat = currentSample - prevBeat; + + // find the previous beat fraction and check if the current position is closer to this or the next one + // place the new loop start to the closer one + double previousFractionBeat = prevBeat + floor(samplesSinceLastBeat / loopLength) * loopLength; + double samplesSinceLastFractionBeat = currentSample - previousFractionBeat; + + if (samplesSinceLastFractionBeat <= (loopLength / 2.0)) { + newloopSamples.start = previousFractionBeat; + } else { + newloopSamples.start = previousFractionBeat + loopLength; + } } + // If running reverse, move the loop one loop size to the left. + // Thus, the loops end will be closest to the current position + bool reverse = false; + if (m_pRateControl != NULL) { + reverse = m_pRateControl->isReverseButtonPressed(); + } + if (reverse) { + newloopSamples.start -= loopLength; + } } else { newloopSamples.start = currentSample; } diff --git a/src/engine/controls/loopingcontrol.h b/src/engine/controls/loopingcontrol.h index 55d4f0c15f17..6be5fdb78b01 100644 --- a/src/engine/controls/loopingcontrol.h +++ b/src/engine/controls/loopingcontrol.h @@ -8,11 +8,12 @@ #include #include -#include "preferences/usersettings.h" +#include "control/controlvalue.h" #include "engine/controls/enginecontrol.h" -#include "track/track.h" +#include "engine/controls/ratecontrol.h" +#include "preferences/usersettings.h" #include "track/beats.h" -#include "control/controlvalue.h" +#include "track/track.h" #define MINIMUM_AUDIBLE_LOOP_SIZE 300 // In samples @@ -50,7 +51,7 @@ class LoopingControl : public EngineControl { double getSyncPositionInsideLoop(double dRequestedPlaypos, double dSyncedPlayPos); void notifySeek(double dNewPlaypos) override; - + void setRateControl(RateControl* rateControl); bool isLoopingEnabled(); public slots: @@ -129,6 +130,7 @@ class LoopingControl : public EngineControl { ControlPushButton* m_pLoopHalveButton; ControlPushButton* m_pLoopDoubleButton; ControlObject* m_pSlipEnabled; + RateControl* m_pRateControl; ControlObject* m_pPlayButton; bool m_bLoopingEnabled; diff --git a/src/engine/controls/ratecontrol.cpp b/src/engine/controls/ratecontrol.cpp index 75c7327b10c2..5ee5af58ffe8 100644 --- a/src/engine/controls/ratecontrol.cpp +++ b/src/engine/controls/ratecontrol.cpp @@ -556,3 +556,10 @@ void RateControl::resetRateTemp(void) void RateControl::notifySeek(double playPos) { m_pScratchController->notifySeek(playPos); } + +bool RateControl::isReverseButtonPressed() { + if (m_pReverseButton) { + return m_pReverseButton->toBool(); + } + return false; +} diff --git a/src/engine/controls/ratecontrol.h b/src/engine/controls/ratecontrol.h index 635ae15b2e37..a53dfd383f15 100644 --- a/src/engine/controls/ratecontrol.h +++ b/src/engine/controls/ratecontrol.h @@ -78,6 +78,7 @@ class RateControl : public EngineControl { static void setRateRampSensitivity(int); static int getRateRampSensitivity(); void notifySeek(double dNewPlaypos) override; + bool isReverseButtonPressed(); public slots: void slotReverseRollActivate(double); diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 2c10c3bfe8a4..5ba47c24a5cf 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -180,14 +180,13 @@ EngineBuffer::EngineBuffer(const QString& group, UserSettingsPointer pConfig, // quantization (alignment) of loop in/out positions and (hot)cues with // beats. QuantizeControl* quantize_control = new QuantizeControl(group, pConfig); + addControl(quantize_control); + m_pQuantize = ControlObject::getControl(ConfigKey(group, "quantize")); // Create the Loop Controller m_pLoopingControl = new LoopingControl(group, pConfig); addControl(m_pLoopingControl); - addControl(quantize_control); - m_pQuantize = ControlObject::getControl(ConfigKey(group, "quantize")); - m_pEngineSync = pMixingEngine->getEngineSync(); m_pSyncControl = new SyncControl(group, pConfig, pChannel, m_pEngineSync); @@ -198,9 +197,12 @@ EngineBuffer::EngineBuffer(const QString& group, UserSettingsPointer pConfig, addControl(m_pVinylControlControl); #endif + // Create the Rate Controller m_pRateControl = new RateControl(group, pConfig); // Add the Rate Controller addControl(m_pRateControl); + // Looping Control needs Rate Control for Reverse Button + m_pLoopingControl->setRateControl(m_pRateControl); // Create the BPM Controller m_pBpmControl = new BpmControl(group, pConfig); @@ -532,6 +534,10 @@ TrackPointer EngineBuffer::getLoadedTrack() const { return m_pCurrentTrack; } +bool EngineBuffer::isReverse() { + return m_reverse_old; +} + void EngineBuffer::ejectTrack() { // clear track values in any case, this may fix Bug #1450424 //qDebug() << "EngineBuffer::ejectTrack()"; diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index 46735d16fa11..394794225fbd 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -146,6 +146,8 @@ class EngineBuffer : public EngineObject { bool getQueuedSeekPosition(double* pSeekPosition); TrackPointer getLoadedTrack() const; + bool isReverse(); + double getExactPlayPos(); double getVisualPlayPos(); double getTrackSamples();