diff --git a/src/effects/Compressor2.cpp b/src/effects/Compressor2.cpp index 345029cb5..1e61ec65b 100644 --- a/src/effects/Compressor2.cpp +++ b/src/effects/Compressor2.cpp @@ -18,6 +18,7 @@ #include "Compressor2.h" #include +#include #include #include @@ -270,6 +271,9 @@ void EffectCompressor2::PopulateOrExchange(ShuttleGui & S) plot = mGainPlot->GetPlotData(0); plot->pen = std::unique_ptr( safenew wxPen(AColor::WideEnvelopePen)); + plot->xdata.resize(61); + plot->ydata.resize(61); + std::iota(plot->xdata.begin(), plot->xdata.end(), -60); mResponsePlot = S.MinSize( { 200, 200 } ) .AddPlot({}, 0, 5, -0.2, 1.2, XO("s"), XO(""), @@ -465,6 +469,46 @@ bool EffectCompressor2::TransferDataFromWindow() // EffectCompressor2 implementation +void EffectCompressor2::InitGainCalculation() +{ + mMakeupGainDB = mMakeupGainPct / 100.0 * + -(mThresholdDB * (1.0 - 1.0 / mRatio)); + mMakeupGain = DB_TO_LINEAR(mMakeupGainDB); +} + +double EffectCompressor2::CompressorGain(double env) +{ + double kneeCond; + double envDB = LINEAR_TO_DB(env); + + // envDB can become NaN is env is exactly zero. + // As solution, use a very low dB value to prevent NaN propagation. + if(isnan(envDB)) + envDB = -200; + + kneeCond = 2.0 * (envDB - mThresholdDB); + if(kneeCond < -mKneeWidthDB) + { + // Below threshold: only apply make-up gain + return mMakeupGain; + } + else if(kneeCond >= mKneeWidthDB) + { + // Above threshold: apply compression and make-up gain + return DB_TO_LINEAR(mThresholdDB + + (envDB - mThresholdDB) / mRatio + mMakeupGainDB - envDB); + } + else + { + // Within knee: apply interpolated compression and make-up gain + return DB_TO_LINEAR( + (1.0 / mRatio - 1.0) + * pow(envDB - mThresholdDB + mKneeWidthDB / 2.0, 2) + / (2.0 * mKneeWidthDB) + mMakeupGainDB); + } +} + + void EffectCompressor2::OnUpdateUI(wxCommandEvent & WXUNUSED(evt)) { if(!mIgnoreGuiEvents) @@ -474,14 +518,30 @@ void EffectCompressor2::OnUpdateUI(wxCommandEvent & WXUNUSED(evt)) void EffectCompressor2::UpdateUI() { - PlotData* plot; - plot = mGainPlot->GetPlotData(0); - plot->xdata = {-60, -40, 0}; - plot->ydata = {-60, -40, -20}; - mGainPlot->Refresh(false); + UpdateCompressorPlot(); + // TODO: update plots + PlotData* plot; plot = mResponsePlot->GetPlotData(1); plot->xdata = {0, 2, 2, 3, 3, 5}; plot->ydata = {0, 0.5, 1, 1, 0.5, 0}; mResponsePlot->Refresh(false); } + +void EffectCompressor2::UpdateCompressorPlot() +{ + PlotData* plot; + plot = mGainPlot->GetPlotData(0); + wxASSERT(plot->xdata.size() == plot->ydata.size()); + + InitGainCalculation(); + size_t xsize = plot->xdata.size(); + for(size_t i = 0; i < xsize; ++i) + plot->ydata[i] = plot->xdata[i] + + LINEAR_TO_DB(CompressorGain(DB_TO_LINEAR(plot->xdata[i]))); + +// XXX: accessibility but fails with TranslatableString required +// mGainPlot->SetName(wxString::Format( +// "Compressor gain reduction: %.1f dB", plot->ydata[xsize-1])); + mGainPlot->Refresh(false); +} diff --git a/src/effects/Compressor2.h b/src/effects/Compressor2.h index eb8f0906f..fc82aac4d 100644 --- a/src/effects/Compressor2.h +++ b/src/effects/Compressor2.h @@ -58,12 +58,14 @@ public: private: // EffectCompressor2 implementation + void InitGainCalculation(); + double CompressorGain(double env); bool UpdateProgress(); void OnUpdateUI(wxCommandEvent & evt); void UpdateUI(); + void UpdateCompressorPlot(); -private: int mAlgorithm; int mCompressBy; bool mStereoInd; @@ -78,6 +80,10 @@ private: double mMakeupGainPct; double mDryWetPct; + // cached intermediate values + double mMakeupGain; + double mMakeupGainDB; + Plot* mGainPlot; Plot* mResponsePlot; bool mIgnoreGuiEvents;