diff --git a/src/engine/controls/loopingcontrol.cpp b/src/engine/controls/loopingcontrol.cpp index f5a099790c70..e8589b665a26 100644 --- a/src/engine/controls/loopingcontrol.cpp +++ b/src/engine/controls/loopingcontrol.cpp @@ -9,6 +9,7 @@ #include "engine/controls/ratecontrol.h" #include "engine/enginebuffer.h" #include "moc_loopingcontrol.cpp" +#include "preferences/dialog/dlgprefdeck.h" #include "preferences/usersettings.h" #include "track/track.h" #include "util/compatibility/qatomic.h" @@ -214,6 +215,28 @@ LoopingControl::LoopingControl(const QString& group, connect(m_pCOLoopMove, &ControlObject::valueChanged, this, &LoopingControl::slotLoopMove, Qt::DirectConnection); + m_pCOLoopMoveSize = new ControlObject(ConfigKey(group, "loopmove_size"), + true, + false, + false, + 4.0); + m_pCOLoopMoveSize->connectValueChangeRequest(this, + &LoopingControl::slotLoopMoveSizeChangeRequest, + Qt::DirectConnection); + + m_pCOLoopMoveForward = new ControlPushButton(ConfigKey(group, "loopmove_forward")); + m_pCOLoopMoveForward->setKbdRepeatable(true); + connect(m_pCOLoopMoveForward, + &ControlObject::valueChanged, + this, + &LoopingControl::slotLoopMoveForward); + m_pCOLoopMoveBackward = new ControlPushButton(ConfigKey(group, "loopmove_backward")); + m_pCOLoopMoveBackward->setKbdRepeatable(true); + connect(m_pCOLoopMoveBackward, + &ControlObject::valueChanged, + this, + &LoopingControl::slotLoopMoveBackward); + // Create loop_move_(SIZE) CO's which all call loop_move, but with a set // value. for (unsigned int i = 0; i < (sizeof(s_dBeatSizes) / sizeof(s_dBeatSizes[0])); ++i) { @@ -287,6 +310,9 @@ LoopingControl::~LoopingControl() { } delete m_pCOLoopMove; + delete m_pCOLoopMoveSize; + delete m_pCOLoopMoveForward; + delete m_pCOLoopMoveBackward; while (!m_loopMoves.isEmpty()) { LoopMoveControl* pLoopMove = m_loopMoves.takeLast(); delete pLoopMove; @@ -1769,7 +1795,11 @@ void LoopingControl::slotBeatJump(double beats) { LoopInfo loopInfo = m_loopInfo.getValue(); const auto currentPosition = m_currentPosition.getValue(); - if (m_bLoopingEnabled && !m_bAdjustingLoopIn && !m_bAdjustingLoopOut && + bool beatjumpDoesLoopmove = m_pConfig->getValue( + ConfigKey(QStringLiteral("[Controls]"), QStringLiteral("BeatjumpDoesLoopmove")), + kDefaultBeatjumpDoesLoopmove); + if (m_bLoopingEnabled && beatjumpDoesLoopmove && + !m_bAdjustingLoopIn && !m_bAdjustingLoopOut && loopInfo.startPosition <= currentPosition && loopInfo.endPosition >= currentPosition) { // If inside an active loop, move loop @@ -1796,6 +1826,19 @@ void LoopingControl::slotBeatJumpSizeChangeRequest(double beats) { m_pCOBeatJumpSize->setAndConfirm(beats); } +void LoopingControl::slotLoopMoveSizeChangeRequest(double beats) { + // Use same limits as for beat loop size + double maxBeatJumpSize = s_dBeatSizes[sizeof(s_dBeatSizes) / sizeof(s_dBeatSizes[0]) - 1]; + double minBeatJumpSize = s_dBeatSizes[0]; + + if ((beats < minBeatJumpSize) || (beats > maxBeatJumpSize)) { + // Don't clamp the value here to not fall out of a measure + return; + } + + m_pCOLoopMoveSize->setAndConfirm(beats); +} + void LoopingControl::slotBeatJumpSizeHalve(double pressed) { if (pressed > 0) { m_pCOBeatJumpSize->set(m_pCOBeatJumpSize->get() / 2); @@ -1820,6 +1863,18 @@ void LoopingControl::slotBeatJumpBackward(double pressed) { } } +void LoopingControl::slotLoopMoveForward(double pressed) { + if (pressed > 0) { + slotLoopMove(m_pCOBeatJumpSize->get()); + } +} + +void LoopingControl::slotLoopMoveBackward(double pressed) { + if (pressed > 0) { + slotLoopMove(-1.0 * m_pCOBeatJumpSize->get()); + } +} + void LoopingControl::slotLoopMove(double beats) { const mixxx::BeatsPointer pBeats = m_pBeats; if (!pBeats || beats == 0) { diff --git a/src/engine/controls/loopingcontrol.h b/src/engine/controls/loopingcontrol.h index e83f2f022d0f..3beecb8bd0c4 100644 --- a/src/engine/controls/loopingcontrol.h +++ b/src/engine/controls/loopingcontrol.h @@ -150,6 +150,9 @@ class LoopingControl : public EngineControl { // Move the loop by beats. void slotLoopMove(double beats); + void slotLoopMoveSizeChangeRequest(double beats); + void slotLoopMoveForward(double pressed); + void slotLoopMoveBackward(double pressed); void slotLoopScale(double scaleFactor); void slotLoopDouble(double pressed); @@ -258,6 +261,9 @@ class LoopingControl : public EngineControl { QList m_beatJumps; ControlObject* m_pCOLoopMove; + ControlObject* m_pCOLoopMoveSize; + ControlPushButton* m_pCOLoopMoveForward; + ControlPushButton* m_pCOLoopMoveBackward; QList m_loopMoves; // objects below are written from an engine worker thread diff --git a/src/preferences/dialog/dlgprefdeck.cpp b/src/preferences/dialog/dlgprefdeck.cpp index 815ea168783e..1999f629267e 100644 --- a/src/preferences/dialog/dlgprefdeck.cpp +++ b/src/preferences/dialog/dlgprefdeck.cpp @@ -220,13 +220,23 @@ DlgPrefDeck::DlgPrefDeck(QWidget* parent, UserSettingsPointer pConfig) // Double-tap Load to clone a deck via keyboard or controller ([ChannelN],LoadSelectedTrack) m_bCloneDeckOnLoadDoubleTap = m_pConfig->getValue( - ConfigKey(kControlsGroup, QStringLiteral("CloneDeckOnLoadDoubleTap")), true); + ConfigKey(kControlsGroup, QStringLiteral("CloneDeckOnLoadDoubleTap")), + kDefaultCloneDeckOnLoad); checkBoxCloneDeckOnLoadDoubleTap->setChecked(m_bCloneDeckOnLoadDoubleTap); connect(checkBoxCloneDeckOnLoadDoubleTap, &QCheckBox::toggled, this, &DlgPrefDeck::slotCloneDeckOnLoadDoubleTapCheckbox); + m_bBeatjumpLoopmove = m_pConfig->getValue( + ConfigKey(kControlsGroup, QStringLiteral("BeatjumpDoesLoopmove")), + kDefaultBeatjumpDoesLoopmove); + checkboxBeatjumpLoopmove->setChecked(m_bBeatjumpLoopmove); + connect(checkboxBeatjumpLoopmove, + &QCheckBox::toggled, + this, + &DlgPrefDeck::slotBeatjumpLoopmove); + m_bRateDownIncreasesSpeed = m_pConfig->getValue( ConfigKey(kControlsGroup, QStringLiteral("RateDir")), kDefaultRateDirectionInverted); setRateDirectionForAllDecks(m_bRateDownIncreasesSpeed); @@ -448,7 +458,12 @@ void DlgPrefDeck::slotUpdate() { slotSetTrackTimeDisplay(m_pControlTrackTimeDisplay->get()); checkBoxCloneDeckOnLoadDoubleTap->setChecked(m_pConfig->getValue( - ConfigKey(kControlsGroup, QStringLiteral("CloneDeckOnLoadDoubleTap")), true)); + ConfigKey(kControlsGroup, QStringLiteral("CloneDeckOnLoadDoubleTap")), + kDefaultCloneDeckOnLoad)); + + checkboxBeatjumpLoopmove->setChecked(m_pConfig->getValue( + ConfigKey(kControlsGroup, QStringLiteral("BeatjumpDoesLoopmove")), + kDefaultBeatjumpDoesLoopmove)); double rateRange = m_rateRangeControls[0]->get(); int index = ComboBoxRateRange->findData(static_cast(rateRange * 100.0)); @@ -536,6 +551,8 @@ void DlgPrefDeck::slotResetToDefaults() { // Clone decks by double-tapping Load button. checkBoxCloneDeckOnLoadDoubleTap->setChecked(kDefaultCloneDeckOnLoad); + checkboxBeatjumpLoopmove->setChecked(kDefaultBeatjumpDoesLoopmove); + // Mixxx cue mode ComboBoxCueMode->setCurrentIndex(0); @@ -694,6 +711,10 @@ void DlgPrefDeck::slotLoadWhenDeckPlayingIndexChanged(int comboboxIndex) { comboBoxLoadWhenDeckPlaying->itemData(comboboxIndex).toInt()); } +void DlgPrefDeck::slotBeatjumpLoopmove(bool checked) { + m_bBeatjumpLoopmove = checked; +} + void DlgPrefDeck::slotApply() { m_pConfig->set(ConfigKey(kControlsGroup, QStringLiteral("SetIntroStartAtMainCue")), ConfigValue(m_bSetIntroStartAtMainCue)); @@ -720,6 +741,9 @@ void DlgPrefDeck::slotApply() { m_pConfig->setValue(ConfigKey(kControlsGroup, QStringLiteral("CloneDeckOnLoadDoubleTap")), m_bCloneDeckOnLoadDoubleTap); + m_pConfig->setValue(ConfigKey(kControlsGroup, QStringLiteral("BeatjumpDoesLoopmove")), + m_bBeatjumpLoopmove); + // Set rate range // Set the config value before setting the CO values in setRateRangeForAllDecks() // because a proxy in DlgPrefLibrary listens to [Channe1],rate_range changes diff --git a/src/preferences/dialog/dlgprefdeck.h b/src/preferences/dialog/dlgprefdeck.h index a2eb35149b3f..5de507aeb22d 100644 --- a/src/preferences/dialog/dlgprefdeck.h +++ b/src/preferences/dialog/dlgprefdeck.h @@ -16,6 +16,7 @@ class QWidget; namespace { constexpr bool kDefaultCloneDeckOnLoad = true; +constexpr bool kDefaultBeatjumpDoesLoopmove = true; } // namespace namespace { @@ -52,6 +53,7 @@ class DlgPrefDeck : public DlgPreferencePage, public Ui::DlgPrefDeckDlg { void slotSetTrackLoadMode(int comboboxIndex); void slotLoadWhenDeckPlayingIndexChanged(int comboboxIndex); void slotCloneDeckOnLoadDoubleTapCheckbox(bool); + void slotBeatjumpLoopmove(bool checked); void slotRateRampingModeLinearButton(bool); void slotRateRampSensitivitySlider(int); @@ -97,6 +99,7 @@ class DlgPrefDeck : public DlgPreferencePage, public Ui::DlgPrefDeckDlg { bool m_bSetIntroStartAtMainCue; bool m_bCloneDeckOnLoadDoubleTap; + bool m_bBeatjumpLoopmove; int m_iRateRangePercent; bool m_bRateDownIncreasesSpeed; diff --git a/src/preferences/dialog/dlgprefdeckdlg.ui b/src/preferences/dialog/dlgprefdeckdlg.ui index deca6e4b5b1b..16765e2b4181 100644 --- a/src/preferences/dialog/dlgprefdeckdlg.ui +++ b/src/preferences/dialog/dlgprefdeckdlg.ui @@ -192,6 +192,35 @@ You can always drag-and-drop tracks on screen to clone a deck. + + + + true + + + + + + Beatjump + + + false + + + checkboxBeatjumpLoopmove + + + + + + + When checked the beatjump controls move the active loop + + + Beatjump moves the active loop + + + @@ -669,6 +698,7 @@ You can always drag-and-drop tracks on screen to clone a deck. comboBoxLoadPoint comboBoxLoadWhenDeckPlaying checkBoxCloneDeckOnLoadDoubleTap + checkboxBeatjumpLoopmove ComboBoxRateRange checkBoxInvertSpeedSlider checkBoxResetPitch