From 872409afd8ef27ee9f7c13377ef6a0dc7e556f70 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Fri, 5 Apr 2024 17:35:17 +0200 Subject: [PATCH] Use dbFS scale for the faders Make the faders use a linear dbFS scale. They now also change position on first click and can then be dragged. The `Fader` class now has a new property called `m_faderMinDb`. It's the minimum value before the amplification drops down fully to 0. It is needed because we cannot represent a scale from -inf to maxDB. Rename `knobPosY` to `calculateKnobPosYFromModel` and move the implementation into the cpp file. The method now first converts the model's current amplification value to dbFS and then computes a ratio based on that values and the minimum and maximum dbFS values. Add the method `setVolumeByLocalPixelValue` which takes a local y coordinate of the widget and sets the amplification of the model based on a dbFS scale. Adjust `mousePressEvent` and `mouseMoveEvent` so that they mostly take the local y coordinate of the event and use that to adjust the model via `setVolumeByLocalPixelValue`. Remove `m_moveStartPoint` and `m_startValue` and they are not needed anymore. --- include/Fader.h | 13 ++---- src/gui/widgets/Fader.cpp | 92 +++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/include/Fader.h b/include/Fader.h index 53e353a3d..a8256bd8c 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -119,13 +119,8 @@ private: void paintLevels(QPaintEvent* ev, QPainter& painter, bool linear = false); - int knobPosY() const - { - float fRange = model()->maxValue() - model()->minValue(); - float realVal = model()->value() - model()->minValue(); - - return height() - ((height() - m_knob.height()) * (realVal / fRange)); - } + int calculateKnobPosYFromModel() const; + void setVolumeByLocalPixelValue(int y); void setPeak(float fPeak, float& targetPeak, float& persistentPeak, QElapsedTimer& lastPeakTimer); @@ -147,8 +142,8 @@ private: bool m_levelsDisplayedInDBFS {true}; - int m_moveStartPoint {-1}; - float m_startValue {0.}; + // The dbFS amount after which we drop down to -inf dbFS + float const m_faderMinDb {-60.}; static SimpleTextFloat* s_textFloat; diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index a647df416..0f45ada2e 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -105,18 +105,13 @@ void Fader::contextMenuEvent(QContextMenuEvent* ev) void Fader::mouseMoveEvent(QMouseEvent* mouseEvent) { - if (m_moveStartPoint >= 0) - { - int dy = m_moveStartPoint - mouseEvent->globalY(); + const int localY = mouseEvent->y(); - float delta = dy * (model()->maxValue() - model()->minValue()) / (float)(height() - (m_knob).height()); + setVolumeByLocalPixelValue(localY); - const auto step = model()->step(); - float newValue = static_cast(static_cast((m_startValue + delta) / step + 0.5)) * step; - model()->setValue(newValue); + updateTextFloat(); - updateTextFloat(); - } + mouseEvent->accept(); } @@ -134,20 +129,13 @@ void Fader::mousePressEvent(QMouseEvent* mouseEvent) thisModel->saveJournallingState(false); } - if (mouseEvent->y() >= knobPosY() - (m_knob).height() && mouseEvent->y() < knobPosY()) - { - updateTextFloat(); - s_textFloat->show(); + const int localY = mouseEvent->y(); + setVolumeByLocalPixelValue(localY); - m_moveStartPoint = mouseEvent->globalY(); - m_startValue = model()->value(); + updateTextFloat(); + s_textFloat->show(); - mouseEvent->accept(); - } - else - { - m_moveStartPoint = -1; - } + mouseEvent->accept(); } else { @@ -201,6 +189,64 @@ 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 const minV = model()->minValue(); + auto const maxV = model()->maxValue(); + auto const value = model()->value(); + + 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()) * ratio; + } +} + + +void Fader::setVolumeByLocalPixelValue(int y) +{ + // The y parameter gives us where the mouse click went. + // Assume that the middle of the fader should go there. + int const lowerFaderKnob = y + (m_knob.height() / 2); + + auto* m = model(); + float const maxDb = ampToDbfs(m->maxValue()); + + LinearMap map(float(m_knob.height()), maxDb, float(height()), m_faderMinDb); + + float const dbValue = map.map(lowerFaderKnob); + + // Pull everything that's quieter than the minimum fader dbFS value down to 0 amplification. + // 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); +} + /// /// Set peak value (0.0 .. 1.0) @@ -265,7 +311,7 @@ void Fader::updateTextFloat() s_textFloat->setText(m_description + " " + QString("%1 ").arg(model()->value() * m_conversionFactor) + " " + m_unit); } - s_textFloat->moveGlobal(this, QPoint(width() + 2, knobPosY() - s_textFloat->height() / 2)); + s_textFloat->moveGlobal(this, QPoint(width() + 2, calculateKnobPosYFromModel() - s_textFloat->height() / 2)); } @@ -277,7 +323,7 @@ void Fader::paintEvent(QPaintEvent* ev) paintLevels(ev, painter, !m_levelsDisplayedInDBFS); // Draw the knob - painter.drawPixmap((width() - m_knob.width()) / 2, knobPosY() - m_knob.height(), m_knob); + painter.drawPixmap((width() - m_knob.width()) / 2, calculateKnobPosYFromModel() - m_knob.height(), m_knob); } void Fader::paintLevels(QPaintEvent* ev, QPainter& painter, bool linear)