Support for dB models
Add support for models that are in dB. There's a new member `m_modelIsLinear` which can be set via the constructor. The default value in the constructor is `true`. Add the method `modelIsLinear` which can be used to query the parameter. It is used in `calculateKnobPosYFromModel` and `setVolumeByLocalPixelValue`. Both methods got extended to deal with models in dB. They were also refactored to extract code that is common for both cases. Ensure that the constructor of `Fader` is called with `false` for `CrossoverEQControlDialog` and `EqFader` because these use models that are in dB.
This commit is contained in:
@@ -62,9 +62,10 @@ namespace lmms::gui
|
||||
|
||||
SimpleTextFloat* Fader::s_textFloat = nullptr;
|
||||
|
||||
Fader::Fader(FloatModel* model, const QString& name, QWidget* parent) :
|
||||
Fader::Fader(FloatModel* model, const QString& name, QWidget* parent, bool modelIsLinear) :
|
||||
QWidget(parent),
|
||||
FloatModelView(model, this)
|
||||
FloatModelView(model, this),
|
||||
m_modelIsLinear(modelIsLinear)
|
||||
{
|
||||
if (s_textFloat == nullptr)
|
||||
{
|
||||
@@ -85,8 +86,8 @@ Fader::Fader(FloatModel* model, const QString& name, QWidget* parent) :
|
||||
}
|
||||
|
||||
|
||||
Fader::Fader(FloatModel* model, const QString& name, QWidget* parent, const QPixmap& knob) :
|
||||
Fader(model, name, parent)
|
||||
Fader::Fader(FloatModel* model, const QString& name, QWidget* parent, const QPixmap& knob, bool modelIsLinear) :
|
||||
Fader(model, name, parent, modelIsLinear)
|
||||
{
|
||||
m_knob = knob;
|
||||
}
|
||||
@@ -191,37 +192,55 @@ void Fader::wheelEvent (QWheelEvent* ev)
|
||||
|
||||
int Fader::calculateKnobPosYFromModel() const
|
||||
{
|
||||
// This method calculates the pixel position where the lower end of
|
||||
// the fader knob should be for the amplification value in the model.
|
||||
//
|
||||
// The following assumes that the model describes an amplification,
|
||||
// i.e. that values are in [0, max] and that 1 is unity, i.e. 0 dbFS.
|
||||
auto* m = model();
|
||||
|
||||
auto const minV = model()->minValue();
|
||||
auto const maxV = model()->maxValue();
|
||||
auto const value = model()->value();
|
||||
auto const minV = m->minValue();
|
||||
auto const maxV = m->maxValue();
|
||||
auto const value = m->value();
|
||||
|
||||
auto const distanceToMin = value - minV;
|
||||
|
||||
// Prevent dbFS calculations with zero or negative values
|
||||
if (distanceToMin <= 0)
|
||||
if (modelIsLinear())
|
||||
{
|
||||
return height();
|
||||
// This method calculates the pixel position where the lower end of
|
||||
// the fader knob should be for the amplification value in the model.
|
||||
//
|
||||
// The following assumes that the model describes an amplification,
|
||||
// i.e. that values are in [0, max] and that 1 is unity, i.e. 0 dbFS.
|
||||
|
||||
auto const distanceToMin = value - minV;
|
||||
|
||||
// Prevent dbFS calculations with zero or negative values
|
||||
if (distanceToMin <= 0)
|
||||
{
|
||||
return height();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const maxDb = ampToDbfs(maxV);
|
||||
|
||||
// Make sure that we do not get values less that the minimum fader dbFS
|
||||
// for the calculations that will follow.
|
||||
auto const actualDb = std::max(m_faderMinDb, ampToDbfs(value));
|
||||
|
||||
auto const ratio = (actualDb - m_faderMinDb) / (maxDb - m_faderMinDb);
|
||||
|
||||
// This returns results between:
|
||||
// * m_knob.height() for a ratio of 1
|
||||
// * height() for a ratio of 0
|
||||
return height() - (height() - m_knob.height()) * std::pow(ratio, 3.);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const maxDb = ampToDbfs(maxV);
|
||||
// The model is in dB so we just show that in a linear fashion
|
||||
|
||||
auto const clampedValue = std::clamp(value, minV, maxV);
|
||||
|
||||
// Make sure that we do not get values less that the minimum fader dbFS
|
||||
// for the calculations that will follow.
|
||||
auto const actualDb = std::max(m_faderMinDb, ampToDbfs(value));
|
||||
|
||||
auto const ratio = (actualDb - m_faderMinDb) / (maxDb - m_faderMinDb);
|
||||
auto const ratio = (clampedValue - minV) / (maxV - minV);
|
||||
|
||||
// This returns results between:
|
||||
// * m_knob.height() for a ratio of 1
|
||||
// * height() for a ratio of 0
|
||||
return height() - (height() - m_knob.height()) * std::pow(ratio, 3.);
|
||||
return height() - (height() - m_knob.height()) * ratio;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,37 +253,49 @@ void Fader::setVolumeByLocalPixelValue(int y)
|
||||
// Assume that the middle of the fader should go there.
|
||||
int const lowerFaderKnob = y + (m_knob.height() / 2);
|
||||
|
||||
if (lowerFaderKnob >= height())
|
||||
// In some cases we need the clamped lower position of the fader knob so we can ensure
|
||||
// that we only set allowed values in the model range.
|
||||
int const clampedLowerFaderKnob = std::clamp(lowerFaderKnob, m_knob.height(), height());
|
||||
|
||||
if (modelIsLinear())
|
||||
{
|
||||
// We have to check this before clamping because otherwise we wouldn't be able to set -inf dB!
|
||||
model()->setValue(0);
|
||||
if (lowerFaderKnob >= height())
|
||||
{
|
||||
// Check the non-clamped value because otherwise we wouldn't be able to set -inf dB!
|
||||
model()->setValue(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are in the case where we set a value that's different from -inf dB so we use the clamped value
|
||||
// of the lower knob position so that we only set allowed values in the model range.
|
||||
|
||||
// First map the lower knob position to [0, 1] so that we can apply some curve mapping, e.g.
|
||||
// square, cube, etc.
|
||||
LinearMap<float> knobMap(float(m_knob.height()), 1., float(height()), 0.);
|
||||
|
||||
// Apply the inverse of what is done in calculateKnobPosYFromModel
|
||||
auto const knobPos = std::pow(knobMap.map(clampedLowerFaderKnob), 1./3.);
|
||||
|
||||
float const maxDb = ampToDbfs(m->maxValue());
|
||||
|
||||
LinearMap<float> dbMap(1., maxDb, 0., m_faderMinDb);
|
||||
|
||||
float const dbValue = dbMap.map(knobPos);
|
||||
|
||||
// Pull everything that's quieter than the minimum fader dbFS value down to 0 amplification.
|
||||
// This should not happen due to the steps above but let's be sure.
|
||||
// Otherwise compute the amplification value from the mapped dbFS value but make sure that we
|
||||
// do not exceed the maximum dbValue of the model
|
||||
float ampValue = dbValue < m_faderMinDb ? 0. : dbfsToAmp(std::min(maxDb, dbValue));
|
||||
|
||||
model()->setValue(ampValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are in the case where we set a value different from -inf dB so we first clamp the
|
||||
// lower position of the fader knob so that we only set allowed values in the model range.
|
||||
int const clampedLowerFaderKnob = std::clamp(lowerFaderKnob, m_knob.height(), height());
|
||||
LinearMap<float> valueMap(float(m_knob.height()), model()->maxValue(), float(height()), model()->minValue());
|
||||
|
||||
// First map the lower knob position to [0, 1] so that we can apply some curved mapping, e.g.
|
||||
// square, cube, etc.
|
||||
LinearMap<float> knobMap(float(m_knob.height()), 1., float(height()), 0.);
|
||||
|
||||
// Apply the inverse of what is done in calculateKnobPosYFromModel
|
||||
auto const knobPos = std::pow(knobMap.map(clampedLowerFaderKnob), 1./3.);
|
||||
|
||||
float const maxDb = ampToDbfs(m->maxValue());
|
||||
|
||||
LinearMap<float> dbMap(1., maxDb, 0., m_faderMinDb);
|
||||
|
||||
float const dbValue = dbMap.map(knobPos);
|
||||
|
||||
// Pull everything that's quieter than the minimum fader dbFS value down to 0 amplification.
|
||||
// This should not happen due to the steps above but let's be sure.
|
||||
// Otherwise compute the amplification value from the mapped dbFS value but make sure that we
|
||||
// do not exceed the maximum dbValue of the model
|
||||
float ampValue = dbValue < m_faderMinDb ? 0. : dbfsToAmp(std::min(maxDb, dbValue));
|
||||
|
||||
model()->setValue(ampValue);
|
||||
model()->setValue(valueMap.map(clampedLowerFaderKnob));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user