Skip to content

Loudness Contour effect#1141

Merged
daschuer merged 5 commits intomixxxdj:masterfrom
daschuer:loudnessEffect
Jan 27, 2017
Merged

Loudness Contour effect#1141
daschuer merged 5 commits intomixxxdj:masterfrom
daschuer:loudnessEffect

Conversation

@daschuer
Copy link
Copy Markdown
Member

This PR adds a Loudness Contour effect intended to be placed at the master output.

It has a master gain mapping Knob, which allows these use cases.

  1. If your soundsystem volume is leveled for full volume. You can map the the Master gain and use it to reduce to dinner volume. You loose some bits of simple precision, but that should be OK.
    Once you rise the volume up, the Loudness effect goes away as it happens on your living room amplifier.

  2. If you like to put the loudness contour to the signal and level the volume at the amplifier or use the master gain dependently, you can disables the master knob mapping.

@daschuer
Copy link
Copy Markdown
Member Author


if (enableState != EffectProcessor::DISABLING) {

bool useMatser = m_pUseMasterGain->toBool();
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.

typo: useMaster

pState->m_oldSampleRate = sampleRate;

if (useMatser) {
masterGain = std::min(std::max(masterGain, 0.03), 1.0); // Limit at 0 .. -30 dB
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.

math_clamp(masterGain, 0.03, 1.0) from util/math.h is more readable IMO

@@ -0,0 +1,159 @@
#include <effects/native/loudnesscontoureffect.h>
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.

please use " instead of <>

manifest.setEffectRampsFromDry(true);
manifest.setIsMixingEQ(true);

EffectManifestParameter* low = manifest.addParameter();
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.

Since you added gain tracking, do we need this parameter? What's the use case if the master-gain-tracking perfectly adjusts the curve to compensate? Usually stereos just have a "loudness" button and not a manual-adjust knob, 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.

My old Yamaha Amp has such a loudness knob and I miss it on my new 5.1 one where I cannot even disable loudness.

The use case here is that the loudness level depends on the systems volume, which we cannot control, because Mixxx has no access to the Amp volume. So the user has the option to pick a suitable value for the current loudness.

manifest.setAuthor("The Mixxx Team");
manifest.setVersion("1.0");
manifest.setDescription(QObject::tr(
"A filter to compensate the loudness contour at lower volume"));
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.

I think we need a bit of explanation for users who don't know why you would want that.

What about?
"Amplifies low and high frequencies at low volumes to compensate for reduced sensitivity of the human ear."

I wish we could add links to the description, like:
https://en.wikipedia.org/wiki/Loudness_compensation

: m_pLoudness(pEffect->getParameterById("loudness")),
m_pUseMasterGain(pEffect->getParameterById("useMasterGain")) {
Q_UNUSED(manifest);
m_pMasterGain = std::make_unique<ControlProxy>("[Master]", "gain");
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.

I think it'd be more general if you plumbed the gain for the group through the GroupFeatureState. This way, if the user enabled LoudnessContour for Channel1, it would adjust using Channel1's gain instead of master.

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.

I have also considered this, but the use case for the channel gain is to level the track, not to set the volume. The alternative would be the volume slider. But this effect does basically the opposite what we need for mixing. Where we normally kill the base.
Using the loudness knob independently seams to be a good solution.

I will add super-knob mapping than this can go to a quick effect.

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.

I have also considered this, but the use case for the channel gain is to level the track, not to set the volume.

Is that a safe assumption? What if the user is not using internal mixing and they have the channel routed to a mixer instead of mixing internally?

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.

The user can always use the "Loudness" parameter. The master gain knob mapping works only in the use-case the sound system is leveled for full volume and the user used the master gain to reduce the loudness. For all other use-cases this mapping is IMHO pointless.
But If we have other use cases, it would be no issue to to add them.

I have also considered to add it to the EQ Rack, but currently we allow only one master EQ.
Or we can introduce a master Quick effect rack 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.

works only in the use-case the sound system is leveled for full volume

The user could have done that for channel1 too -- my point is just that you are limiting the possible options for the user by hard-coding it to the master gain and I see no reason to do that.

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.

OK, do you which to use the channel gain? Since the mapping is switchable it should be no issue to add that.

#ifndef LOUDNESSCONTOUREFFECT_H
#define LOUDNESSCONTOUREFFECT_H

#include <QMap>
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.

not needed

@daschuer
Copy link
Copy Markdown
Member Author

I have thought a bit about the GroupFeatureState and future requirements. This GroupFeatureState is a candidate for growing. It is no deal for me to add now additional gain member, but it requires additional engine code. This is quite easy in this case, but can be complicated for future cases.

This effect is most likely the only effect which will make ever use of gain. But adding it to GroupFeatureState put persistent CPU load on any effect chain call for any channel even if the loudness effect is not used.

The GroupFeatureState has already 20 values an 20 exist flags. Every value is set for every effect channel every callback cycle, but in almost all cases the values are not used.
If we also allow to access Control objects from the effect depending on the channel, we would have no performance penalty and we have an easy way to access any control object which is needed.

It would be also no problem to implement a fancy transport effect which accesses many controls, without bothering the user if other effects are loaded.

GroupFeatureState will remain, because it is handy to transfer a set of values that belongs together without races. The beat values are a perfect example for it.

I could use the gain CO here for a prove of concept how to add a CO to the EffectGroupState.

What do you think?

@rryan
Copy link
Copy Markdown
Member

rryan commented Jan 27, 2017

But adding it to GroupFeatureState put persistent CPU load on any effect chain call for any channel even if the loudness effect is not used.

The additional CPU load is negligible -- a single move instruction per deck to copy the gain into a new double field in the GroupFeatureState. The GroupFeatureState is passed to the effects via const reference so there is no cost for adding additional state.

Every value is set for every effect channel every callback cycle, but in almost all cases the values are not used.

Yes, they are unused sofar but I added them to make it easy on effect writers so they wouldn't have to do the plumbing themselves and could focus on writing their effect. .

I could use the gain CO here for a prove of concept how to add a CO to the EffectGroupState.

What do you think?

I think we should remove all CO use from the engine, so I don't want to add more places that use them. GroupFeatureState is a low-cost mechanism for exposing per-group state to the effects -- let's use that.

@daschuer
Copy link
Copy Markdown
Member Author

Finished!

@rryan
Copy link
Copy Markdown
Member

rryan commented Jan 27, 2017

Thanks! LGTM

@daschuer
Copy link
Copy Markdown
Member Author

Thank you for testing.

@daschuer daschuer merged commit ad789bd into mixxxdj:master Jan 27, 2017
@rryan
Copy link
Copy Markdown
Member

rryan commented Feb 6, 2017

I just noticed this is marked as a mixing EQ so it shows up as a choice for the deck EQs -- @daschuer, did you intend to do that? What's the use case there?

@daschuer
Copy link
Copy Markdown
Member Author

daschuer commented Feb 6, 2017

That is just a typo. Originally it should be isForFilterKnob(true). But since this is gone now the line can be removed.

@rryan
Copy link
Copy Markdown
Member

rryan commented Feb 6, 2017

Got it -- done: 95f74bd

@esbrandt esbrandt mentioned this pull request Jun 24, 2017
37 tasks
@daschuer daschuer deleted the loudnessEffect branch September 7, 2021 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants