LOMM (upward/downward multiband compressor) (#6925)

This commit is contained in:
Lost Robot
2023-11-10 07:10:44 -08:00
committed by GitHub
parent 3b9e8c5ea1
commit 89c98a77a5
41 changed files with 1376 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
INCLUDE(BuildPlugin)
BUILD_PLUGIN(lomm LOMM.cpp LOMMControls.cpp LOMMControlDialog.cpp MOCFILES LOMM.h LOMMControls.h LOMMControlDialog.h EMBEDDED_RESOURCES *.png)

444
plugins/LOMM/LOMM.cpp Normal file
View File

@@ -0,0 +1,444 @@
/*
* LOMM.cpp
*
* Copyright (c) 2023 Lost Robot <r94231/at/gmail/dot/com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "LOMM.h"
#include "embed.h"
#include "plugin_export.h"
namespace lmms
{
extern "C"
{
Plugin::Descriptor PLUGIN_EXPORT lomm_plugin_descriptor =
{
LMMS_STRINGIFY(PLUGIN_NAME),
"LOMM",
QT_TRANSLATE_NOOP("PluginBrowser", "Upwards/downwards multiband compression plugin powered by the eldritch elder god LOMMUS."),
"Lost Robot <r94231/at/gmail/dot/com>",
0x0100,
Plugin::Type::Effect,
new PluginPixmapLoader("logo"),
nullptr,
nullptr
};
}
LOMMEffect::LOMMEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) :
Effect(&lomm_plugin_descriptor, parent, key),
m_lommControls(this),
m_sampleRate(Engine::audioEngine()->processingSampleRate()),
m_lp1(m_sampleRate),
m_lp2(m_sampleRate),
m_hp1(m_sampleRate),
m_hp2(m_sampleRate),
m_needsUpdate(true),
m_coeffPrecalc(-0.05),
m_crestTimeConst(0.999),
m_lookWrite(0),
m_lookBufLength(2)
{
autoQuitModel()->setValue(autoQuitModel()->maxValue());
m_yL[0][0] = m_yL[0][1] = LOMM_MIN_FLOOR;
m_yL[1][0] = m_yL[1][1] = LOMM_MIN_FLOOR;
m_yL[2][0] = m_yL[2][1] = LOMM_MIN_FLOOR;
connect(Engine::audioEngine(), SIGNAL(sampleRateChanged()), this, SLOT(changeSampleRate()));
emit changeSampleRate();
}
void LOMMEffect::changeSampleRate()
{
m_sampleRate = Engine::audioEngine()->processingSampleRate();
m_lp1.setSampleRate(m_sampleRate);
m_lp2.setSampleRate(m_sampleRate);
m_hp1.setSampleRate(m_sampleRate);
m_hp2.setSampleRate(m_sampleRate);
m_coeffPrecalc = -2.2f / (m_sampleRate * 0.001f);
m_needsUpdate = true;
m_crestTimeConst = exp(-1.f / (0.2f * m_sampleRate));
m_lookBufLength = std::ceil((LOMM_MAX_LOOKAHEAD / 1000.f) * m_sampleRate) + 2;
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 3; ++j)
{
m_inLookBuf[j][i].resize(m_lookBufLength);
m_scLookBuf[j][i].resize(m_lookBufLength, LOMM_MIN_FLOOR);
}
}
}
void LOMMEffect::clearFilterHistories()
{
m_lp1.clearHistory();
m_lp2.clearHistory();
m_hp1.clearHistory();
m_hp2.clearHistory();
}
bool LOMMEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning())
{
return false;
}
if (m_needsUpdate || m_lommControls.m_split1Model.isValueChanged())
{
m_lp1.setLowpass(m_lommControls.m_split1Model.value());
m_hp1.setHighpass(m_lommControls.m_split1Model.value());
}
if (m_needsUpdate || m_lommControls.m_split2Model.isValueChanged())
{
m_lp2.setLowpass(m_lommControls.m_split2Model.value());
m_hp2.setHighpass(m_lommControls.m_split2Model.value());
}
m_needsUpdate = false;
float outSum = 0.f;
const float d = dryLevel();
const float w = wetLevel();
const float depth = m_lommControls.m_depthModel.value();
const float time = m_lommControls.m_timeModel.value();
const float inVol = dbfsToAmp(m_lommControls.m_inVolModel.value());
const float outVol = dbfsToAmp(m_lommControls.m_outVolModel.value());
const float upward = m_lommControls.m_upwardModel.value();
const float downward = m_lommControls.m_downwardModel.value();
const bool split1Enabled = m_lommControls.m_split1EnabledModel.value();
const bool split2Enabled = m_lommControls.m_split2EnabledModel.value();
const bool band1Enabled = m_lommControls.m_band1EnabledModel.value();
const bool band2Enabled = m_lommControls.m_band2EnabledModel.value();
const bool band3Enabled = m_lommControls.m_band3EnabledModel.value();
const float inHigh = dbfsToAmp(m_lommControls.m_inHighModel.value());
const float inMid = dbfsToAmp(m_lommControls.m_inMidModel.value());
const float inLow = dbfsToAmp(m_lommControls.m_inLowModel.value());
float inBandVol[3] = {inHigh, inMid, inLow};
const float outHigh = dbfsToAmp(m_lommControls.m_outHighModel.value());
const float outMid = dbfsToAmp(m_lommControls.m_outMidModel.value());
const float outLow = dbfsToAmp(m_lommControls.m_outLowModel.value());
float outBandVol[3] = {outHigh, outMid, outLow};
const float aThreshH = m_lommControls.m_aThreshHModel.value();
const float aThreshM = m_lommControls.m_aThreshMModel.value();
const float aThreshL = m_lommControls.m_aThreshLModel.value();
float aThresh[3] = {aThreshH, aThreshM, aThreshL};
const float aRatioH = m_lommControls.m_aRatioHModel.value();
const float aRatioM = m_lommControls.m_aRatioMModel.value();
const float aRatioL = m_lommControls.m_aRatioLModel.value();
float aRatio[3] = {1.f / aRatioH, 1.f / aRatioM, 1.f / aRatioL};
const float bThreshH = m_lommControls.m_bThreshHModel.value();
const float bThreshM = m_lommControls.m_bThreshMModel.value();
const float bThreshL = m_lommControls.m_bThreshLModel.value();
float bThresh[3] = {bThreshH, bThreshM, bThreshL};
const float bRatioH = m_lommControls.m_bRatioHModel.value();
const float bRatioM = m_lommControls.m_bRatioMModel.value();
const float bRatioL = m_lommControls.m_bRatioLModel.value();
float bRatio[3] = {1.f / bRatioH, 1.f / bRatioM, 1.f / bRatioL};
const float atkH = m_lommControls.m_atkHModel.value() * time;
const float atkM = m_lommControls.m_atkMModel.value() * time;
const float atkL = m_lommControls.m_atkLModel.value() * time;
const float atkCoefH = msToCoeff(atkH);
const float atkCoefM = msToCoeff(atkM);
const float atkCoefL = msToCoeff(atkL);
float atk[3] = {atkH, atkM, atkL};
float atkCoef[3] = {atkCoefH, atkCoefM, atkCoefL};
const float relH = m_lommControls.m_relHModel.value() * time;
const float relM = m_lommControls.m_relMModel.value() * time;
const float relL = m_lommControls.m_relLModel.value() * time;
const float relCoefH = msToCoeff(relH);
const float relCoefM = msToCoeff(relM);
const float relCoefL = msToCoeff(relL);
float rel[3] = {relH, relM, relL};
float relCoef[3] = {relCoefH, relCoefM, relCoefL};
const float rmsTime = m_lommControls.m_rmsTimeModel.value();
const float rmsTimeConst = (rmsTime == 0) ? 0 : exp(-1.f / (rmsTime * 0.001f * m_sampleRate));
const float knee = m_lommControls.m_kneeModel.value() * 0.5f;
const float range = m_lommControls.m_rangeModel.value();
const float rangeAmp = dbfsToAmp(range);
const float balance = m_lommControls.m_balanceModel.value();
const float balanceAmpTemp = dbfsToAmp(balance);
const float balanceAmp[2] = {1.f / balanceAmpTemp, balanceAmpTemp};
const bool depthScaling = m_lommControls.m_depthScalingModel.value();
const bool stereoLink = m_lommControls.m_stereoLinkModel.value();
const float autoTime = m_lommControls.m_autoTimeModel.value() * m_lommControls.m_autoTimeModel.value();
const float mix = m_lommControls.m_mixModel.value();
const bool midside = m_lommControls.m_midsideModel.value();
const bool lookaheadEnable = m_lommControls.m_lookaheadEnableModel.value();
const int lookahead = std::ceil((m_lommControls.m_lookaheadModel.value() / 1000.f) * m_sampleRate);
const bool feedback = m_lommControls.m_feedbackModel.value() && !lookaheadEnable;
const bool lowSideUpwardSuppress = m_lommControls.m_lowSideUpwardSuppressModel.value() && midside;
for (fpp_t f = 0; f < frames; ++f)
{
std::array<sample_t, 2> s = {buf[f][0], buf[f][1]};
// Convert left/right to mid/side. Side channel is intentionally made
// to be 6 dB louder to bring it into volume ranges comparable to the mid channel.
if (midside)
{
float tempS0 = s[0];
s[0] = (s[0] + s[1]) * 0.5f;
s[1] = tempS0 - s[1];
}
std::array<std::array<float, 2>, 3> bands = {{}};
std::array<std::array<float, 2>, 3> bandsDry = {{}};
for (int i = 0; i < 2; ++i)// Channels
{
// These values are for the Auto time knob. Higher crest factor allows for faster attack/release.
float inSquared = s[i] * s[i];
m_crestPeakVal[i] = std::max(std::max(LOMM_MIN_FLOOR, inSquared), m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inSquared));
m_crestRmsVal[i] = std::max(LOMM_MIN_FLOOR, m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inSquared)));
m_crestFactorVal[i] = m_crestPeakVal[i] / m_crestRmsVal[i];
float crestFactorValTemp = ((m_crestFactorVal[i] - LOMM_AUTO_TIME_ADJUST) * autoTime) + LOMM_AUTO_TIME_ADJUST;
// Crossover filters
bands[0][i] = m_hp1.update(s[i], i);
bands[1][i] = m_hp2.update(m_lp1.update(s[i], i), i);
bands[2][i] = m_lp2.update(s[i], i);
if (!split1Enabled)
{
bands[1][i] += bands[0][i];
bands[0][i] = 0;
}
if (!split2Enabled)
{
bands[1][i] += bands[2][i];
bands[2][i] = 0;
}
// Mute disabled bands
bands[0][i] *= band1Enabled;
bands[1][i] *= band2Enabled;
bands[2][i] *= band3Enabled;
std::array<float, 3> detect = {0, 0, 0};
for (int j = 0; j < 3; ++j)// Bands
{
bandsDry[j][i] = bands[j][i];
if (feedback && !lookaheadEnable)
{
bands[j][i] = m_prevOut[j][i];
}
bands[j][i] *= inBandVol[j] * inVol * balanceAmp[i];
if (rmsTime > 0)// RMS
{
m_rms[j][i] = rmsTimeConst * m_rms[j][i] + ((1 - rmsTimeConst) * (bands[j][i] * bands[j][i]));
detect[j] = std::max(LOMM_MIN_FLOOR, std::sqrt(m_rms[j][i]));
}
else// Peak
{
detect[j] = std::max(LOMM_MIN_FLOOR, std::abs(bands[j][i]));
}
if (detect[j] > m_yL[j][i])// Attack phase
{
// Calculate attack value depending on crest factor
const float currentAttack = autoTime
? msToCoeff(LOMM_AUTO_TIME_ADJUST * atk[j] / crestFactorValTemp)
: atkCoef[j];
m_yL[j][i] = m_yL[j][i] * currentAttack + (1 - currentAttack) * detect[j];
}
else// Release phase
{
// Calculate release value depending on crest factor
const float currentRelease = autoTime
? msToCoeff(LOMM_AUTO_TIME_ADJUST * rel[j] / crestFactorValTemp)
: relCoef[j];
m_yL[j][i] = m_yL[j][i] * currentRelease + (1 - currentRelease) * detect[j];
}
m_yL[j][i] = std::max(LOMM_MIN_FLOOR, m_yL[j][i]);
float yAmp = m_yL[j][i];
if (lookaheadEnable)
{
float temp = yAmp;
// Lookahead is calculated by picking the largest value between
// the current sidechain signal and the delayed sidechain signal.
yAmp = std::max(m_scLookBuf[j][i][m_lookWrite], m_scLookBuf[j][i][(m_lookWrite + m_lookBufLength - lookahead) % m_lookBufLength]);
m_scLookBuf[j][i][m_lookWrite] = temp;
}
const float yDbfs = ampToDbfs(yAmp);
float aboveGain = 0;
float belowGain = 0;
// Downward compression
if (yDbfs - aThresh[j] < -knee)// Below knee
{
aboveGain = yDbfs;
}
else if (yDbfs - aThresh[j] < knee)// Within knee
{
const float temp = yDbfs - aThresh[j] + knee;
aboveGain = yDbfs + (aRatio[j] - 1) * temp * temp / (4 * knee);
}
else// Above knee
{
aboveGain = aThresh[j] + (yDbfs - aThresh[j]) * aRatio[j];
}
if (aboveGain < yDbfs)
{
if (downward * depth <= 1)
{
aboveGain = linearInterpolate(yDbfs, aboveGain, downward * depth);
}
else
{
aboveGain = linearInterpolate(aboveGain, aThresh[j], downward * depth - 1);
}
}
// Upward compression
if (yDbfs - bThresh[j] > knee)// Above knee
{
belowGain = yDbfs;
}
else if (bThresh[j] - yDbfs < knee)// Within knee
{
const float temp = bThresh[j] - yDbfs + knee;
belowGain = yDbfs + (1 - bRatio[j]) * temp * temp / (4 * knee);
}
else// Below knee
{
belowGain = bThresh[j] + (yDbfs - bThresh[j]) * bRatio[j];
}
if (belowGain > yDbfs)
{
if (upward * depth <= 1)
{
belowGain = linearInterpolate(yDbfs, belowGain, upward * depth);
}
else
{
belowGain = linearInterpolate(belowGain, bThresh[j], upward * depth - 1);
}
}
m_displayIn[j][i] = yDbfs;
m_gainResult[j][i] = (dbfsToAmp(aboveGain) / yAmp) * (dbfsToAmp(belowGain) / yAmp);
if (lowSideUpwardSuppress && m_gainResult[j][i] > 1 && j == 2 && i == 1) //undo upward compression if low side band
{
m_gainResult[j][i] = 1;
}
m_gainResult[j][i] = std::min(m_gainResult[j][i], rangeAmp);
m_displayOut[j][i] = ampToDbfs(std::max(LOMM_MIN_FLOOR, yAmp * m_gainResult[j][i]));
// Apply the same gain reduction to both channels if stereo link is enabled.
if (stereoLink && i == 1)
{
if (m_gainResult[j][1] < m_gainResult[j][0])
{
m_gainResult[j][0] = m_gainResult[j][1];
m_displayOut[j][0] = m_displayIn[j][0] - (m_displayIn[j][1] - m_displayOut[j][1]);
}
else
{
m_gainResult[j][1] = m_gainResult[j][0];
m_displayOut[j][1] = m_displayIn[j][1] - (m_displayIn[j][0] - m_displayOut[j][0]);
}
}
}
}
for (int i = 0; i < 2; ++i)// Channels
{
for (int j = 0; j < 3; ++j)// Bands
{
if (lookaheadEnable)
{
float temp = bands[j][i];
bands[j][i] = m_inLookBuf[j][i][m_lookWrite];
m_inLookBuf[j][i][m_lookWrite] = temp;
bandsDry[j][i] = bands[j][i];
}
else if (feedback)
{
bands[j][i] = bandsDry[j][i] * inBandVol[j] * inVol * balanceAmp[i];
}
// Apply gain reduction
bands[j][i] *= m_gainResult[j][i];
// Store for Feedback
m_prevOut[j][i] = bands[j][i];
bands[j][i] *= outBandVol[j];
bands[j][i] = linearInterpolate(bandsDry[j][i], bands[j][i], mix);
}
s[i] = bands[0][i] + bands[1][i] + bands[2][i];
s[i] *= linearInterpolate(1.f, outVol, mix * (depthScaling ? depth : 1));
}
// Convert mid/side back to left/right.
// Note that the side channel was intentionally made to be 6 dB louder prior to compression.
if (midside)
{
float tempS0 = s[0];
s[0] = s[0] + (s[1] * 0.5f);
s[1] = tempS0 - (s[1] * 0.5f);
}
if (--m_lookWrite < 0) { m_lookWrite = m_lookBufLength - 1; }
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];
outSum += buf[f][0] + buf[f][1];
}
checkGate(outSum / frames);
return isRunning();
}
extern "C"
{
// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main(Model* parent, void* data)
{
return new LOMMEffect(parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(data));
}
}
} // namespace lmms

106
plugins/LOMM/LOMM.h Normal file
View File

@@ -0,0 +1,106 @@
/*
* LOMM.h
*
* Copyright (c) 2023 Lost Robot <r94231/at/gmail/dot/com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_LOMM_H
#define LMMS_LOMM_H
#include "LOMMControls.h"
#include "Effect.h"
#include "BasicFilters.h"
#include "lmms_math.h"
namespace lmms
{
constexpr inline float LOMM_MIN_FLOOR = 0.00012589;// -72 dBFS
constexpr inline float LOMM_MAX_LOOKAHEAD = 20.f;
constexpr inline float LOMM_AUTO_TIME_ADJUST = 5.f;
class LOMMEffect : public Effect
{
Q_OBJECT
public:
LOMMEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
~LOMMEffect() override = default;
bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{
return &m_lommControls;
}
void clearFilterHistories();
inline float msToCoeff(float ms)
{
return (ms == 0) ? 0 : exp(m_coeffPrecalc / ms);
}
private slots:
void changeSampleRate();
private:
LOMMControls m_lommControls;
float m_sampleRate;
StereoLinkwitzRiley m_lp1;
StereoLinkwitzRiley m_lp2;
StereoLinkwitzRiley m_hp1;
StereoLinkwitzRiley m_hp2;
bool m_needsUpdate;
float m_coeffPrecalc;
std::array<std::array<float, 2>, 3> m_yL;
std::array<std::array<float, 2>, 3> m_rms;
std::array<std::array<float, 2>, 3> m_gainResult;
std::array<std::array<float, 2>, 3> m_displayIn;
std::array<std::array<float, 2>, 3> m_displayOut;
std::array<float, 2> m_crestPeakVal;
std::array<float, 2> m_crestRmsVal;
std::array<float, 2> m_crestFactorVal;
float m_crestTimeConst = 0.0f;
std::array<std::array<float, 2>, 3> m_prevOut;
std::array<std::array<std::vector<float>, 2>, 3> m_inLookBuf;
std::array<std::array<std::vector<float>, 2>, 3> m_scLookBuf;
int m_lookWrite = 0;
int m_lookBufLength = 0;
friend class LOMMControls;
friend class gui::LOMMControlDialog;
};
} // namespace lmms
#endif // LMMS_LOMM_H

View File

@@ -0,0 +1,275 @@
/*
* LOMMControlDialog.cpp
*
* Copyright (c) 2023 Lost Robot <r94231/at/gmail/dot/com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "LOMM.h"
#include "LOMMControlDialog.h"
#include "LOMMControls.h"
namespace lmms::gui
{
LOMMControlDialog::LOMMControlDialog(LOMMControls* controls) :
EffectControlDialog(controls),
m_controls(controls)
{
setAutoFillBackground(true);
QPalette pal;
pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork"));
setPalette(pal);
setFixedSize(400, 256);
createKnob(KnobType::Bright26, this, 10, 4, &controls->m_depthModel, tr("Depth:"), "", tr("Compression amount for all bands"));
createKnob(KnobType::Bright26, this, 10, 41, &controls->m_timeModel, tr("Time:"), "", tr("Attack/release scaling for all bands"));
createKnob(KnobType::Bright26, this, 10, 220, &controls->m_inVolModel, tr("Input Volume:"), " dB", tr("Input volume"));
createKnob(KnobType::Bright26, this, 363, 220, &controls->m_outVolModel, tr("Output Volume:"), " dB", tr("Output volume"));
createKnob(KnobType::Bright26, this, 10, 179, &controls->m_upwardModel, tr("Upward Depth:"), "", tr("Upward compression amount for all bands"));
createKnob(KnobType::Bright26, this, 363, 179, &controls->m_downwardModel, tr("Downward Depth:"), "", tr("Downward compression amount for all bands"));
createLcdFloatSpinBox(5, 2, "11green", tr("High/Mid Crossover"), this, 352, 76, &controls->m_split1Model, tr("High/Mid Crossover"));
createLcdFloatSpinBox(5, 2, "11green", tr("Mid/Low Crossover"), this, 352, 156, &controls->m_split2Model, tr("Mid/Low Crossover"));
createPixmapButton(tr("High/mid band split"), this, 369, 104, &controls->m_split1EnabledModel, "crossover_led_green", "crossover_led_off", tr("High/mid band split"));
createPixmapButton(tr("Mid/low band split"), this, 369, 126, &controls->m_split2EnabledModel, "crossover_led_green", "crossover_led_off", tr("Mid/low band split"));
createPixmapButton(tr("Enable High Band"), this, 143, 66, &controls->m_band1EnabledModel, "high_band_active", "high_band_inactive", tr("Enable High Band"));
createPixmapButton(tr("Enable Mid Band"), this, 143, 146, &controls->m_band2EnabledModel, "mid_band_active", "mid_band_inactive", tr("Enable Mid Band"));
createPixmapButton(tr("Enable Low Band"), this, 143, 226, &controls->m_band3EnabledModel, "low_band_active", "low_band_inactive", tr("Enable Low Band"));
createKnob(KnobType::Bright26, this, 53, 43, &controls->m_inHighModel, tr("High Input Volume:"), " dB", tr("Input volume for high band"));
createKnob(KnobType::Bright26, this, 53, 123, &controls->m_inMidModel, tr("Mid Input Volume:"), " dB", tr("Input volume for mid band"));
createKnob(KnobType::Bright26, this, 53, 203, &controls->m_inLowModel, tr("Low Input Volume:"), " dB", tr("Input volume for low band"));
createKnob(KnobType::Bright26, this, 320, 43, &controls->m_outHighModel, tr("High Output Volume:"), " dB", tr("Output volume for high band"));
createKnob(KnobType::Bright26, this, 320, 123, &controls->m_outMidModel, tr("Mid Output Volume:"), " dB", tr("Output volume for mid band"));
createKnob(KnobType::Bright26, this, 320, 203, &controls->m_outLowModel, tr("Low Output Volume:"), " dB", tr("Output volume for low band"));
createLcdFloatSpinBox(3, 3, "11green", tr("Above Threshold High"), this, 300, 13, &controls->m_aThreshHModel, tr("Downward compression threshold for high band"));
createLcdFloatSpinBox(3, 3, "11green", tr("Above Threshold Mid"), this, 300, 93, &controls->m_aThreshMModel, tr("Downward compression threshold for mid band"));
createLcdFloatSpinBox(3, 3, "11green", tr("Above Threshold Low"), this, 300, 173, &controls->m_aThreshLModel, tr("Downward compression threshold for low band"));
createLcdFloatSpinBox(2, 2, "11green", tr("Above Ratio High"), this, 284, 44, &controls->m_aRatioHModel, tr("Downward compression ratio for high band"));
createLcdFloatSpinBox(2, 2, "11green", tr("Above Ratio Mid"), this, 284, 124, &controls->m_aRatioMModel, tr("Downward compression ratio for mid band"));
createLcdFloatSpinBox(2, 2, "11green", tr("Above Ratio Low"), this, 284, 204, &controls->m_aRatioLModel, tr("Downward compression ratio for low band"));
createLcdFloatSpinBox(3, 3, "11green", tr("Below Threshold High"), this, 59, 13, &controls->m_bThreshHModel, tr("Upward compression threshold for high band"));
createLcdFloatSpinBox(3, 3, "11green", tr("Below Threshold Mid"), this, 59, 93, &controls->m_bThreshMModel, tr("Upward compression threshold for mid band"));
createLcdFloatSpinBox(3, 3, "11green", tr("Below Threshold Low"), this, 59, 173, &controls->m_bThreshLModel, tr("Upward compression threshold for low band"));
createLcdFloatSpinBox(2, 2, "11green", tr("Below Ratio High"), this, 87, 44, &controls->m_bRatioHModel, tr("Upward compression ratio for high band"));
createLcdFloatSpinBox(2, 2, "11green", tr("Below Ratio Mid"), this, 87, 124, &controls->m_bRatioMModel, tr("Upward compression ratio for mid band"));
createLcdFloatSpinBox(2, 2, "11green", tr("Below Ratio Low"), this, 87, 204, &controls->m_bRatioLModel, tr("Upward compression ratio for low band"));
createKnob(KnobType::Small17, this, 120, 61, &controls->m_atkHModel, tr("Attack High:"), " ms", tr("Attack time for high band"));
createKnob(KnobType::Small17, this, 120, 141, &controls->m_atkMModel, tr("Attack Mid:"), " ms", tr("Attack time for mid band"));
createKnob(KnobType::Small17, this, 120, 221, &controls->m_atkLModel, tr("Attack Low:"), " ms", tr("Attack time for low band"));
createKnob(KnobType::Small17, this, 261, 61, &controls->m_relHModel, tr("Release High:"), " ms", tr("Release time for high band"));
createKnob(KnobType::Small17, this, 261, 141, &controls->m_relMModel, tr("Release Mid:"), " ms", tr("Release time for mid band"));
createKnob(KnobType::Small17, this, 261, 221, &controls->m_relLModel, tr("Release Low:"), " ms", tr("Release time for low band"));
createKnob(KnobType::Small17, this, 380, 42, &controls->m_rmsTimeModel, tr("RMS Time:"), " ms", tr("RMS size for sidechain signal (set to 0 for Peak mode)"));
createKnob(KnobType::Small17, this, 356, 42, &controls->m_kneeModel, tr("Knee:"), " dB", tr("Knee size for all compressors"));
createKnob(KnobType::Small17, this, 24, 146, &controls->m_rangeModel, tr("Range:"), " dB", tr("Maximum gain increase for all bands"));
createKnob(KnobType::Small17, this, 13, 114, &controls->m_balanceModel, tr("Balance:"), " dB", tr("Bias input volume towards one channel"));
createPixmapButton(tr("Scale output volume with Depth"), this, 51, 0, &controls->m_depthScalingModel, "depthScaling_active", "depthScaling_inactive",
tr("Scale output volume with Depth parameter"));
createPixmapButton(tr("Stereo Link"), this, 52, 237, &controls->m_stereoLinkModel, "stereoLink_active", "stereoLink_inactive",
tr("Apply same gain change to both channels"));
createKnob(KnobType::Small17, this, 24, 80, &controls->m_autoTimeModel, tr("Auto Time:"), "", tr("Speed up attack and release times when transients occur"));
createKnob(KnobType::Bright26, this, 363, 4, &controls->m_mixModel, tr("Mix:"), "", tr("Wet/Dry of all bands"));
m_feedbackButton = createPixmapButton(tr("Feedback"), this, 317, 238, &controls->m_feedbackModel, "feedback_active", "feedback_inactive",
tr("Use output as sidechain signal instead of input"));
createPixmapButton(tr("Mid/Side"), this, 285, 238, &controls->m_midsideModel, "midside_active", "midside_inactive", tr("Compress mid/side channels instead of left/right"));
m_lowSideUpwardSuppressButton = createPixmapButton(tr("Suppress upward compression for side band"), this, 106, 180, &controls->m_lowSideUpwardSuppressModel,
"lowSideUpwardSuppress_active", "lowSideUpwardSuppress_inactive", tr("Suppress upward compression for side band"));
createPixmapButton(tr("Lookahead"), this, 147, 0, &controls->m_lookaheadEnableModel, "lookahead_active", "lookahead_inactive",
tr(("Enable lookahead with fixed " + std::to_string(int(LOMM_MAX_LOOKAHEAD)) + " ms latency").c_str()));
createLcdFloatSpinBox(2, 2, "11green", tr("Lookahead"), this, 214, 2, &controls->m_lookaheadModel, tr("Lookahead length"));
PixmapButton* initButton = createPixmapButton(tr("Clear all parameters"), this, 84, 237, nullptr, "init_active", "init_inactive", tr("Clear all parameters"));
connect(initButton, SIGNAL(clicked()), m_controls, SLOT(resetAllParameters()));
connect(&controls->m_lookaheadEnableModel, SIGNAL(dataChanged()), this, SLOT(updateFeedbackVisibility()));
connect(&controls->m_midsideModel, SIGNAL(dataChanged()), this, SLOT(updateLowSideUpwardSuppressVisibility()));
connect(getGUI()->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(updateDisplay()));
emit updateFeedbackVisibility();
emit updateLowSideUpwardSuppressVisibility();
}
void LOMMControlDialog::updateFeedbackVisibility()
{
m_feedbackButton->setVisible(!m_controls->m_lookaheadEnableModel.value());
}
void LOMMControlDialog::updateLowSideUpwardSuppressVisibility()
{
m_lowSideUpwardSuppressButton->setVisible(m_controls->m_midsideModel.value());
}
void LOMMControlDialog::updateDisplay()
{
update();
}
void LOMMControlDialog::paintEvent(QPaintEvent *event)
{
if (!isVisible()) { return; }
QPainter p;
p.begin(this);
// Draw threshold lines
QColor aColor(255, 255, 0, 31);
QColor bColor(255, 0, 0, 31);
QPen aPen(QColor(255, 255, 0, 255), 1);
QPen bPen(QColor(255, 0, 0, 255), 1);
int thresholdsX[] = {dbfsToX(m_controls->m_aThreshHModel.value()),
dbfsToX(m_controls->m_aThreshMModel.value()),
dbfsToX(m_controls->m_aThreshLModel.value()),
dbfsToX(m_controls->m_bThreshHModel.value()),
dbfsToX(m_controls->m_bThreshMModel.value()),
dbfsToX(m_controls->m_bThreshLModel.value())};
for (int i = 0; i < 3; ++i) {
p.setPen(aPen);
p.fillRect(thresholdsX[i], LOMM_DISPLAY_Y[2 * i], LOMM_DISPLAY_X + LOMM_DISPLAY_WIDTH - thresholdsX[i], LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT - LOMM_DISPLAY_Y[2 * i], aColor);
p.drawLine(thresholdsX[i], LOMM_DISPLAY_Y[2 * i], thresholdsX[i], LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT);
p.setPen(bPen);
p.fillRect(LOMM_DISPLAY_X, LOMM_DISPLAY_Y[2 * i], thresholdsX[i + 3] - LOMM_DISPLAY_X, LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT - LOMM_DISPLAY_Y[2 * i], bColor);
p.drawLine(thresholdsX[i + 3], LOMM_DISPLAY_Y[2 * i], thresholdsX[i + 3], LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT);
}
QPen inputPen(QColor(200, 200, 200, 80), 1);
QPen outputPen(QColor(255, 255, 255, 255), 1);
for (int i = 0; i < 3; ++i) {
// Draw input lines
p.setPen(inputPen);
int inL = dbfsToX(m_controls->m_effect->m_displayIn[i][0]);
p.drawLine(inL, LOMM_DISPLAY_Y[2 * i] + 4, inL, LOMM_DISPLAY_Y[2 * i] + LOMM_DISPLAY_HEIGHT);
int inR = dbfsToX(m_controls->m_effect->m_displayIn[i][1]);
p.drawLine(inR, LOMM_DISPLAY_Y[2 * i + 1], inR, LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT - 4);
// Draw output lines
p.setPen(outputPen);
int outL = dbfsToX(m_controls->m_effect->m_displayOut[i][0]);
p.drawLine(outL, LOMM_DISPLAY_Y[2 * i], outL, LOMM_DISPLAY_Y[2 * i] + LOMM_DISPLAY_HEIGHT);
int outR = dbfsToX(m_controls->m_effect->m_displayOut[i][1]);
p.drawLine(outR, LOMM_DISPLAY_Y[2 * i + 1], outR, LOMM_DISPLAY_Y[2 * i + 1] + LOMM_DISPLAY_HEIGHT);
}
p.end();
}
int LOMMControlDialog::dbfsToX(float dbfs)
{
float returnX = (dbfs - LOMM_DISPLAY_MIN) / (LOMM_DISPLAY_MAX - LOMM_DISPLAY_MIN);
returnX = qBound(LOMM_DISPLAY_X, LOMM_DISPLAY_X + returnX * LOMM_DISPLAY_WIDTH, LOMM_DISPLAY_X + LOMM_DISPLAY_WIDTH);
return returnX;
}
float LOMMControlDialog::xToDbfs(int x)
{
float xNorm = static_cast<float>(x - LOMM_DISPLAY_X) / LOMM_DISPLAY_WIDTH;
float dbfs = xNorm * (LOMM_DISPLAY_MAX - LOMM_DISPLAY_MIN) + LOMM_DISPLAY_MIN;
return dbfs;
}
void LOMMControlDialog::mousePressEvent(QMouseEvent* event)
{
if ((event->button() == Qt::LeftButton || event->button() == Qt::MiddleButton) && !(event->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier)))
{
const QPoint& p = event->pos();
if (LOMM_DISPLAY_X - 10 <= p.x() && p.x() <= LOMM_DISPLAY_X + LOMM_DISPLAY_WIDTH + 10)
{
FloatModel* aThresh[] = {&m_controls->m_aThreshHModel, &m_controls->m_aThreshMModel, &m_controls->m_aThreshLModel};
FloatModel* bThresh[] = {&m_controls->m_bThreshHModel, &m_controls->m_bThreshMModel, &m_controls->m_bThreshLModel};
for (int i = 0; i < 3; ++i)
{
if (LOMM_DISPLAY_Y[i * 2] <= p.y() && p.y() <= LOMM_DISPLAY_Y[i * 2 + 1] + LOMM_DISPLAY_HEIGHT)
{
int behavior = (p.x() < dbfsToX(bThresh[i]->value())) ? 0 : (p.x() > dbfsToX(aThresh[i]->value())) ? 1 : 2;
if (event->button() == Qt::MiddleButton)
{
if (behavior == 0 || behavior == 2) {bThresh[i]->reset();}
if (behavior == 1 || behavior == 2) {aThresh[i]->reset();}
return;
}
m_bandDrag = i;
m_lastMousePos = p;
m_buttonPressed = true;
m_dragType = behavior;
return;
}
}
}
}
}
void LOMMControlDialog::mouseMoveEvent(QMouseEvent * event)
{
if (m_buttonPressed && event->pos() != m_lastMousePos)
{
const float distance = event->pos().x() - m_lastMousePos.x();
float dbDistance = distance * LOMM_DISPLAY_DB_PER_PIXEL;
m_lastMousePos = event->pos();
FloatModel* aModel[] = {&m_controls->m_aThreshHModel, &m_controls->m_aThreshMModel, &m_controls->m_aThreshLModel};
FloatModel* bModel[] = {&m_controls->m_bThreshHModel, &m_controls->m_bThreshMModel, &m_controls->m_bThreshLModel};
float bVal = bModel[m_bandDrag]->value();
float aVal = aModel[m_bandDrag]->value();
if (m_dragType == 0)
{
bModel[m_bandDrag]->setValue(bVal + dbDistance);
}
else if (m_dragType == 1)
{
aModel[m_bandDrag]->setValue(aVal + dbDistance);
}
else
{
dbDistance = qBound(bModel[m_bandDrag]->minValue(), bVal + dbDistance, bModel[m_bandDrag]->maxValue()) - bVal;
dbDistance = qBound(aModel[m_bandDrag]->minValue(), aVal + dbDistance, aModel[m_bandDrag]->maxValue()) - aVal;
bModel[m_bandDrag]->setValue(bVal + dbDistance);
aModel[m_bandDrag]->setValue(aVal + dbDistance);
}
}
}
void LOMMControlDialog::mouseReleaseEvent(QMouseEvent* event)
{
if (event && event->button() == Qt::LeftButton)
{
m_buttonPressed = false;
}
}
} // namespace lmms::gui

View File

@@ -0,0 +1,129 @@
/*
* LOMMControlDialog.h
*
* Copyright (c) 2023 Lost Robot <r94231/at/gmail/dot/com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_GUI_LOMM_CONTROL_DIALOG_H
#define LMMS_GUI_LOMM_CONTROL_DIALOG_H
#include "EffectControlDialog.h"
#include <QMouseEvent>
#include <QPainter>
#include "embed.h"
#include "GuiApplication.h"
#include "Knob.h"
#include "LcdFloatSpinBox.h"
#include "LcdSpinBox.h"
#include "LedCheckBox.h"
#include "MainWindow.h"
#include "PixmapButton.h"
namespace lmms
{
inline constexpr float LOMM_DISPLAY_MIN = -72;
inline constexpr float LOMM_DISPLAY_MAX = 0;
inline constexpr float LOMM_DISPLAY_X = 125;
inline constexpr float LOMM_DISPLAY_Y[6] = {24, 41, 106, 123, 186, 203};
inline constexpr float LOMM_DISPLAY_WIDTH = 150;
inline constexpr float LOMM_DISPLAY_HEIGHT = 13;
inline constexpr float LOMM_DISPLAY_DB_PER_PIXEL = (LOMM_DISPLAY_MAX - LOMM_DISPLAY_MIN) / LOMM_DISPLAY_WIDTH;
class LOMMControls;
namespace gui
{
class LOMMControlDialog : public EffectControlDialog
{
Q_OBJECT
public:
LOMMControlDialog(LOMMControls* controls);
~LOMMControlDialog() override = default;
int dbfsToX(float dbfs);
float xToDbfs(int x);
Knob* createKnob(KnobType knobType, QWidget* parent, int x, int y, FloatModel* model, const QString& hintText, const QString& unit, const QString& toolTip)
{
Knob* knob = new Knob(knobType, parent);
knob->move(x, y);
knob->setModel(model);
knob->setHintText(hintText, unit);
knob->setToolTip(toolTip);
return knob;
}
LcdFloatSpinBox* createLcdFloatSpinBox(int integerDigits, int decimalDigits, const QString& color, const QString& unit, QWidget* parent, int x, int y, FloatModel* model, const QString& toolTip)
{
LcdFloatSpinBox* spinBox = new LcdFloatSpinBox(integerDigits, decimalDigits, color, unit, parent);
spinBox->move(x, y);
spinBox->setModel(model);
spinBox->setSeamless(true, true);
spinBox->setToolTip(toolTip);
return spinBox;
}
PixmapButton* createPixmapButton(const QString& text, QWidget* parent, int x, int y, BoolModel* model, const QString& activeIcon, const QString& inactiveIcon, const QString& tooltip)
{
PixmapButton* button = new PixmapButton(parent, text);
button->move(x, y);
button->setCheckable(true);
if (model) { button->setModel(model); }
button->setActiveGraphic(PLUGIN_NAME::getIconPixmap(activeIcon));
button->setInactiveGraphic(PLUGIN_NAME::getIconPixmap(inactiveIcon));
button->setToolTip(tooltip);
return button;
}
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
private:
LOMMControls* m_controls;
QPoint m_lastMousePos;
bool m_buttonPressed = false;
int m_bandDrag = 0;
int m_dragType = -1;
PixmapButton* m_feedbackButton;
PixmapButton* m_lowSideUpwardSuppressButton;
private slots:
void updateFeedbackVisibility();
void updateLowSideUpwardSuppressVisibility();
void updateDisplay();
};
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_LOMM_CONTROL_DIALOG_H

View File

@@ -0,0 +1,277 @@
/*
* LOMMControls.cpp
*
* Copyright (c) 2023 Lost Robot <r94231/at/gmail/dot/com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "LOMMControls.h"
#include "LOMM.h"
#include <QDomElement>
#include <QMessageBox>
namespace lmms
{
LOMMControls::LOMMControls(LOMMEffect* effect) :
EffectControls(effect),
m_effect(effect),
m_depthModel(0.4, 0, 1, 0.00001, this, tr("Depth")),
m_timeModel(1, 0, 10, 0.00001, this, tr("Time")),
m_inVolModel(0, -48, 48, 0.00001, this, tr("Input Volume")),
m_outVolModel(8, -48, 48, 0.00001, this, tr("Output Volume")),
m_upwardModel(1, 0, 2, 0.00001, this, tr("Upward Depth")),
m_downwardModel(1, 0, 2, 0.00001, this, tr("Downward Depth")),
m_split1Model(2500, 20, 20000, 0.01, this, tr("High/Mid Split")),
m_split2Model(88.3, 20, 20000, 0.01, this, tr("Mid/Low Split")),
m_split1EnabledModel(true, this, tr("Enable High/Mid Split")),
m_split2EnabledModel(true, this, tr("Enable Mid/Low Split")),
m_band1EnabledModel(true, this, tr("Enable High Band")),
m_band2EnabledModel(true, this, tr("Enable Mid Band")),
m_band3EnabledModel(true, this, tr("Enable Low Band")),
m_inHighModel(0, -48, 48, 0.00001, this, tr("High Input Volume")),
m_inMidModel(0, -48, 48, 0.00001, this, tr("Mid Input Volume")),
m_inLowModel(0, -48, 48, 0.00001, this, tr("Low Input Volume")),
m_outHighModel(4.6, -48, 48, 0.00001, this, tr("High Output Volume")),
m_outMidModel(0.0, -48, 48, 0.00001, this, tr("Mid Output Volume")),
m_outLowModel(4.6, -48, 48, 0.00001, this, tr("Low Output Volume")),
m_aThreshHModel(-30.3, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Above Threshold High")),
m_aThreshMModel(-25.0, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Above Threshold Mid")),
m_aThreshLModel(-28.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Above Threshold Low")),
m_aRatioHModel(99.99, 1, 99.99, 0.01, this, tr("Above Ratio High")),
m_aRatioMModel(66.7, 1, 99.99, 0.01, this, tr("Above Ratio Mid")),
m_aRatioLModel(66.7, 1, 99.99, 0.01, this, tr("Above Ratio Low")),
m_bThreshHModel(-35.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Below Threshold High")),
m_bThreshMModel(-36.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Below Threshold Mid")),
m_bThreshLModel(-35.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Below Threshold Low")),
m_bRatioHModel(4.17, 1, 99.99, 0.01, this, tr("Below Ratio High")),
m_bRatioMModel(4.17, 1, 99.99, 0.01, this, tr("Below Ratio Mid")),
m_bRatioLModel(4.17, 1, 99.99, 0.01, this, tr("Below Ratio Low")),
m_atkHModel(13.5, 0, 1000, 0.001, this, tr("Attack High")),
m_atkMModel(22.4, 0, 1000, 0.001, this, tr("Attack Mid")),
m_atkLModel(47.8, 0, 1000, 0.001, this, tr("Attack Low")),
m_relHModel(132, 0, 1000, 0.001, this, tr("Release High")),
m_relMModel(282, 0, 1000, 0.001, this, tr("Release Mid")),
m_relLModel(282, 0, 1000, 0.001, this, tr("Release Low")),
m_rmsTimeModel(10, 0, 500, 0.001, this, tr("RMS Time")),
m_kneeModel(6, 0, 36, 0.00001, this, tr("Knee")),
m_rangeModel(36, 0, 96, 0.00001, this, tr("Range")),
m_balanceModel(0, -18, 18, 0.00001, this, tr("Balance")),
m_depthScalingModel(true, this, tr("Scale output volume with Depth")),
m_stereoLinkModel(false, this, tr("Stereo Link")),
m_autoTimeModel(0, 0, 1, 0.00001, this, tr("Auto Time")),
m_mixModel(1, 0, 1, 0.00001, this, tr("Mix")),
m_feedbackModel(false, this, tr("Feedback")),
m_midsideModel(false, this, tr("Mid/Side")),
m_lookaheadEnableModel(false, this, tr("Lookahead")),
m_lookaheadModel(0.f, 0.f, LOMM_MAX_LOOKAHEAD, 0.01, this, tr("Lookahead Length")),
m_lowSideUpwardSuppressModel(false, this, tr("Suppress upward compression for side band"))
{
auto models = {&m_timeModel, &m_inVolModel, &m_outVolModel, &m_inHighModel, &m_inMidModel,
&m_inLowModel, &m_outHighModel, &m_outMidModel, &m_outLowModel, &m_aRatioHModel,
&m_aRatioMModel, &m_aRatioLModel, &m_bRatioHModel, &m_bRatioMModel, &m_bRatioLModel,
&m_atkHModel, &m_atkMModel, &m_atkLModel, &m_relHModel, &m_relMModel, &m_relLModel,
&m_rmsTimeModel, &m_balanceModel};
for (auto model : models) { model->setScaleLogarithmic(true); }
}
void LOMMControls::resetAllParameters()
{
int choice = QMessageBox::question(m_view, "Clear Plugin Settings", "Are you sure you want to clear all parameters?\n(This wipes LOMM to a clean slate, not the default preset.)", QMessageBox::Yes | QMessageBox::No);
if (choice != QMessageBox::Yes) { return; }
// give the user a chance to beg LMMS for forgiveness
addJournalCheckPoint();
// This plugin's normal default values are fairly close to what they'd want in most applications.
// The Init button is there so the user can start from a clean slate instead.
// These are those values.
setInitAndReset(m_depthModel, 1);
setInitAndReset(m_timeModel, 1);
setInitAndReset(m_inVolModel, 0);
setInitAndReset(m_outVolModel, 0);
setInitAndReset(m_upwardModel, 1);
setInitAndReset(m_downwardModel, 1);
setInitAndReset(m_split1Model, 2500);
setInitAndReset(m_split2Model, 88);
setInitAndReset(m_split1EnabledModel, true);
setInitAndReset(m_split2EnabledModel, true);
setInitAndReset(m_band1EnabledModel, true);
setInitAndReset(m_band2EnabledModel, true);
setInitAndReset(m_band3EnabledModel, true);
setInitAndReset(m_inHighModel, 0);
setInitAndReset(m_inMidModel, 0);
setInitAndReset(m_inLowModel, 0);
setInitAndReset(m_outHighModel, 0);
setInitAndReset(m_outMidModel, 0);
setInitAndReset(m_outLowModel, 0);
setInitAndReset(m_aThreshHModel, m_aThreshHModel.maxValue());
setInitAndReset(m_aThreshMModel, m_aThreshMModel.maxValue());
setInitAndReset(m_aThreshLModel, m_aThreshLModel.maxValue());
setInitAndReset(m_aRatioHModel, 1);
setInitAndReset(m_aRatioMModel, 1);
setInitAndReset(m_aRatioLModel, 1);
setInitAndReset(m_bThreshHModel, m_bThreshHModel.minValue());
setInitAndReset(m_bThreshMModel, m_bThreshMModel.minValue());
setInitAndReset(m_bThreshLModel, m_bThreshLModel.minValue());
setInitAndReset(m_bRatioHModel, 1);
setInitAndReset(m_bRatioMModel, 1);
setInitAndReset(m_bRatioLModel, 1);
setInitAndReset(m_atkHModel, 13.5);
setInitAndReset(m_atkMModel, 22.4);
setInitAndReset(m_atkLModel, 47.8);
setInitAndReset(m_relHModel, 132);
setInitAndReset(m_relMModel, 282);
setInitAndReset(m_relLModel, 282);
setInitAndReset(m_rmsTimeModel, 10);
setInitAndReset(m_kneeModel, 6);
setInitAndReset(m_rangeModel, 36);
setInitAndReset(m_balanceModel, 0);
setInitAndReset(m_depthScalingModel, true);
setInitAndReset(m_stereoLinkModel, false);
setInitAndReset(m_autoTimeModel, 0);
setInitAndReset(m_mixModel, 1);
setInitAndReset(m_feedbackModel, false);
setInitAndReset(m_midsideModel, false);
setInitAndReset(m_lookaheadEnableModel, false);
setInitAndReset(m_lookaheadModel, 0.f);
setInitAndReset(m_lowSideUpwardSuppressModel, false);
}
void LOMMControls::loadSettings(const QDomElement& parent)
{
m_depthModel.loadSettings(parent, "depth");
m_timeModel.loadSettings(parent, "time");
m_inVolModel.loadSettings(parent, "inVol");
m_outVolModel.loadSettings(parent, "outVol");
m_upwardModel.loadSettings(parent, "upward");
m_downwardModel.loadSettings(parent, "downward");
m_split1Model.loadSettings(parent, "split1");
m_split2Model.loadSettings(parent, "split2");
m_split1EnabledModel.loadSettings(parent, "split1Enabled");
m_split2EnabledModel.loadSettings(parent, "split2Enabled");
m_band1EnabledModel.loadSettings(parent, "band1Enabled");
m_band2EnabledModel.loadSettings(parent, "band2Enabled");
m_band3EnabledModel.loadSettings(parent, "band3Enabled");
m_inHighModel.loadSettings(parent, "inHigh");
m_inMidModel.loadSettings(parent, "inMid");
m_inLowModel.loadSettings(parent, "inLow");
m_outHighModel.loadSettings(parent, "outHigh");
m_outMidModel.loadSettings(parent, "outMid");
m_outLowModel.loadSettings(parent, "outLow");
m_aThreshHModel.loadSettings(parent, "aThreshH");
m_aThreshMModel.loadSettings(parent, "aThreshM");
m_aThreshLModel.loadSettings(parent, "aThreshL");
m_aRatioHModel.loadSettings(parent, "aRatioH");
m_aRatioMModel.loadSettings(parent, "aRatioM");
m_aRatioLModel.loadSettings(parent, "aRatioL");
m_bThreshHModel.loadSettings(parent, "bThreshH");
m_bThreshMModel.loadSettings(parent, "bThreshM");
m_bThreshLModel.loadSettings(parent, "bThreshL");
m_bRatioHModel.loadSettings(parent, "bRatioH");
m_bRatioMModel.loadSettings(parent, "bRatioM");
m_bRatioLModel.loadSettings(parent, "bRatioL");
m_atkHModel.loadSettings(parent, "atkH");
m_atkMModel.loadSettings(parent, "atkM");
m_atkLModel.loadSettings(parent, "atkL");
m_relHModel.loadSettings(parent, "relH");
m_relMModel.loadSettings(parent, "relM");
m_relLModel.loadSettings(parent, "relL");
m_rmsTimeModel.loadSettings(parent, "rmsTime");
m_kneeModel.loadSettings(parent, "knee");
m_rangeModel.loadSettings(parent, "range");
m_balanceModel.loadSettings(parent, "balance");
m_depthScalingModel.loadSettings(parent, "depthScaling");
m_stereoLinkModel.loadSettings(parent, "stereoLink");
m_autoTimeModel.loadSettings(parent, "autoTime");
m_mixModel.loadSettings(parent, "mix");
m_feedbackModel.loadSettings(parent, "feedback");
m_midsideModel.loadSettings(parent, "midside");
m_lookaheadEnableModel.loadSettings(parent, "lookaheadEnable");
m_lookaheadModel.loadSettings(parent, "lookahead");
m_lowSideUpwardSuppressModel.loadSettings(parent, "lowSideUpwardSuppress");
}
void LOMMControls::saveSettings(QDomDocument& doc, QDomElement& parent)
{
m_depthModel.saveSettings(doc, parent, "depth");
m_timeModel.saveSettings(doc, parent, "time");
m_inVolModel.saveSettings(doc, parent, "inVol");
m_outVolModel.saveSettings(doc, parent, "outVol");
m_upwardModel.saveSettings(doc, parent, "upward");
m_downwardModel.saveSettings(doc, parent, "downward");
m_split1Model.saveSettings(doc, parent, "split1");
m_split2Model.saveSettings(doc, parent, "split2");
m_split1EnabledModel.saveSettings(doc, parent, "split1Enabled");
m_split2EnabledModel.saveSettings(doc, parent, "split2Enabled");
m_band1EnabledModel.saveSettings(doc, parent, "band1Enabled");
m_band2EnabledModel.saveSettings(doc, parent, "band2Enabled");
m_band3EnabledModel.saveSettings(doc, parent, "band3Enabled");
m_inHighModel.saveSettings(doc, parent, "inHigh");
m_inMidModel.saveSettings(doc, parent, "inMid");
m_inLowModel.saveSettings(doc, parent, "inLow");
m_outHighModel.saveSettings(doc, parent, "outHigh");
m_outMidModel.saveSettings(doc, parent, "outMid");
m_outLowModel.saveSettings(doc, parent, "outLow");
m_aThreshHModel.saveSettings(doc, parent, "aThreshH");
m_aThreshMModel.saveSettings(doc, parent, "aThreshM");
m_aThreshLModel.saveSettings(doc, parent, "aThreshL");
m_aRatioHModel.saveSettings(doc, parent, "aRatioH");
m_aRatioMModel.saveSettings(doc, parent, "aRatioM");
m_aRatioLModel.saveSettings(doc, parent, "aRatioL");
m_bThreshHModel.saveSettings(doc, parent, "bThreshH");
m_bThreshMModel.saveSettings(doc, parent, "bThreshM");
m_bThreshLModel.saveSettings(doc, parent, "bThreshL");
m_bRatioHModel.saveSettings(doc, parent, "bRatioH");
m_bRatioMModel.saveSettings(doc, parent, "bRatioM");
m_bRatioLModel.saveSettings(doc, parent, "bRatioL");
m_atkHModel.saveSettings(doc, parent, "atkH");
m_atkMModel.saveSettings(doc, parent, "atkM");
m_atkLModel.saveSettings(doc, parent, "atkL");
m_relHModel.saveSettings(doc, parent, "relH");
m_relMModel.saveSettings(doc, parent, "relM");
m_relLModel.saveSettings(doc, parent, "relL");
m_rmsTimeModel.saveSettings(doc, parent, "rmsTime");
m_kneeModel.saveSettings(doc, parent, "knee");
m_rangeModel.saveSettings(doc, parent, "range");
m_balanceModel.saveSettings(doc, parent, "balance");
m_depthScalingModel.saveSettings(doc, parent, "depthScaling");
m_stereoLinkModel.saveSettings(doc, parent, "stereoLink");
m_autoTimeModel.saveSettings(doc, parent, "autoTime");
m_mixModel.saveSettings(doc, parent, "mix");
m_feedbackModel.saveSettings(doc, parent, "feedback");
m_midsideModel.saveSettings(doc, parent, "midside");
m_lookaheadEnableModel.saveSettings(doc, parent, "lookaheadEnable");
m_lookaheadModel.saveSettings(doc, parent, "lookahead");
m_lowSideUpwardSuppressModel.saveSettings(doc, parent, "lowSideUpwardSuppress");
}
} // namespace lmms

136
plugins/LOMM/LOMMControls.h Normal file
View File

@@ -0,0 +1,136 @@
/*
* LOMMControls.h
*
* Copyright (c) 2023 Lost Robot <r94231/at/gmail/dot/com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_LOMM_CONTROLS_H
#define LMMS_LOMM_CONTROLS_H
#include "LOMMControlDialog.h"
#include "EffectControls.h"
namespace lmms
{
class LOMMEffect;
namespace gui
{
class LOMMControlDialog;
}
class LOMMControls : public EffectControls
{
Q_OBJECT
public:
LOMMControls(LOMMEffect* effect);
~LOMMControls() override = default;
void saveSettings(QDomDocument & doc, QDomElement & parent) override;
void loadSettings(const QDomElement & parent) override;
inline QString nodeName() const override
{
return "LOMMControls";
}
int controlCount() override
{
return 49;
}
gui::EffectControlDialog* createView() override
{
m_view = new gui::LOMMControlDialog(this);
return m_view;
}
template <typename T>
void setInitAndReset(AutomatableModel& model, T initValue)
{
model.setInitValue(initValue);
model.reset();
}
public slots:
void resetAllParameters();
private:
LOMMEffect* m_effect;
gui::LOMMControlDialog* m_view;
FloatModel m_depthModel;
FloatModel m_timeModel;
FloatModel m_inVolModel;
FloatModel m_outVolModel;
FloatModel m_upwardModel;
FloatModel m_downwardModel;
FloatModel m_split1Model;
FloatModel m_split2Model;
BoolModel m_split1EnabledModel;
BoolModel m_split2EnabledModel;
BoolModel m_band1EnabledModel;
BoolModel m_band2EnabledModel;
BoolModel m_band3EnabledModel;
FloatModel m_inHighModel;
FloatModel m_inMidModel;
FloatModel m_inLowModel;
FloatModel m_outHighModel;
FloatModel m_outMidModel;
FloatModel m_outLowModel;
FloatModel m_aThreshHModel;
FloatModel m_aThreshMModel;
FloatModel m_aThreshLModel;
FloatModel m_aRatioHModel;
FloatModel m_aRatioMModel;
FloatModel m_aRatioLModel;
FloatModel m_bThreshHModel;
FloatModel m_bThreshMModel;
FloatModel m_bThreshLModel;
FloatModel m_bRatioHModel;
FloatModel m_bRatioMModel;
FloatModel m_bRatioLModel;
FloatModel m_atkHModel;
FloatModel m_atkMModel;
FloatModel m_atkLModel;
FloatModel m_relHModel;
FloatModel m_relMModel;
FloatModel m_relLModel;
FloatModel m_rmsTimeModel;
FloatModel m_kneeModel;
FloatModel m_rangeModel;
FloatModel m_balanceModel;
BoolModel m_depthScalingModel;
BoolModel m_stereoLinkModel;
FloatModel m_autoTimeModel;
FloatModel m_mixModel;
BoolModel m_feedbackModel;
BoolModel m_midsideModel;
BoolModel m_lookaheadEnableModel;
FloatModel m_lookaheadModel;
BoolModel m_lowSideUpwardSuppressModel;
friend class gui::LOMMControlDialog;
friend class LOMMEffect;
};
} // namespace lmms
#endif // LMMS_LOMM_CONTROLS_H

BIN
plugins/LOMM/artwork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
plugins/LOMM/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB