Skip to content

Commit

Permalink
UI Consistency - Align compressor display values (#1557)
Browse files Browse the repository at this point in the history
* Align comp menu values

Updated values displayed in comp values to align them with the pop-up values displayed in the Song Master Compressor

Still some work to do as values aren't exactly the same

* rename ratio

* simplify display variables

* Align menu values with gold knob values

Updated menu's to use the 0-128 value range so that values displayed are equal to those values displayed with gold knobs in song view

* Display comp param units on OLED

On OLED, all comp params (except threshold) now display the units (e.g. MS, HZ, or : 1)

* Updated strncat char's

---------

Co-authored-by: m-m-adams <[email protected]>
  • Loading branch information
seangoodvibes and m-m-adams committed Mar 23, 2024
1 parent e03e6b2 commit b63c286
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 40 deletions.
4 changes: 2 additions & 2 deletions src/deluge/dsp/compressor/rms_feedback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void RMSFeedbackCompressor::updateER(float numSamples, q31_t finalVolume) {
// this is effectively where song volume gets applied, so we'll stick an IIR filter (e.g. the envelope) here to
// reduce clicking
float lastER = er;
er = std::max<float>((songVolumedB - threshdb - 1) * ratio, 0);
er = std::max<float>((songVolumedB - threshdb - 1) * reduction, 0);
// using the envelope is convenient since it means makeup gain and compression amount change at the same rate
er = runEnvelope(lastER, er, numSamples);
}
Expand All @@ -57,7 +57,7 @@ void RMSFeedbackCompressor::render(StereoSample* buffer, uint16_t numSamples, q3

state = runEnvelope(state, over, numSamples);

float reduction = -state * ratio;
float reduction = -state * reduction;

// this is the most gain available without overflow
float dbGain = 3.f + er + reduction;
Expand Down
14 changes: 9 additions & 5 deletions src/deluge/dsp/compressor/rms_feedback.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,20 @@ class RMSFeedbackCompressor {
threshold = 1 - 0.8f * (float(thresholdKnobPos) / ONE_Q31f);
}
q31_t getRatio() { return ratioKnobPos; }
constexpr int32_t getRatioForDisplay() { return ratio; }
constexpr int32_t setRatio(q31_t rat) {
ratioKnobPos = rat;
ratio = 0.5f + (float(ratioKnobPos) / ONE_Q31f) / 2;
return 1 / (1 - ratio);
reduction = 0.5f + (float(ratioKnobPos) / ONE_Q31f) / 2;
ratio = 1 / (1 - reduction);
return ratio;
}
q31_t getSidechain() { return sideChainKnobPos; }

constexpr int32_t getSidechainForDisplay() { return fc_hz; }
constexpr int32_t setSidechain(q31_t f) {
sideChainKnobPos = f;
// this exp will be between 1 and 5ish, half the knob range is about 2
// the result will then be from 0 to 100hz with half the knob range at 60hz
float fc_hz = (std::exp(1.5 * float(f) / ONE_Q31f) - 1) * 30;
fc_hz = (std::exp(1.5 * float(f) / ONE_Q31f) - 1) * 30;
float fc = fc_hz / float(kSampleRate);
float wc = fc / (1 + fc);
a = wc * ONE_Q31;
Expand All @@ -93,7 +95,7 @@ class RMSFeedbackCompressor {
// parameters in use
float a_ = (-1000.0f / kSampleRate);
float r_ = (-1000.0f / kSampleRate);
float ratio = 2;
float reduction = 0.5;
float er = 0;
float threshdb = 17;
float threshold = 1;
Expand All @@ -112,6 +114,8 @@ class RMSFeedbackCompressor {
// for display
float attackMS = 0;
float releaseMS = 0;
float ratio = 2;
float fc_hz;

// raw knob positions
q31_t thresholdKnobPos = 0;
Expand Down
58 changes: 30 additions & 28 deletions src/deluge/gui/menu_item/audio_compressor/compressor_values.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,71 +12,73 @@ class Attack final : public Integer {
using Integer::Integer;
void readCurrentValue() override {
auto value = (uint64_t)soundEditor.currentModControllable->compressor.getAttack();
this->setValue((value * (kMaxMenuValue * 2) + 2147483648) >> 32);
this->setValue(value >> 24);
}
void writeCurrentValue() override {
if (this->getValue() == kMaxMenuValue) {
soundEditor.currentModControllable->compressor.setAttack(ONE_Q31);
}
else {
q31_t knobPos = (uint32_t)this->getValue() * (2147483648 / kMidMenuValue) >> 1;
auto value = this->getValue();
if (value < kMaxKnobPos) {
q31_t knobPos = lshiftAndSaturate<24>(value);
soundEditor.currentModControllable->compressor.setAttack(knobPos);
}
}
[[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; }
int32_t getDisplayValue() override { return soundEditor.currentModControllable->compressor.getAttackMS(); }
const char* getUnit() override { return " MS"; }
[[nodiscard]] int32_t getMaxValue() const override { return kMaxKnobPos; }
};
class Release final : public Integer {
public:
using Integer::Integer;
void readCurrentValue() override {
auto value = (uint64_t)soundEditor.currentModControllable->compressor.getRelease();
this->setValue((value * (kMaxMenuValue * 2) + 2147483648) >> 32);
this->setValue(value >> 24);
}
void writeCurrentValue() override {
if (this->getValue() == kMaxMenuValue) {
soundEditor.currentModControllable->compressor.setRelease(ONE_Q31);
}
else {
q31_t knobPos = (uint32_t)this->getValue() * (2147483648 / kMidMenuValue) >> 1;
auto value = this->getValue();
if (value < kMaxKnobPos) {
q31_t knobPos = lshiftAndSaturate<24>(value);
soundEditor.currentModControllable->compressor.setRelease(knobPos);
}
}
[[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; }
int32_t getDisplayValue() override { return soundEditor.currentModControllable->compressor.getReleaseMS(); }
const char* getUnit() override { return " MS"; }
[[nodiscard]] int32_t getMaxValue() const override { return kMaxKnobPos; }
};
class Ratio final : public Integer {
public:
using Integer::Integer;
void readCurrentValue() override {
auto value = (uint64_t)soundEditor.currentModControllable->compressor.getRatio();
this->setValue((value * (kMaxMenuValue * 2) + 2147483648) >> 32);
this->setValue(value >> 24);
}
void writeCurrentValue() override {
if (this->getValue() == kMaxMenuValue) {
soundEditor.currentModControllable->compressor.setRatio(ONE_Q31);
}
else {
q31_t knobPos = (uint32_t)this->getValue() * (2147483648 / kMidMenuValue) >> 1;
auto value = this->getValue();
if (value < kMaxKnobPos) {
q31_t knobPos = lshiftAndSaturate<24>(value);
soundEditor.currentModControllable->compressor.setRatio(knobPos);
}
}
[[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; }
int32_t getDisplayValue() override { return soundEditor.currentModControllable->compressor.getRatioForDisplay(); }
const char* getUnit() override { return " : 1"; }
[[nodiscard]] int32_t getMaxValue() const override { return kMaxKnobPos; }
};
class SideHPF final : public Integer {
public:
using Integer::Integer;
void readCurrentValue() override {
auto value = (uint64_t)soundEditor.currentModControllable->compressor.getSidechain();
this->setValue((value * (kMaxMenuValue * 2) + 2147483648) >> 32);
this->setValue(value >> 24);
}
void writeCurrentValue() override {
if (this->getValue() == kMaxMenuValue) {
soundEditor.currentModControllable->compressor.setSidechain(ONE_Q31);
}
else {
q31_t knobPos = (uint32_t)this->getValue() * (2147483648 / kMidMenuValue) >> 1;
auto value = this->getValue();
if (value < kMaxKnobPos) {
q31_t knobPos = lshiftAndSaturate<24>(value);
soundEditor.currentModControllable->compressor.setSidechain(knobPos);
}
}
[[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; }
int32_t getDisplayValue() override {
return soundEditor.currentModControllable->compressor.getSidechainForDisplay();
}
const char* getUnit() override { return " HZ"; }
[[nodiscard]] int32_t getMaxValue() const override { return kMaxKnobPos; }
};
} // namespace deluge::gui::menu_item::audio_compressor
5 changes: 3 additions & 2 deletions src/deluge/gui/menu_item/integer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void Integer::selectEncoderAction(int32_t offset) {
}

void Integer::drawValue() {
display->setTextAsNumber(this->getValue());
display->setTextAsNumber(getDisplayValue());
}

void IntegerWithOff::drawValue() {
Expand All @@ -53,7 +53,8 @@ void IntegerWithOff::drawValue() {

void Integer::drawInteger(int32_t textWidth, int32_t textHeight, int32_t yPixel) {
char buffer[12];
intToString(this->getValue(), buffer, 1);
intToString(getDisplayValue(), buffer, 1);
strncat(buffer, getUnit(), 4);
deluge::hid::display::OLED::drawStringCentred(buffer, yPixel + OLED_MAIN_TOPMOST_PIXEL,
deluge::hid::display::OLED::oledMainImage[0], OLED_MAIN_WIDTH_PIXELS,
textWidth, textHeight);
Expand Down
3 changes: 3 additions & 0 deletions src/deluge/gui/menu_item/integer.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class Integer : public Number {
void selectEncoderAction(int32_t offset) override;

protected:
virtual int32_t getDisplayValue() { return this->getValue(); }
virtual const char* getUnit() { return ""; }

void drawPixelsForOled();
virtual void drawInteger(int32_t textWidth, int32_t textHeight, int32_t yPixel);

Expand Down
13 changes: 10 additions & 3 deletions src/deluge/model/global_effectable/global_effectable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse
int current;
int displayLevel;
int ledLevel;
const char* unit;
// this is only reachable in comp editing mode, otherwise it's an existent param
if (whichModEncoder == 1) { // sidechain (threshold)
current = (compressor.getThreshold() >> 24) - 64;
Expand All @@ -535,6 +536,7 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse
displayLevel = ((ledLevel)*kMaxMenuValue) / 128;
compressor.setThreshold(lshiftAndSaturate<24>(current + 64));
indicator_leds::setKnobIndicatorLevel(1, ledLevel);
unit = "";
}
else if (whichModEncoder == 0) {
switch (currentCompParam) {
Expand All @@ -545,9 +547,8 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse
// this range is ratio of 2 to 20
current = std::clamp(current, -64, 64);
ledLevel = (64 + current);
displayLevel = ((ledLevel)*kMaxMenuValue) / 128;

displayLevel = compressor.setRatio(lshiftAndSaturate<24>(current + 64));
unit = " : 1";
break;

case CompParam::ATTACK:
Expand All @@ -557,6 +558,7 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse
ledLevel = (64 + current);

displayLevel = compressor.setAttack(lshiftAndSaturate<24>(current + 64));
unit = " MS";
break;

case CompParam::RELEASE:
Expand All @@ -566,6 +568,7 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse
ledLevel = (64 + current);

displayLevel = compressor.setRelease(lshiftAndSaturate<24>(current + 64));
unit = " MS";
break;

case CompParam::SIDECHAIN:
Expand All @@ -575,12 +578,16 @@ ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offse
ledLevel = (64 + current);

displayLevel = compressor.setSidechain(lshiftAndSaturate<24>(current + 64));
unit = " HZ";
break;
}
indicator_leds::setKnobIndicatorLevel(0, ledLevel);
}
char buffer[5];
char buffer[12];
intToString(displayLevel, buffer);
if (display->haveOLED()) {
strncat(buffer, unit, 4);
}
display->displayPopup(buffer);

return ActionResult::DEALT_WITH;
Expand Down

0 comments on commit b63c286

Please sign in to comment.