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

Preferences: show 'real' xfader configuration #14124

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
180 changes: 116 additions & 64 deletions src/preferences/dialog/dlgprefmixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <QButtonGroup>
#include <QPainterPath>
#include <QStandardItemModel>
#include <QTimer>

#include "control/controlobject.h"
#include "control/controlproxy.h"
Expand Down Expand Up @@ -41,12 +42,14 @@ const ConfigKey kLowEqFreqKey = ConfigKey(kMixerProfile, kLowEqFrequency);
const ConfigKey kLowEqFreqPreciseKey =
ConfigKey(kMixerProfile, QStringLiteral("LoEQFrequencyPrecise"));

const QString kXfaderCalibration = QStringLiteral("xFaderCalibration");

const ConfigKey kXfaderModeKey = ConfigKey(EngineXfader::kXfaderConfigKey,
QStringLiteral("xFaderMode"));
const ConfigKey kXfaderCurveKey = ConfigKey(EngineXfader::kXfaderConfigKey,
QStringLiteral("xFaderCurve"));
const ConfigKey kXfaderCalibrationKey = ConfigKey(EngineXfader::kXfaderConfigKey,
QStringLiteral("xFaderCalibration"));
kXfaderCalibration);
const ConfigKey kXfaderReverseKey = ConfigKey(EngineXfader::kXfaderConfigKey,
QStringLiteral("xFaderReverse"));

Expand All @@ -72,13 +75,14 @@ DlgPrefMixer::DlgPrefMixer(
: DlgPreferencePage(pParent),
m_pConfig(pConfig),
m_xFaderMode(MIXXX_XFADER_ADDITIVE),
m_transform(EngineXfader::kTransformDefault),
m_cal(0.0),
m_mode(kXfaderModeKey),
m_curve(kXfaderCurveKey),
m_calibration(kXfaderCalibrationKey),
m_reverse(kXfaderReverseKey),
m_crossfader("[Master]", "crossfader"),
m_xFaderTransform(EngineXfader::kTransformDefault),
m_xFaderCal(0.0),
m_xfModeCO(make_parented<ControlProxy>(kXfaderModeKey, this)),
m_xfCurveCO(make_parented<ControlProxy>(kXfaderCurveKey, this)),
m_xfReverseCO(make_parented<ControlProxy>(kXfaderReverseKey, this)),
m_xfCalibrationCO(
ConfigKey(EngineXfader::kXfaderConfigKey, kXfaderCalibration)),
m_crossfader(QStringLiteral("[Master]"), QStringLiteral("crossfader")),
m_xFaderReverse(false),
m_COLoFreq(kLowEqFreqKey),
m_COHiFreq(kHighEqFreqKey),
Expand Down Expand Up @@ -110,14 +114,40 @@ DlgPrefMixer::DlgPrefMixer(
connect(SliderXFader,
QOverload<int>::of(&QSlider::valueChanged),
this,
&DlgPrefMixer::slotUpdateXFader);
connect(SliderXFader, &QSlider::sliderMoved, this, &DlgPrefMixer::slotUpdateXFader);
connect(SliderXFader, &QSlider::sliderReleased, this, &DlgPrefMixer::slotUpdateXFader);
connect(radioButtonAdditive, &QRadioButton::clicked, this, &DlgPrefMixer::slotUpdateXFader);
&DlgPrefMixer::slotXFaderWidgetsChanged);
connect(SliderXFader, &QSlider::sliderMoved, this, &DlgPrefMixer::slotXFaderWidgetsChanged);
connect(SliderXFader, &QSlider::sliderReleased, this, &DlgPrefMixer::slotXFaderWidgetsChanged);
connect(radioButtonAdditive,
&QRadioButton::clicked,
this,
&DlgPrefMixer::slotXFaderWidgetsChanged);
connect(radioButtonConstantPower,
&QRadioButton::clicked,
this,
&DlgPrefMixer::slotUpdateXFader);
&DlgPrefMixer::slotXFaderWidgetsChanged);
connect(checkBoxReverse,
// TODO deprecated after Qt 6.9, replace with checkStateChanged(Qt::CheckState)
&QCheckBox::stateChanged,
this,
&DlgPrefMixer::slotXFaderReverseBoxToggled);

m_xfModeCO->connectValueChanged(this, &DlgPrefMixer::slotXFaderControlChanged);
m_xfCurveCO->connectValueChanged(this, &DlgPrefMixer::slotXFaderControlChanged);
m_xfReverseCO->connectValueChanged(this, &DlgPrefMixer::slotXFaderControlChanged);
// When e.g. controller mappings rapidly set multiple xfader controls we may
// get concurrent calls of slotUpdateXFaderFromConfigOrControls().
// In slotXFaderControlChanged() we start a timer to avoid that, only the last
// calls th update method.
connect(&m_xfaderControlUpdateTimer,
&QTimer::timeout,
this,
[this]() {
slotUpdateXFaderFromConfigOrControls();
// store new settings in config
applyXFader();
});
m_xfaderControlUpdateTimer.setSingleShot(true);
m_xfaderControlUpdateTimer.setInterval(200);

// Don't allow the xfader graph getting keyboard focus
graphicsViewXfader->setFocusPolicy(Qt::NoFocus);
Expand Down Expand Up @@ -192,7 +222,6 @@ DlgPrefMixer::DlgPrefMixer(
m_initializing = false;
}

// Create EQ & QuickEffect selectors and deck label for each added deck
void DlgPrefMixer::slotNumDecksChanged(double numDecks) {
while (m_deckEqEffectSelectors.size() < static_cast<int>(numDecks)) {
// 1-based for display
Expand Down Expand Up @@ -688,19 +717,7 @@ int DlgPrefMixer::getSliderPosition(double eqFreq, int minValue, int maxValue) {
}

void DlgPrefMixer::slotApply() {
// xfader //////////////////////////////////////////////////////////////////
m_mode.set(m_xFaderMode);
m_curve.set(m_transform);
m_calibration.set(m_cal);
if (checkBoxReverse->isChecked() != m_xFaderReverse) {
m_reverse.set(checkBoxReverse->isChecked());
double position = m_crossfader.get();
m_crossfader.set(0.0 - position);
m_xFaderReverse = checkBoxReverse->isChecked();
}
m_pConfig->set(kXfaderModeKey, ConfigValue(m_xFaderMode));
m_pConfig->set(kXfaderCurveKey, ConfigValue(QString::number(m_transform)));
m_pConfig->set(kXfaderReverseKey, ConfigValue(checkBoxReverse->isChecked() ? 1 : 0));
applyXFader();

// EQ & QuickEffect settings ///////////////////////////////////////////////
m_pConfig->set(kEnableEqsKey, ConfigValue(m_eqBypass ? 0 : 1));
Expand All @@ -717,6 +734,22 @@ void DlgPrefMixer::slotApply() {
storeEqShelves();
}

void DlgPrefMixer::applyXFader() {
m_xfModeCO->set(m_xFaderMode);
m_xfCurveCO->set(m_xFaderTransform);
m_xfReverseCO->set(m_xFaderReverse ? 1.0 : 0.0);
m_xfCalibrationCO.set(m_xFaderCal);
if (m_xFaderReverse != (m_xfReverseCO->get() > 0)) {
double position = m_crossfader.get();
m_crossfader.set(0.0 - position);
}
m_xfReverseCO->set(m_xFaderReverse ? 1.0 : 0.0);

m_pConfig->set(kXfaderModeKey, ConfigValue(m_xFaderMode));
m_pConfig->set(kXfaderCurveKey, ConfigValue(QString::number(m_xFaderTransform)));
m_pConfig->set(kXfaderReverseKey, ConfigValue(m_xFaderReverse ? 1 : 0));
}

void DlgPrefMixer::storeEqShelves() {
if (m_initializing) {
return;
Expand All @@ -726,30 +759,8 @@ void DlgPrefMixer::storeEqShelves() {
m_pConfig->set(kLowEqFreqPreciseKey, ConfigValue(QString::number(m_lowEqFreq, 'f')));
}

// Update the widgets with values from config / EffectsManager
void DlgPrefMixer::slotUpdate() {
// xfader //////////////////////////////////////////////////////////////////
m_transform = m_pConfig->getValue(kXfaderCurveKey, EngineXfader::kTransformDefault);

// Range SliderXFader 0 .. 100
double sliderVal = RescalerUtils::oneByXToLinear(
m_transform - EngineXfader::kTransformMin + 1,
EngineXfader::kTransformMax - EngineXfader::kTransformMin + 1,
SliderXFader->minimum(),
SliderXFader->maximum());
SliderXFader->setValue(static_cast<int>(std::round(sliderVal)));

m_xFaderMode = m_pConfig->getValueString(kXfaderModeKey).toInt();
if (m_xFaderMode == MIXXX_XFADER_CONSTPWR) {
radioButtonConstantPower->setChecked(true);
} else {
radioButtonAdditive->setChecked(true);
}

m_xFaderReverse = m_pConfig->getValueString(kXfaderReverseKey).toInt() == 1;
checkBoxReverse->setChecked(m_xFaderReverse);

slotUpdateXFader();
slotUpdateXFaderFromConfigOrControls();

// EQs & QuickEffects //////////////////////////////////////////////////////
QString eqsOnly = m_pConfig->getValueString(kEqsOnlyKey);
Expand Down Expand Up @@ -815,8 +826,46 @@ void DlgPrefMixer::slotUpdate() {
updateMainEQ();
}

// Draw the crossfader curve graph. Only needs to get drawn when a change
// has been made.
void DlgPrefMixer::slotUpdateXFaderFromConfigOrControls() {
// Read values from config only on first update if the xfader curve controls
// are still at their default values. This should detect if controller mappings
// (or skin attributes) have changed the xfader controls.
// Else and on later calls, always read the current state from controls.
if (m_initializing &&
m_xfCurveCO->get() == m_xfCurveCO->getDefault() &&
m_xfCalibrationCO.get() == m_xfCalibrationCO.getDefault() &&
m_xfModeCO->get() == m_xfModeCO->getDefault() &&
m_xfReverseCO->get() == m_xfReverseCO->getDefault()) {
m_xFaderTransform = m_pConfig->getValue(kXfaderCurveKey, EngineXfader::kTransformDefault);
m_xFaderMode = m_pConfig->getValueString(kXfaderModeKey).toInt();
m_xFaderReverse = m_pConfig->getValueString(kXfaderReverseKey).toInt() == 1;
} else {
// Update xfader from controls
m_xFaderTransform = m_xfCurveCO->get();
m_xFaderMode = static_cast<int>(m_xfModeCO->get());
m_xFaderReverse = static_cast<bool>(m_xfReverseCO->get());
}

checkBoxReverse->setChecked(m_xFaderReverse);

// Range SliderXFader 0 .. 100
double sliderVal = RescalerUtils::oneByXToLinear(
m_xFaderTransform - EngineXfader::kTransformMin + 1,
EngineXfader::kTransformMax - EngineXfader::kTransformMin + 1,
SliderXFader->minimum(),
SliderXFader->maximum());
// This triggers slotXFaderWidgetsChanged() which calculates
// m_xFaderCal and calls drawXfaderDisplay()
SliderXFader->setValue(static_cast<int>(std::round(sliderVal)));

// These also trigger slotXFaderWidgetsChanged()
if (m_xFaderMode == MIXXX_XFADER_CONSTPWR) {
radioButtonConstantPower->setChecked(true);
} else {
radioButtonAdditive->setChecked(true);
}
}

void DlgPrefMixer::drawXfaderDisplay() {
// Initialize or clear scene
if (m_pxfScene) {
Expand Down Expand Up @@ -878,10 +927,10 @@ void DlgPrefMixer::drawXfaderDisplay() {
for (int x = 1; x <= pointCount + 1; x++) {
CSAMPLE_GAIN gainL, gainR;
EngineXfader::getXfadeGains((-1. + (xfadeStep * (x - 1))),
m_transform,
m_cal,
m_xFaderTransform,
m_xFaderCal,
m_xFaderMode,
checkBoxReverse->isChecked(),
m_xFaderReverse,
&gainL,
&gainR);

Expand All @@ -908,14 +957,17 @@ void DlgPrefMixer::drawXfaderDisplay() {
graphicsViewXfader->repaint();
}

void DlgPrefMixer::slotUpdateXFader() {
if (radioButtonAdditive->isChecked()) {
m_xFaderMode = MIXXX_XFADER_ADDITIVE;
} else {
m_xFaderMode = MIXXX_XFADER_CONSTPWR;
}
void DlgPrefMixer::slotXFaderControlChanged() {
// calls slotUpdateXFaderFromConfigOrControls() on timeout
m_xfaderControlUpdateTimer.start();
}

void DlgPrefMixer::slotXFaderReverseBoxToggled() {
m_xFaderReverse = checkBoxReverse->isChecked();
}

// m_transform is in the range of 1 to 1000 while 50 % slider results
void DlgPrefMixer::slotXFaderWidgetsChanged() {
// m_xFaderTransform is in the range of 1 to 1000 while 50 % slider results
// to ~2, which represents a medium rounded fader curve.
double transform = RescalerUtils::linearToOneByX(
SliderXFader->value(),
Expand All @@ -924,8 +976,8 @@ void DlgPrefMixer::slotUpdateXFader() {
EngineXfader::kTransformMax) -
1 + EngineXfader::kTransformMin;
// Round to 4 decimal places to avoid round-trip offsets with default 1.0
m_transform = std::round(transform * 10000) / 10000;
m_cal = EngineXfader::getPowerCalibration(m_transform);
m_xFaderTransform = std::round(transform * 10000) / 10000;
m_xFaderCal = EngineXfader::getPowerCalibration(m_xFaderTransform);

drawXfaderDisplay();
}
Expand Down
29 changes: 20 additions & 9 deletions src/preferences/dialog/dlgprefmixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
class QComboBox;
class QWidget;
class EffectsManager;
class QTimer;

class DlgPrefMixer : public DlgPreferencePage, public Ui::DlgPrefMixerDlg {
Q_OBJECT
Expand All @@ -26,10 +27,12 @@ class DlgPrefMixer : public DlgPreferencePage, public Ui::DlgPrefMixerDlg {

public slots:
void slotApply() override;
/// Update the widgets with values from config / EffectsManager
void slotUpdate() override;
void slotResetToDefaults() override;

private slots:
/// Create EQ & QuickEffect selectors and deck label for each added deck
void slotNumDecksChanged(double numDecks);
void slotEQEffectSelectionChanged(int effectIndex);
void slotQuickEffectSelectionChanged(int effectIndex);
Expand All @@ -41,16 +44,20 @@ class DlgPrefMixer : public DlgPreferencePage, public Ui::DlgPrefMixerDlg {
void slotStemAutoResetToggled(bool checked);
#endif
void slotBypassEqToggled(bool checked);
// Create, populate and show/hide EQ & QuickEffect selectors, considering the
// number of decks and the 'Single EQ' checkbox
/// Create, populate and show/hide EQ & QuickEffect selectors, considering the
/// number of decks and the 'Single EQ' checkbox
void slotPopulateDeckEqSelectors();
void slotPopulateQuickEffectSelectors();

void slotUpdateXFader();
void slotUpdateXFaderFromConfigOrControls();
void slotXFaderReverseBoxToggled();
void slotXFaderControlChanged();
void slotXFaderWidgetsChanged();

void slotHiEqSliderChanged();
void slotLoEqSliderChanged();

// Update the Main EQ
/// Update the Main EQ
void slotMainEQParameterSliderChanged(int value);
void slotMainEQToDefault();
void slotMainEqEffectChanged(int effectIndex);
Expand All @@ -63,6 +70,8 @@ class DlgPrefMixer : public DlgPreferencePage, public Ui::DlgPrefMixerDlg {
int getSliderPosition(double eqFreq, int minimum, int maximum);
void validateEQShelves();

void applyXFader();

void applyDeckEQs();
void applyQuickEffects();

Expand All @@ -79,12 +88,12 @@ class DlgPrefMixer : public DlgPreferencePage, public Ui::DlgPrefMixerDlg {

// X-fader values
int m_xFaderMode;
double m_transform, m_cal;
double m_xFaderTransform, m_xFaderCal;

PollingControlProxy m_mode;
PollingControlProxy m_curve;
PollingControlProxy m_calibration;
PollingControlProxy m_reverse;
parented_ptr<ControlProxy> m_xfModeCO;
parented_ptr<ControlProxy> m_xfCurveCO;
parented_ptr<ControlProxy> m_xfReverseCO;
PollingControlProxy m_xfCalibrationCO;
PollingControlProxy m_crossfader;

bool m_xFaderReverse;
Expand Down Expand Up @@ -123,4 +132,6 @@ class DlgPrefMixer : public DlgPreferencePage, public Ui::DlgPrefMixerDlg {

QList<int> m_eqIndiciesOnUpdate;
QList<int> m_quickEffectIndiciesOnUpdate;

QTimer m_xfaderControlUpdateTimer;
};
Loading