Skip to content

Preferences: show 'real' xfader configuration#14124

Merged
Swiftb0y merged 2 commits intomixxxdj:mainfrom
ronso0:pref-xfader-from-controls
Apr 29, 2025
Merged

Preferences: show 'real' xfader configuration#14124
Swiftb0y merged 2 commits intomixxxdj:mainfrom
ronso0:pref-xfader-from-controls

Conversation

@ronso0
Copy link
Copy Markdown
Member

@ronso0 ronso0 commented Jan 6, 2025

This makes the Mixer preferences load the xfader configuration

  • from config on first load (if all xfader controls are still at their default values¹)
  • from controls each following update

This means touching the usual 3-state switch or curve knob on a controller will update the xfader graphic immedtiately.

¹Since mappings are loaded before DlgPreferences is instantiated this is supposed to detect if mappings have already set the xfader config in their init(), either explicitly or received updates via inputReport request.

Will do another test soon with the S4 Mk3 mapping which requests a status report on startup to sync all controls.

If someone wants to test it without controller, here's a mapping for Vir-MIDI or MIDI Through
https://gist.github.com/ronso0/ae7a6f3fc2b28e43f16bf50bfb84e62e

Comment thread src/preferences/dialog/dlgprefmixer.cpp Outdated
Comment on lines +45 to +52
const ConfigKey kXfaderModeKey = ConfigKey(EngineXfader::kXfaderGroup,
EngineXfader::kXfaderModeKey);
const ConfigKey kXfaderCurveKey = ConfigKey(EngineXfader::kXfaderGroup,
EngineXfader::kXfaderCurveKey);
const ConfigKey kXfaderCalibrationKey = ConfigKey(EngineXfader::kXfaderGroup,
EngineXfader::kXfaderCalibrationKey);
const ConfigKey kXfaderReverseKey = ConfigKey(EngineXfader::kXfaderGroup,
EngineXfader::kXfaderReverseKey);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is UB and will very likely cause crashes on startup. https://en.cppreference.com/w/cpp/language/siof

Copy link
Copy Markdown
Member Author

@ronso0 ronso0 Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so IIUC this is not specific to the diff here but also applies to the state before?
Like EngineXfader::kXfaderConfigKey being defined and initialized in the namespace here?
And making those members of DlgPrefMixer and move them to the initializer list would avoid it, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or create a few getXYZCfgKey() helpers in dlgprefmixer.h?

Copy link
Copy Markdown
Member Author

@ronso0 ronso0 Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or simply define them in enginexfader.h like this:
static const QString kXfaderGroup = QStringLiteral("[Mixer Profile]");
?

meeh..

error: in-class initialization of static data member ‘const QString EngineXfader::kXfaderGroup’ of non-literal type

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, I use

const ConfigKey xfaderModeKey() {
    return ConfigKey(EngineXfader::kXfaderGroup, EngineXfader::kXfaderModeKey);
}

and alike in this namespace.
Should be fine since they're not called before DlgPrefMixer is constructed?
Please check my fixups.

(sorry I force-pushed earlier, some debug stuff slipped in which I had to remove)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is not that its defined in the namespace (please do and keep that), the problem is that you expose the symbol via the header and then other translation units initialize their static data based on the static data of another translation unit. The solution in both cases is either some "construct on first use" idiom (there are multiple ways to achieve that in C++) or just don't depend on static data from other translation units. So duplicate the QString across both translation units. Its ugly but thats what we get for using QStrings for everything...

