From ae7079212c7dfe3a28b07b0998e0865da853c796 Mon Sep 17 00:00:00 2001 From: Lukas Waslowski Date: Fri, 3 May 2024 13:45:09 +0000 Subject: [PATCH] AutoDJProcessor: Add option to reset the crossfader to neutral --- src/library/autodj/autodjprocessor.cpp | 86 +++++++++++++++++----- src/library/autodj/autodjprocessor.h | 1 + src/preferences/dialog/dlgprefautodj.cpp | 13 ++++ src/preferences/dialog/dlgprefautodj.h | 1 + src/preferences/dialog/dlgprefautodjdlg.ui | 59 +++++++++++++++ 5 files changed, 141 insertions(+), 19 deletions(-) diff --git a/src/library/autodj/autodjprocessor.cpp b/src/library/autodj/autodjprocessor.cpp index 0910d6f9b89..7f46593380b 100644 --- a/src/library/autodj/autodjprocessor.cpp +++ b/src/library/autodj/autodjprocessor.cpp @@ -14,12 +14,17 @@ namespace { const char* kTransitionPreferenceName = "Transition"; const char* kTransitionModePreferenceName = "TransitionMode"; +const char* kResetFaderPreferenceName = "ResetFaderToNeutralOnIdle"; constexpr double kTransitionPreferenceDefault = 10.0; constexpr double kKeepPosition = -1.0; // A track needs to be longer than two callbacks to not stop AutoDJ constexpr double kMinimumTrackDurationSec = 0.2; +constexpr double kCrossfaderLeftOnly = -1.0; +constexpr double kCrossfaderNeutral = 0.0; +constexpr double kCrossfaderRightOnly = 1.0; + constexpr bool sDebug = false; } // anonymous namespace @@ -209,6 +214,26 @@ void AutoDJProcessor::setCrossfader(double value) { m_pCOCrossfader->set(value); } +void AutoDJProcessor::setCrossfaderToIdle(double value) { + DEBUG_ASSERT(value == kCrossfaderLeftOnly || value == kCrossfaderRightOnly); + + // Depending on the user's preferences, the idle position + // of the crossfader is either fully to the left/right, + // or in the middle. + const bool resetFaderToNeutralOnIdle = m_pConfig->getValue( + ConfigKey(kConfigKey, kResetFaderPreferenceName), + false); + + if (resetFaderToNeutralOnIdle) { + // Move crossfader to neutral. Crossfader will be moved + // to the left/right just before starting a crossfade. + setCrossfader(kCrossfaderNeutral); + } else { + // Move crossfader fully to the left/right + setCrossfader(value); + } +} + AutoDJProcessor::AutoDJError AutoDJProcessor::shufflePlaylist( const QModelIndexList& selectedIndices) { QModelIndex exclude; @@ -543,8 +568,11 @@ AutoDJProcessor::AutoDJError AutoDJProcessor::toggleAutoDJ(bool enable) { // playerPositionChanged for deck1 after the track is loaded. m_eState = ADJ_ENABLE_P1LOADED; - // Move crossfader to the left. - setCrossfader(-1.0); + // Move crossfader to its idle position (either to the left, + // or in the middle, depending on the user's preferences). + // We will set it fully to the left just before starting + // a crossfade anyway. + setCrossfaderToIdle(kCrossfaderLeftOnly); // Load track into the left deck and play. Once it starts playing, // we will receive a playerPositionChanged update for deck 1 which @@ -557,13 +585,21 @@ AutoDJProcessor::AutoDJError AutoDJProcessor::toggleAutoDJ(bool enable) { if (leftDeckPlaying) { // Load track into the right deck. emitLoadTrackToPlayer(nextTrack, pRightDeck->group, false); - // Move crossfader to the left. - setCrossfader(-1.0); + + // Move crossfader to its idle position (either to the left, + // or in the middle, depending on the user's preferences). + // We will set it fully to the left just before starting + // a crossfade anyway. + setCrossfaderToIdle(kCrossfaderLeftOnly); } else { // Load track into the left deck. emitLoadTrackToPlayer(nextTrack, pLeftDeck->group, false); - // Move crossfader to the right. - setCrossfader(1.0); + + // Move crossfader to its idle position (either to the right, + // or in the middle, depending on the user's preferences). + // We will set it fully to the right just before starting + // a crossfade anyway. + setCrossfaderToIdle(kCrossfaderRightOnly); } } emitAutoDJStateChanged(m_eState); @@ -742,12 +778,11 @@ void AutoDJProcessor::playerPositionChanged(DeckAttributes* pAttributes, // If the user stops the toDeck during a fade, let the fade continue // and do not load the next track. if (!otherDeckPlaying && otherDeck->isFromDeck) { - // Force crossfader all the way to the (non fading) toDeck. - if (m_eState == ADJ_RIGHT_FADING) { - setCrossfader(-1.0); - } else { - setCrossfader(1.0); - } + // Force crossfader all the way to the (non fading) toDeck, + // or to the middle, depending on the user's preferences. + setCrossfaderToIdle(m_eState == ADJ_RIGHT_FADING + ? kCrossfaderLeftOnly + : kCrossfaderRightOnly); m_eState = ADJ_IDLE; // Invalidate threshold calculated for the old otherDeck // This avoids starting a fade back before the new track is @@ -804,12 +839,25 @@ void AutoDJProcessor::playerPositionChanged(DeckAttributes* pAttributes, otherDeck->setPlayPosition(otherDeck->startPos); } - if (!otherDeckPlaying) { - otherDeck->play(); - } + const bool resetFaderToNeutralOnIdle = m_pConfig->getValue( + ConfigKey(kConfigKey, kResetFaderPreferenceName), + false); if (thisDeck->fadeBeginPos >= thisDeck->fadeEndPos) { - setCrossfader(thisDeck->isLeft() ? 1.0 : -1.0); + // This deck has an invalid fade position, so we + // immediately switch over to the other deck. + setCrossfader(thisDeck->isLeft() ? kCrossfaderRightOnly : kCrossfaderLeftOnly); + } else if (!otherDeckPlaying && resetFaderToNeutralOnIdle) { + // The user has requested the crossfader to be reset to + // neutral as long as no automatic crossfade is in progress + // (which is handled by setCrossfaderToIdle), so we need + // to set up the crossfader here instead, right before + // starting the fade. + setCrossfader(thisDeck->isLeft() ? kCrossfaderLeftOnly : kCrossfaderRightOnly); + } + + if (!otherDeckPlaying) { + otherDeck->play(); } // Now that we have started the other deck playing, remove the track @@ -827,9 +875,9 @@ void AutoDJProcessor::playerPositionChanged(DeckAttributes* pAttributes, double crossfaderTarget; if (m_eState == ADJ_LEFT_FADING) { - crossfaderTarget = 1.0; + crossfaderTarget = kCrossfaderRightOnly; } else if (m_eState == ADJ_RIGHT_FADING) { - crossfaderTarget = -1.0; + crossfaderTarget = kCrossfaderLeftOnly; } else { // this happens if the not playing track is cued into the outro region, // calculated for the swapped roles. @@ -847,7 +895,7 @@ void AutoDJProcessor::playerPositionChanged(DeckAttributes* pAttributes, m_transitionProgress = 1.0; // Note: If the user has stopped the toDeck during the transition. // this deck just stops as well. In this case a stopped AutoDJ is accepted - // because the use did it intentionally + // because the user did it intentionally } else { // We are in Fading state. // Calculate the current transitionProgress, the place between begin diff --git a/src/library/autodj/autodjprocessor.h b/src/library/autodj/autodjprocessor.h index 600cf7bfb70..87efdaa266b 100644 --- a/src/library/autodj/autodjprocessor.h +++ b/src/library/autodj/autodjprocessor.h @@ -242,6 +242,7 @@ class AutoDJProcessor : public QObject { // every time) double getCrossfader() const; void setCrossfader(double value); + void setCrossfaderToIdle(double value); // Following functions return seconds computed from samples or -1 if // track in deck has invalid sample rate (<= 0) diff --git a/src/preferences/dialog/dlgprefautodj.cpp b/src/preferences/dialog/dlgprefautodj.cpp index 7842318bf60..1761d0873e9 100644 --- a/src/preferences/dialog/dlgprefautodj.cpp +++ b/src/preferences/dialog/dlgprefautodj.cpp @@ -8,6 +8,14 @@ DlgPrefAutoDJ::DlgPrefAutoDJ(QWidget* pParent, m_pConfig(pConfig) { setupUi(this); + // Whether to reset the crossfader to neutral when not fading + ResetFaderToNeutralOnIdleCheckBox->setChecked(m_pConfig->getValue( + ConfigKey("[Auto DJ]", "ResetFaderToNeutralOnIdle"), false)); + connect(ResetFaderToNeutralOnIdleCheckBox, + &QCheckBox::stateChanged, + this, + &DlgPrefAutoDJ::slotToggleResetFaderToNeutralOnIdle); + // The minimum available for randomly-selected tracks MinimumAvailableSpinBox->setValue( m_pConfig->getValue( @@ -154,6 +162,11 @@ void DlgPrefAutoDJ::slotToggleRequeueIgnore(int buttonState) { RequeueIgnoreTimeEdit->setEnabled(checked); } +void DlgPrefAutoDJ::slotToggleResetFaderToNeutralOnIdle(int buttonState) { + bool checked = buttonState == Qt::Checked; + m_pConfig->setValue(ConfigKey("[Auto DJ]", "ResetFaderToNeutralOnIdle"), checked); +} + void DlgPrefAutoDJ::slotSetRequeueIgnoreTime(const QTime& a_rTime) { QString str = a_rTime.toString(RequeueIgnoreTimeEdit->displayFormat()); m_pConfig->set(ConfigKey("[Auto DJ]", "IgnoreTimeBuff"), str); diff --git a/src/preferences/dialog/dlgprefautodj.h b/src/preferences/dialog/dlgprefautodj.h index cc5e28fde06..054cf304f36 100644 --- a/src/preferences/dialog/dlgprefautodj.h +++ b/src/preferences/dialog/dlgprefautodj.h @@ -20,6 +20,7 @@ class DlgPrefAutoDJ : public DlgPreferencePage, public Ui::DlgPrefAutoDJDlg { private slots: void slotSetMinimumAvailable(int); void slotToggleRequeueIgnore(int buttonState); + void slotToggleResetFaderToNeutralOnIdle(int buttonState); void slotSetRequeueIgnoreTime(const QTime& a_rTime); void slotSetRandomQueueMin(int); void slotConsiderRepeatPlaylistState(bool); diff --git a/src/preferences/dialog/dlgprefautodjdlg.ui b/src/preferences/dialog/dlgprefautodjdlg.ui index c84dab445dd..585555bac82 100644 --- a/src/preferences/dialog/dlgprefautodjdlg.ui +++ b/src/preferences/dialog/dlgprefautodjdlg.ui @@ -15,6 +15,65 @@ + + + + Crossfader + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 0 + 0 + + + + Check to always reset the crossfader to neutral when no fade is in progress. + + + Reset fader to neutral after crossfading + + + ResetFaderToNeutralOnIdleCheckBox + + + + + + + + Check to always reset the crossfader to neutral when no fade is in progress. + + + + + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + +