Skip to content

Commit

Permalink
measure beatloop_size when setting manual loops with quantize
Browse files Browse the repository at this point in the history
Plus:
* make loop_double/halve always update beatloop_size
* remove beatloop_double/halve ControlPushButtons

Now, beatloop_size can only be out of sync with the loop in two cases:
* a new track is loaded with a loop of a different size
* a manual loop is set using loop_in/out without quantize

It is no longer possible to resize manual loops that are not quantized
with loop_double/halve. As discussed on PR #1187, that is not very
useful behavior. It is still possible to resize manual loops that are
not quantized by setting loop_scale. That will not update beatloop_size.
  • Loading branch information
Be-ing committed May 8, 2017
1 parent 697695c commit 2c129ea
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 136 deletions.
2 changes: 1 addition & 1 deletion src/engine/enginebuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class EngineBuffer : public EngineObject {
UserSettingsPointer m_pConfig;

LoopingControl* m_pLoopingControl;
FRIEND_TEST(LoopingControlTest, LoopHalveButton_HalvesLoop);
FRIEND_TEST(LoopingControlTest, LoopScale_HalvesLoop);
FRIEND_TEST(LoopingControlTest, LoopMoveTest);
FRIEND_TEST(LoopingControlTest, LoopResizeSeek);
FRIEND_TEST(LoopingControlTest, ReloopToggleButton_DoesNotJumpAhead);
Expand Down
55 changes: 15 additions & 40 deletions src/engine/loopingcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,6 @@ LoopingControl::LoopingControl(QString group,
true, false, false, 4.0);
m_pCOBeatLoopSize->connectValueChangeRequest(this,
SLOT(slotBeatLoopSizeChangeRequest(double)), Qt::DirectConnection);
m_pCOBeatLoopDouble = new ControlPushButton(ConfigKey(group, "beatloop_double"));
connect(m_pCOBeatLoopDouble, SIGNAL(valueChanged(double)),
this, SLOT(slotBeatLoopDouble(double)));
m_pCOBeatLoopHalve = new ControlPushButton(ConfigKey(group, "beatloop_halve"));
connect(m_pCOBeatLoopHalve, SIGNAL(valueChanged(double)),
this, SLOT(slotBeatLoopHalve(double)));
m_pCOBeatLoopToggle = new ControlPushButton(ConfigKey(group, "beatloop_toggle"));
connect(m_pCOBeatLoopToggle, SIGNAL(valueChanged(double)),
this, SLOT(slotBeatLoopToggle(double)));
Expand Down Expand Up @@ -233,8 +227,6 @@ LoopingControl::~LoopingControl() {
delete pBeatLoop;
}
delete m_pCOBeatLoopSize;
delete m_pCOBeatLoopDouble;
delete m_pCOBeatLoopHalve;
delete m_pCOBeatLoopToggle;
delete m_pCOBeatLoopRollActivate;

Expand Down Expand Up @@ -315,29 +307,15 @@ void LoopingControl::slotLoopHalve(double pressed) {
return;
}

LoopSamples loopSamples = m_loopSamples.getValue();
bool noLoopSet = loopSamples.start == kNoTrigger || loopSamples.end == kNoTrigger;

if (noLoopSet || currentLoopMatchesBeatloopSize()) {
slotBeatLoop(m_pCOBeatLoopSize->get() / 2.0, true, false);
} else {
slotLoopScale(0.5);
}
slotBeatLoop(m_pCOBeatLoopSize->get() / 2.0, true, false);
}

void LoopingControl::slotLoopDouble(double pressed) {
if (pressed <= 0.0) {
return;
}

LoopSamples loopSamples = m_loopSamples.getValue();
bool noLoopSet = loopSamples.start == kNoTrigger || loopSamples.end == kNoTrigger;

if (noLoopSet || currentLoopMatchesBeatloopSize()) {
slotBeatLoop(m_pCOBeatLoopSize->get() * 2.0, true, false);
} else {
slotLoopScale(2.0);
}
slotBeatLoop(m_pCOBeatLoopSize->get() * 2.0, true, false);
}