@ronso0 ronso0 force-pushed the pref-xfader-from-controls branch 3 times, most recently from 6025dbe to f00cfec Compare January 7, 2025 09:13
Comment thread src/engine/enginexfader.h Outdated
Comment on lines +22 to +26
static const QString kXfaderGroup;
static const QString kXfaderModeKey;
static const QString kXfaderCurveKey;
static const QString kXfaderCalibrationKey;
static const QString kXfaderReverseKey;
Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the offenders. If you can come up with a solution that removes this static data declarations from the header, you should be good. Again, I recommend you simply duplicate the required QStrings/ConfigKeys across both files. The same technically applies to the other more trivial members (double and const char*) too, it just likely doesn't crash in the unitialized state because the code is able to handle the "unitialized" values. Its still UB though. Best would be to make them static constexpr and initialize right there in the header, but thats not an option for the QStrings.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation, though I'm at a loss: I read about Construct on first Use but explanations and tutorials only refer to obejcts/pointers and classes with a constructor, which is not the case for EngineXfader.
I'll revert the commit and stick with the duplicate QStrings.

Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple thoughts

Comment thread src/preferences/dialog/dlgprefmixer.cpp
Comment thread src/preferences/dialog/dlgprefmixer.cpp Outdated
Comment thread src/preferences/dialog/dlgprefmixer.cpp Outdated
Comment on lines 77 to +76
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),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proposal: Create a struct CrossfaderParameter in the class to bundle the calibration, transform, reverse and mode together instead of prefixing all of those with xFader?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean just for holding the parameters in the preferences?
Sure, we can do that. But tbh I'd like to get the mechanics right first before we start optimizing it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean just for holding the parameters in the preferences?

Yes, and for comparing whether the settings have changed and the display would need to be rerendered. Putting that into a struct and relying on the default operator== is easier/nicer than writing that comparision manually. But sure, we can postpone if you think thats raising complexity.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, let's postpone it.
Wanna file a request for it?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah, its no need to file an issue over code improvement. someone else will clean it up when they look at it again.

Comment thread src/preferences/dialog/dlgprefmixer.cpp Outdated
@ronso0 ronso0 force-pushed the pref-xfader-from-controls branch 2 times, most recently from 0057d9b to a7bd77b Compare February 6, 2025 13:21
@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 6, 2025

Sorry what? Why is CI failing on that one now?

Error: /home/runner/work/mixxx/mixxx/src/test/borrowabletest.cpp:4:10: error: Module QtConcurrent should not be included directly [-Wclazy-no-module-include]
4 | #include

@Swiftb0y
Copy link
Copy Markdown
Member

Swiftb0y commented Feb 6, 2025

not your fault. ignore clazy for now. #1713 (comment)

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 7, 2025

Alright, all works as desired now!

@Swiftb0y
Copy link
Copy Markdown
Member

Swiftb0y commented Feb 7, 2025

great. what next steps would you prefer? Remove the debugging statements, address my previous comments, etcc?

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 7, 2025

Testing! 😄
With real controllers, or a dummy mapping.
I've been using this: https://gist.github.com/ronso0/ae7a6f3fc2b28e43f16bf50bfb84e62e

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 7, 2025

then with 👍 remove the debug stuff

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 7, 2025

all comments addressed, except the struct.
I removed the timer, all good now with the no-op checks.

Fun fact: if I connect all CO changes to one slot and read all COs there, all (2-3) changes made by the mapping script are already applied, regardless: the slot is called 2-3 times.
So COs are update before preferences receive the first valueChanged signal..

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 7, 2025

Alright, all works as desired now!

whoops, not quite, widget update after controls change was missing.

@ronso0 ronso0 force-pushed the pref-xfader-from-controls branch from 72deec0 to 4c46a32 Compare February 7, 2025 13:33
@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 17, 2025

