Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoDJProcessor: Add option to reset the crossfader to neutral #13332

Closed
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
AutoDJProcessor: Add option to reset the crossfader to neutral
cr7pt0gr4ph7 committed May 3, 2024
commit ae7079212c7dfe3a28b07b0998e0865da853c796
86 changes: 67 additions & 19 deletions src/library/autodj/autodjprocessor.cpp
Original file line number Diff line number Diff line change
@@ -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<bool>(
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<bool>(
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
1 change: 1 addition & 0 deletions src/library/autodj/autodjprocessor.h
Original file line number Diff line number Diff line change
@@ -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)
13 changes: 13 additions & 0 deletions src/preferences/dialog/dlgprefautodj.cpp
Original file line number Diff line number Diff line change
@@ -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);
1 change: 1 addition & 0 deletions src/preferences/dialog/dlgprefautodj.h
Original file line number Diff line number Diff line change
@@ -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);
59 changes: 59 additions & 0 deletions src/preferences/dialog/dlgprefautodjdlg.ui
Original file line number Diff line number Diff line change
@@ -15,6 +15,65 @@
</property>
<layout class="QVBoxLayout" name="AutoDJGridLayout">

<item>
<widget class="QGroupBox" name="CrossfaderOptions">
<property name="title">
<string>Crossfader</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<layout class="QGridLayout" name="CrossfaderGridLayout">

<item row="0" column="0">
<widget class="QLabel" name="ResetFaderToNeutralOnIdleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Check to always reset the crossfader to neutral when no fade is in progress.</string>
</property>
<property name="text">
<string>Reset fader to neutral after crossfading</string>
</property>
<property name="buddy">
<cstring>ResetFaderToNeutralOnIdleCheckBox</cstring>
</property>
</widget>
</item>

<item row="0" column="1">
<widget class="QCheckBox" name="ResetFaderToNeutralOnIdleCheckBox">
<property name="toolTip">
<string>Check to always reset the crossfader to neutral when no fade is in progress.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>

<item row="1" column="2">
<spacer name="horizontalSpacerCrossfader">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</spacer>
</item>

</layout>
</widget>
</item>

<item>
<widget class="QGroupBox" name="RequeueOptions">
<property name="title">