double LoopingControl::process(const double dRate,
Expand Down Expand Up @@ -499,6 +477,13 @@ void LoopingControl::setLoopInToCurrentPosition() {
loopSamples.start = pos;
m_pCOLoopStartPosition->set(loopSamples.start);

if (m_pQuantizeEnabled->toBool()
&& loopSamples.start < loopSamples.end
&& m_pBeats != nullptr) {
m_pCOBeatLoopSize->setAndConfirm(
m_pBeats->numBeatsInRange(loopSamples.start, loopSamples.end));
}

m_loopSamples.setValue(loopSamples);
//qDebug() << "set loop_in to " << loopSamples.start;
}
Expand Down Expand Up @@ -575,6 +560,10 @@ void LoopingControl::setLoopOutToCurrentPosition() {
loopSamples.end = pos;
m_pCOLoopEndPosition->set(loopSamples.end);
m_loopSamples.setValue(loopSamples);
if (m_pQuantizeEnabled->toBool() && m_pBeats != nullptr) {
m_pCOBeatLoopSize->setAndConfirm(
m_pBeats->numBeatsInRange(loopSamples.start, loopSamples.end));
}

// start looping
if (loopSamples.start != kNoTrigger &&
Expand Down Expand Up @@ -902,7 +891,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable
double nextBeat;
m_pBeats->findPrevNextBeats(cur_pos, &prevBeat, &nextBeat);

if (m_pQuantizeEnabled->get() > 0.0 && prevBeat != -1) {
if (m_pQuantizeEnabled->toBool() && prevBeat != -1) {
if (beats >= 1.0) {
newloopSamples.start = prevBeat;
} else {
Expand Down Expand Up @@ -960,7 +949,7 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable
return;
}

// When loading a new track or after setting a manual loop,
// When loading a new track or after setting a manual loop without quantize,
// do not resize the existing loop until beatloop_size matches
// the size of the existing loop.
// Do not return immediately so beatloop_size can be updated.
Expand Down Expand Up @@ -1020,20 +1009,6 @@ void LoopingControl::slotBeatLoopSizeChangeRequest(double beats) {
slotBeatLoop(beats, true, false);
}

void LoopingControl::slotBeatLoopDouble(double pressed) {
if (pressed <= 0.0) {
return;
}
slotBeatLoop(m_pCOBeatLoopSize->get() * 2.0, true, false);
}

void LoopingControl::slotBeatLoopHalve(double pressed) {
if (pressed <= 0.0) {
return;
}
slotBeatLoop(m_pCOBeatLoopSize->get() / 2.0, true, false);
}

void LoopingControl::slotBeatLoopToggle(double pressed) {
if (pressed > 0) {
if (m_bLoopingEnabled) {
Expand Down
4 changes: 0 additions & 4 deletions src/engine/loopingcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ class LoopingControl : public EngineControl {
// beatslicing effect.
void slotBeatLoop(double loopSize, bool keepStartPoint=false, bool enable=true);
void slotBeatLoopSizeChangeRequest(double beats);
void slotBeatLoopHalve(double pressed);
void slotBeatLoopDouble(double pressed);
void slotBeatLoopToggle(double pressed);
void slotBeatLoopRollActivate(double pressed);
void slotBeatLoopActivate(BeatLoopingControl* pBeatLoopControl);
Expand Down Expand Up @@ -156,8 +154,6 @@ class LoopingControl : public EngineControl {
// Base BeatLoop Control Object.
ControlObject* m_pCOBeatLoop;
ControlObject* m_pCOBeatLoopSize;
ControlPushButton* m_pCOBeatLoopDouble;
ControlPushButton* m_pCOBeatLoopHalve;
// Different sizes for Beat Loops/Seeks.
static double s_dBeatSizes[];
// Array of BeatLoopingControls, one for each size.
Expand Down
135 changes: 44 additions & 91 deletions src/test/looping_control_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class LoopingControlTest : public MockedEngineBackendTest {
m_pLoopEnabled = std::make_unique<ControlProxy>(m_sGroup1, "loop_enabled");
m_pLoopStartPoint = std::make_unique<ControlProxy>(m_sGroup1, "loop_start_position");
m_pLoopEndPoint = std::make_unique<ControlProxy>(m_sGroup1, "loop_end_position");
m_pLoopScale = std::make_unique<ControlProxy>(m_sGroup1, "loop_scale");
m_pButtonPlay = std::make_unique<ControlProxy>(m_sGroup1, "play");
m_pPlayPosition = std::make_unique<ControlProxy>(m_sGroup1, "playposition");
m_pButtonBeatMoveForward = std::make_unique<ControlProxy>(m_sGroup1, "loop_move_1_forward");
Expand All @@ -50,8 +51,6 @@ class LoopingControlTest : public MockedEngineBackendTest {
m_pBeatLoop64Enabled = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_64_enabled");
m_pBeatLoop = std::make_unique<ControlProxy>(m_sGroup1, "beatloop");
m_pBeatLoopSize = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_size");
m_pButtonBeatLoopDouble = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_double");
m_pButtonBeatLoopHalve = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_halve");
m_pButtonBeatLoopToggle = std::make_unique<ControlProxy>(m_sGroup1, "beatloop_toggle");
}

Expand Down Expand Up @@ -79,6 +78,7 @@ class LoopingControlTest : public MockedEngineBackendTest {
std::unique_ptr<ControlProxy> m_pLoopEnabled;
std::unique_ptr<ControlProxy> m_pLoopStartPoint;
std::unique_ptr<ControlProxy> m_pLoopEndPoint;
std::unique_ptr<ControlProxy> m_pLoopScale;
std::unique_ptr<ControlProxy> m_pPlayPosition;
std::unique_ptr<ControlProxy> m_pButtonPlay;
std::unique_ptr<ControlProxy> m_pButtonBeatMoveForward;
Expand All @@ -90,8 +90,6 @@ class LoopingControlTest : public MockedEngineBackendTest {
std::unique_ptr<ControlProxy> m_pBeatLoop64Enabled;
std::unique_ptr<ControlProxy> m_pBeatLoop;
std::unique_ptr<ControlProxy> m_pBeatLoopSize;
std::unique_ptr<ControlProxy> m_pButtonBeatLoopDouble;
std::unique_ptr<ControlProxy> m_pButtonBeatLoopHalve;
std::unique_ptr<ControlProxy> m_pButtonBeatLoopToggle;
};

Expand Down Expand Up @@ -336,7 +334,7 @@ TEST_F(LoopingControlTest, ReloopAndStopButton) {
EXPECT_TRUE(m_pLoopEnabled->toBool());
}

TEST_F(LoopingControlTest, LoopDoubleButton_DoublesLoop) {
TEST_F(LoopingControlTest, LoopScale_DoublesLoop) {
seekToSampleAndProcess(0);
m_pButtonLoopIn->set(1);
m_pButtonLoopIn->set(0);
Expand All @@ -345,16 +343,42 @@ TEST_F(LoopingControlTest, LoopDoubleButton_DoublesLoop) {
m_pButtonLoopOut->set(0);
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(500, m_pLoopEndPoint->get());
m_pButtonLoopDouble->slotSet(1);
m_pButtonLoopDouble->slotSet(0);
m_pLoopScale->set(2.0);
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(1000, m_pLoopEndPoint->get());
m_pButtonLoopDouble->slotSet(1);
m_pButtonLoopDouble->slotSet(0);
m_pLoopScale->set(2.0);
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(2000, m_pLoopEndPoint->get());
}

TEST_F(LoopingControlTest, LoopScale_HalvesLoop) {
m_pLoopStartPoint->slotSet(0);
m_pLoopEndPoint->slotSet(2000);
seekToSampleAndProcess(1800);
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(2000, m_pLoopEndPoint->get());
EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample());
EXPECT_FALSE(isLoopEnabled());
m_pLoopScale->set(0.5);
ProcessBuffer();
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(1000, m_pLoopEndPoint->get());

// The loop was not enabled so halving the loop should not move the playhead
// even though it is outside the loop.
EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample());

m_pButtonReloopToggle->slotSet(1);
EXPECT_TRUE(isLoopEnabled());
m_pLoopScale->set(0.5);
ProcessBuffer();
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(500, m_pLoopEndPoint->get());
// Since the current sample was out of range of the new loop,
// the current sample should reseek based on the new loop size.
EXPECT_EQ(300, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample());
}

TEST_F(LoopingControlTest, LoopDoubleButton_IgnoresPastTrackEnd) {
seekToSampleAndProcess(50);
m_pLoopStartPoint->slotSet(kTrackLengthSamples / 2.0);
Expand All @@ -377,18 +401,19 @@ TEST_F(LoopingControlTest, LoopDoubleButton_DoublesBeatloopSize) {
EXPECT_EQ(32.0, m_pBeatLoopSize->get());
}

TEST_F(LoopingControlTest, LoopDoubleButton_DoesNotDoubleBeatloopSizeForManualLoop) {
m_pTrack1->setBpm(120.0);
m_pBeatLoopSize->set(8.0);
TEST_F(LoopingControlTest, LoopDoubleButton_DoesNotResizeManualLoop) {
seekToSampleAndProcess(500);
m_pButtonLoopIn->set(1.0);
m_pButtonLoopIn->set(0.0);
seekToSampleAndProcess(1000);
m_pButtonLoopOut->set(1.0);
m_pButtonLoopOut->set(0.0);
EXPECT_EQ(500, m_pLoopStartPoint->get());
EXPECT_EQ(1000, m_pLoopEndPoint->get());
m_pButtonLoopDouble->slotSet(1);
m_pButtonLoopDouble->slotSet(0);
EXPECT_EQ(8.0, m_pBeatLoopSize->get());
EXPECT_EQ(500, m_pLoopStartPoint->get());
EXPECT_EQ(1000, m_pLoopEndPoint->get());
}

TEST_F(LoopingControlTest, LoopDoubleButton_UpdatesNumberedBeatloopActivationControls) {
Expand All @@ -405,36 +430,6 @@ TEST_F(LoopingControlTest, LoopDoubleButton_UpdatesNumberedBeatloopActivationCon
EXPECT_TRUE(m_pBeatLoop4Enabled->toBool());
}

TEST_F(LoopingControlTest, LoopHalveButton_HalvesLoop) {
m_pLoopStartPoint->slotSet(0);
m_pLoopEndPoint->slotSet(2000);
seekToSampleAndProcess(1800);
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(2000, m_pLoopEndPoint->get());
EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample());
EXPECT_FALSE(isLoopEnabled());
m_pButtonLoopHalve->slotSet(1);
m_pButtonLoopHalve->slotSet(0);
ProcessBuffer();
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(1000, m_pLoopEndPoint->get());

// The loop was not enabled so halving the loop should not move the playhead
// even though it is outside the loop.
EXPECT_EQ(1800, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample());

m_pButtonReloopToggle->slotSet(1);
EXPECT_TRUE(isLoopEnabled());
m_pButtonLoopHalve->slotSet(1);
m_pButtonLoopHalve->slotSet(0);
ProcessBuffer();
EXPECT_EQ(0, m_pLoopStartPoint->get());
EXPECT_EQ(500, m_pLoopEndPoint->get());
// Since the current sample was out of range of the new loop,
// the current sample should reseek based on the new loop size.
EXPECT_EQ(300, m_pChannel1->getEngineBuffer()->m_pLoopingControl->getCurrentSample());
}

TEST_F(LoopingControlTest, LoopHalveButton_IgnoresTooSmall) {
ProcessBuffer();
m_pLoopStartPoint->slotSet(0);
Expand All @@ -457,18 +452,19 @@ TEST_F(LoopingControlTest, LoopHalveButton_HalvesBeatloopSize) {
EXPECT_EQ(32.0, m_pBeatLoopSize->get());
}

TEST_F(LoopingControlTest, LoopHalveButton_DoesNotHalveBeatloopSizeForManualLoop) {
m_pTrack1->setBpm(120.0);
m_pBeatLoopSize->set(64.0);
TEST_F(LoopingControlTest, LoopHalveButton_DoesNotResizeManualLoop) {
seekToSampleAndProcess(500);
m_pButtonLoopIn->set(1.0);
m_pButtonLoopIn->set(0.0);
seekToSampleAndProcess(1000);
m_pButtonLoopOut->set(1.0);
m_pButtonLoopOut->set(0.0);
EXPECT_EQ(500, m_pLoopStartPoint->get());
EXPECT_EQ(1000, m_pLoopEndPoint->get());
m_pButtonLoopHalve->slotSet(1);
m_pButtonLoopHalve->slotSet(0);
EXPECT_EQ(64.0, m_pBeatLoopSize->get());
EXPECT_EQ(500, m_pLoopStartPoint->get());
EXPECT_EQ(1000, m_pLoopEndPoint->get());
}

TEST_F(LoopingControlTest, LoopHalveButton_UpdatesNumberedBeatloopActivationControls) {
Expand Down Expand Up @@ -716,64 +712,21 @@ TEST_F(LoopingControlTest, BeatLoopSize_ValueChangeResizesBeatLoop) {
TEST_F(LoopingControlTest, BeatLoopSize_ValueChangeDoesNotResizeManualLoop) {
seekToSampleAndProcess(50);
m_pTrack1->setBpm(160.0);
m_pQuantizeEnabled->set(0);
m_pBeatLoopSize->set(4.0);
m_pButtonLoopIn->slotSet(1);
m_pButtonLoopIn->slotSet(0);
seekToSampleAndProcess(500);
m_pButtonLoopOut->slotSet(1);
m_pButtonLoopOut->slotSet(0);
EXPECT_TRUE(m_pLoopEnabled->toBool());
double oldLoopStart = m_pLoopStartPoint->get();
double oldLoopEnd = m_pLoopEndPoint->get();
double oldLoopLength = oldLoopEnd - oldLoopStart;

m_pButtonBeatLoopToggle->set(1.0);
m_pButtonBeatLoopToggle->set(0.0);
EXPECT_FALSE(m_pLoopEnabled->toBool());
m_pBeatLoopSize->set(8.0);

double newLoopStart = m_pLoopStartPoint->get();
double newLoopEnd = m_pLoopEndPoint->get();
double newLoopLength = newLoopEnd - newLoopStart;
EXPECT_EQ(oldLoopStart, newLoopStart);
EXPECT_EQ(oldLoopEnd, newLoopEnd);
EXPECT_EQ(oldLoopLength, newLoopLength);
}

TEST_F(LoopingControlTest, BeatLoopDoubleButton_DoublesBeatloopSize) {
m_pTrack1->setBpm(120.0);
m_pBeatLoopSize->set(3.0);
m_pButtonBeatLoopDouble->set(1.0);
m_pButtonBeatLoopDouble->set(0.0);
EXPECT_EQ(6.0, m_pBeatLoopSize->get());
}

TEST_F(LoopingControlTest, BeatLoopDoubleButton_DoublesBeatloopSizeWhenNoLoopIsSet) {
m_pTrack1->setBpm(120.0);
m_pBeatLoopSize->set(64.0);
m_pLoopStartPoint->set(kNoTrigger);
m_pLoopEndPoint->set(kNoTrigger);
m_pButtonBeatLoopDouble->slotSet(1);
m_pButtonBeatLoopDouble->slotSet(0);
EXPECT_EQ(128.0, m_pBeatLoopSize->get());
}

TEST_F(LoopingControlTest, BeatLoopHalveButton_HalvesBeatloopSize) {
m_pTrack1->setBpm(120.0);
m_pBeatLoopSize->set(6.0);
m_pButtonBeatLoopHalve->set(1.0);
m_pButtonBeatLoopHalve->set(0.0);
EXPECT_EQ(3.0, m_pBeatLoopSize->get());
}

TEST_F(LoopingControlTest, BeatLoopHalveButton_HalvesBeatloopSizeWhenNoLoopIsSet) {
m_pTrack1->setBpm(120.0);
m_pBeatLoopSize->set(64.0);
m_pLoopStartPoint->set(kNoTrigger);
m_pLoopEndPoint->set(kNoTrigger);
m_pButtonBeatLoopHalve->slotSet(1);
m_pButtonBeatLoopHalve->slotSet(0);
EXPECT_EQ(32.0, m_pBeatLoopSize->get());
}

TEST_F(LoopingControlTest, LegacyBeatLoopControl) {
Expand Down
Loading

0 comments on commit 2c129ea

Please sign in to comment.