This has proven to be very handy recently:
already late into a session when I wanted to cut, I noticed that the S4's xfader switch must have been flipped when I've put it into its case, and I could quickly adjust and very the desired curve.
(some sort of popup would have been super nice in this case, so i don't have to open the preferences and navigate to Mixer anymore 🤔 bit like the key wheel window with a auto-hide timer)

edit popup is here #14361

@Swiftb0y
Copy link
Copy Markdown
Member

I agree. Do you intend to finish this then? There are still open review comments and the code is also still full of debug printing.

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Feb 17, 2025

I think I addressed all comments.

I'll squash and remove the debug commit once this has received some testing.

Comment thread src/preferences/dialog/dlgprefmixer.cpp Outdated
Comment on lines +1035 to +1013
m_xFaderMode = radioButtonConstantPower->isChecked()
? MIXXX_XFADER_CONSTPWR
: MIXXX_XFADER_ADDITIVE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be in the slider slot?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, good catch 👍

Comment thread src/preferences/dialog/dlgprefmixer.cpp Outdated
// wiped on shutdown.
m_xFaderCal = EngineXfader::getPowerCalibration(m_xFaderCurve);
m_xFaderMode = m_pConfig->getValue<int>(kXfaderModeKey);
m_xFaderReverse = m_pConfig->getValue<int>(kXfaderReverseKey) == 1;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
m_xFaderReverse = m_pConfig->getValue<int>(kXfaderReverseKey) == 1;
m_xFaderReverse = m_pConfig->getValue<bool>(kXfaderReverseKey);

or is there a reason otherwise?

Comment thread src/preferences/dialog/dlgprefmixer.cpp
@ronso0 ronso0 added this to the 2.6-beta milestone Feb 25, 2025
@github-project-automation github-project-automation Bot moved this to In progress in Releases Mar 25, 2025
@daschuer
Copy link
Copy Markdown
Member

@Swiftb0y Are all review findings solved?

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Apr 29, 2025

If added the testing mapping I was using for#14361

If someone wants to test it without controller, here's a mapping for Vir-MIDI or MIDI Through
https://gist.github.com/ronso0/ae7a6f3fc2b28e43f16bf50bfb84e62e

Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the qWarnings. I will test ASAP.

Comment thread src/preferences/dialog/dlgprefmixer.cpp
Comment thread src/preferences/dialog/dlgprefmixer.cpp Outdated
Comment on lines +884 to +874
SliderXFader->blockSignals(true);
buttonGroupCrossfaderModes->blockSignals(true);
checkBoxReverse->blockSignals(true);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets use a QSignalBlocker instead. Otherwise an exception or an inconsiderate future early return could cause the widgets to stay blocked.

@Swiftb0y
Copy link
Copy Markdown
Member

I will test ASAP.

Tested. Builds and works well afaict.

@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Apr 29, 2025

please remove the qWarnings. I will test ASAP.

Thanks!

I'll squash, remove the trace logging and force-push after LGTM.

Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you.

@ronso0 ronso0 force-pushed the pref-xfader-from-controls branch from 7dc4de0 to b9b5854 Compare April 29, 2025 13:15
@ronso0 ronso0 marked this pull request as draft April 29, 2025 13:17
@ronso0 ronso0 force-pushed the pref-xfader-from-controls branch 3 times, most recently from 23f7207 to 8e5bce5 Compare April 29, 2025 13:27
…ve changed

Initially check if the xfader controls have been touched already (at least one is not at default value anymore).
If not, load all from config.
Else load from controls.

Later on, update the widgets and xfader graphic when any control is touched, eg. by controller mappings.
@ronso0 ronso0 force-pushed the pref-xfader-from-controls branch from 8e5bce5 to 8297186 Compare April 29, 2025 13:31
@ronso0
Copy link
Copy Markdown
Member Author

ronso0 commented Apr 29, 2025

Sorry for the noise, I messed up the intertwined fixups.
Final rebase diff is https://github.com/mixxxdj/mixxx/compare/7dc4de0..829718639b45516f271ff11ddebd5acb693a9fbe

@ronso0 ronso0 marked this pull request as ready for review April 29, 2025 13:32
Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you. Waiting for CI.

@Swiftb0y Swiftb0y enabled auto-merge April 29, 2025 13:34
@Swiftb0y Swiftb0y merged commit 9c47653 into mixxxdj:main Apr 29, 2025
1 check passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in Releases Apr 29, 2025
@ronso0 ronso0 deleted the pref-xfader-from-controls branch April 29, 2025 13:55
@ronso0 ronso0 mentioned this pull request Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants