Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions src/engine/loopingcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@ void LoopingControl::slotReloopToggle(double val) {
if (m_bLoopRollActive) {
m_pSlipEnabled->set(0);
m_bLoopRollActive = false;
m_activeLoopRolls.clear();
}
setLoopingEnabled(false);
//qDebug() << "reloop_toggle looping off";
Expand Down Expand Up @@ -846,10 +847,11 @@ void LoopingControl::slotBeatLoopActivateRoll(BeatLoopingControl* pBeatLoopContr
return;
}

// Disregard existing loops.
// Disregard existing loops (except beatlooprolls).
m_pSlipEnabled->set(1);
slotBeatLoop(pBeatLoopControl->getSize(), false, true);
slotBeatLoop(pBeatLoopControl->getSize(), m_bLoopRollActive, true);
m_bLoopRollActive = true;
m_activeLoopRolls.push(pBeatLoopControl->getSize());
}

void LoopingControl::slotBeatLoopDeactivate(BeatLoopingControl* pBeatLoopControl) {
Expand All @@ -858,14 +860,29 @@ void LoopingControl::slotBeatLoopDeactivate(BeatLoopingControl* pBeatLoopControl
}

void LoopingControl::slotBeatLoopDeactivateRoll(BeatLoopingControl* pBeatLoopControl) {
Q_UNUSED(pBeatLoopControl);
setLoopingEnabled(false);
pBeatLoopControl->deactivate();
const double size = pBeatLoopControl->getSize();
auto i = m_activeLoopRolls.begin();
while (i != m_activeLoopRolls.end()) {
if (size == *i) {
i = m_activeLoopRolls.erase(i);
} else {
++i;
}
}

// Make sure slip mode is not turned off if it was turned on
// by something that was not a rolling beatloop.
if (m_bLoopRollActive) {
if (m_bLoopRollActive && m_activeLoopRolls.empty()) {
setLoopingEnabled(false);
m_pSlipEnabled->set(0);
m_bLoopRollActive = false;
}

// Return to the previous beatlooproll if necessary.
if (!m_activeLoopRolls.empty()) {
slotBeatLoop(m_activeLoopRolls.top(), m_bLoopRollActive, true);
}
}

void LoopingControl::clearActiveBeatLoop() {
Expand Down Expand Up @@ -1055,6 +1072,7 @@ void LoopingControl::slotBeatLoopRollActivate(double pressed) {
if (m_bLoopRollActive) {
m_pSlipEnabled->set(0.0);
m_bLoopRollActive = false;
m_activeLoopRolls.clear();
}
} else {
m_pSlipEnabled->set(1.0);
Expand All @@ -1068,6 +1086,7 @@ void LoopingControl::slotBeatLoopRollActivate(double pressed) {
if (m_bLoopRollActive) {
m_pSlipEnabled->set(0.0);
m_bLoopRollActive = false;
m_activeLoopRolls.clear();
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/engine/loopingcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define LOOPINGCONTROL_H

#include <QObject>
#include <QStack>

#include "preferences/usersettings.h"
#include "engine/enginecontrol.h"
Expand Down Expand Up @@ -131,6 +132,7 @@ class LoopingControl : public EngineControl {
bool m_bAdjustingLoopInOld;
bool m_bAdjustingLoopOutOld;
bool m_bLoopOutPressedWhileLoopDisabled;
QStack<double> m_activeLoopRolls;
ControlValueAtomic<LoopSamples> m_loopSamples;
LoopSamples m_oldLoopSamples;
ControlValueAtomic<double> m_currentSample;
Expand Down
114 changes: 114 additions & 0 deletions src/test/looping_control_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class LoopingControlTest : public MockedEngineBackendTest {
m_pButtonBeatMoveBackward = std::make_unique<ControlProxy>(m_sGroup1, "loop_move_1_backward");
m_pButtonBeatLoop2Activate = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_2_activate");
m_pButtonBeatLoop4Activate = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_4_activate");
m_pBeatLoop1Enabled = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_1_enabled");
m_pBeatLoop2Enabled = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_2_enabled");
m_pBeatLoop4Enabled = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_4_enabled");
m_pBeatLoop64Enabled = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_64_enabled");
Expand All @@ -59,6 +60,9 @@ class LoopingControlTest : public MockedEngineBackendTest {
m_pBeatJumpSize = std::make_unique<ControlProxy>(m_sGroup1, "beatjump_size");
m_pButtonBeatJumpForward = std::make_unique<ControlProxy>(m_sGroup1, "beatjump_forward");
m_pButtonBeatJumpBackward = std::make_unique<ControlProxy>(m_sGroup1, "beatjump_backward");
m_pButtonBeatLoopRoll1Activate = std::make_unique<ControlProxy>(m_sGroup1, "beatlooproll_1_activate");
m_pButtonBeatLoopRoll2Activate = std::make_unique<ControlProxy>(m_sGroup1, "beatlooproll_2_activate");
m_pButtonBeatLoopRoll4Activate = std::make_unique<ControlProxy>(m_sGroup1, "beatlooproll_4_activate");
}

bool isLoopEnabled() {
Expand Down Expand Up @@ -92,6 +96,7 @@ class LoopingControlTest : public MockedEngineBackendTest {
std::unique_ptr<ControlProxy> m_pButtonBeatMoveBackward;
std::unique_ptr<ControlProxy> m_pButtonBeatLoop2Activate;
std::unique_ptr<ControlProxy> m_pButtonBeatLoop4Activate;
std::unique_ptr<ControlProxy> m_pBeatLoop1Enabled;
std::unique_ptr<ControlProxy> m_pBeatLoop2Enabled;
std::unique_ptr<ControlProxy> m_pBeatLoop4Enabled;
std::unique_ptr<ControlProxy> m_pBeatLoop64Enabled;
Expand All @@ -101,6 +106,9 @@ class LoopingControlTest : public MockedEngineBackendTest {
std::unique_ptr<ControlProxy> m_pBeatJumpSize;
std::unique_ptr<ControlProxy> m_pButtonBeatJumpForward;
std::unique_ptr<ControlProxy> m_pButtonBeatJumpBackward;
std::unique_ptr<ControlProxy> m_pButtonBeatLoopRoll1Activate;
std::unique_ptr<ControlProxy> m_pButtonBeatLoopRoll2Activate;
std::unique_ptr<ControlProxy> m_pButtonBeatLoopRoll4Activate;
};

TEST_F(LoopingControlTest, LoopSet) {
Expand Down Expand Up @@ -874,3 +882,109 @@ TEST_F(LoopingControlTest, LoopEscape) {
seekToSampleAndProcess(50);
EXPECT_FALSE(isLoopEnabled());
}

TEST_F(LoopingControlTest, BeatLoopRoll_Activation) {
m_pTrack1->setBpm(120.0);

m_pButtonBeatLoopRoll2Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop2Enabled->toBool());

m_pButtonBeatLoopRoll2Activate->set(0.0);
EXPECT_FALSE(m_pLoopEnabled->toBool());
EXPECT_FALSE(m_pBeatLoop2Enabled->toBool());
}

TEST_F(LoopingControlTest, BeatLoopRoll_Overlap) {
m_pTrack1->setBpm(120.0);

m_pButtonBeatLoopRoll2Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop2Enabled->toBool());

m_pButtonBeatLoopRoll4Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop4Enabled->toBool());
EXPECT_FALSE(m_pBeatLoop2Enabled->toBool());

m_pButtonBeatLoopRoll2Activate->set(0.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop4Enabled->toBool());
EXPECT_FALSE(m_pBeatLoop2Enabled->toBool());

m_pButtonBeatLoopRoll4Activate->set(0.0);
EXPECT_FALSE(m_pLoopEnabled->toBool());
EXPECT_FALSE(m_pBeatLoop4Enabled->toBool());
}

TEST_F(LoopingControlTest, BeatLoopRoll_OverlapStackUnwind) {
m_pTrack1->setBpm(120.0);

// start a 2 beat loop roll
m_pButtonBeatLoopRoll2Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop2Enabled->toBool());

// start a 4 beat loop roll on top of the previous loop
m_pButtonBeatLoopRoll4Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop4Enabled->toBool());
EXPECT_FALSE(m_pBeatLoop2Enabled->toBool());

// start a 1 beat loop roll on top of the previous loop
m_pButtonBeatLoopRoll1Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop1Enabled->toBool());
EXPECT_FALSE(m_pBeatLoop4Enabled->toBool());

// stop the 4 beat loop roll, the 1 beat roll should continue
m_pButtonBeatLoopRoll4Activate->set(0.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop1Enabled->toBool());

// stop the 1 beat loop roll, the 2 beat roll should continue
m_pButtonBeatLoopRoll1Activate->set(0.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop2Enabled->toBool());
EXPECT_FALSE(m_pBeatLoop1Enabled->toBool());

// stop the 2 beat loop roll
m_pButtonBeatLoopRoll2Activate->set(0.0);
EXPECT_FALSE(m_pLoopEnabled->toBool());
EXPECT_FALSE(m_pBeatLoop2Enabled->toBool());
}

TEST_F(LoopingControlTest, BeatLoopRoll_StartPoint) {
m_pTrack1->setBpm(120.0);

// start a 4 beat loop roll, start point should be overriden to play position
m_pLoopStartPoint->slotSet(8);
m_pButtonBeatLoopRoll4Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop4Enabled->toBool());
EXPECT_EQ(0, m_pLoopStartPoint->get());

// move the start point, activate a 1 beat loop roll, new start point be preserved
m_pLoopStartPoint->slotSet(8);
m_pButtonBeatLoopRoll1Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop1Enabled->toBool());
EXPECT_EQ(8, m_pLoopStartPoint->get());

// end the 1 beat loop roll
m_pButtonBeatLoopRoll1Activate->set(0.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_EQ(8, m_pLoopStartPoint->get());
EXPECT_TRUE(m_pBeatLoop4Enabled->toBool());

// end the 4 beat loop roll
m_pButtonBeatLoopRoll4Activate->set(0.0);
EXPECT_FALSE(m_pLoopEnabled->toBool());
EXPECT_FALSE(m_pBeatLoop4Enabled->toBool());

// new loop should start back at 0
m_pButtonBeatLoopRoll4Activate->set(1.0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
EXPECT_TRUE(m_pBeatLoop4Enabled->toBool());
EXPECT_EQ(0, m_pLoopStartPoint->get());
}