Audacity 3.2.0
LegacyCompressor.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 LegacyCompressor.cpp
6
7 Dominic Mazzoni
8 Martyn Shaw
9 Steve Jolly
10
11*******************************************************************//*******************************************************************/
17#include "LegacyCompressor.h"
18#include "EffectEditor.h"
19#include "LoadEffects.h"
20
21#include <wx/brush.h>
22#include <wx/checkbox.h>
23#include <wx/dcclient.h>
24#include <wx/slider.h>
25#include <wx/stattext.h>
26
27#include "AColor.h"
28#include "ShuttleGui.h"
29#include "Theme.h"
30#include "float_cast.h"
31#include "../widgets/LinearUpdater.h"
32#include "../widgets/Ruler.h"
33#include "../widgets/LinearDBFormat.h"
34#include "../widgets/LinearUpdater.h"
35
36#include "AllThemeResources.h"
37
38enum
39{
40 ID_Threshold = 10000,
45};
46
47namespace
48{
50}
51
52BEGIN_EVENT_TABLE(EffectLegacyCompressor, wxEvtHandler)
53EVT_SLIDER(wxID_ANY, EffectLegacyCompressor::OnSlider)
55
56// Effect Implementation
57
58namespace {
59
61 /* i18n-hint: usually leave this as is as dB doesn't get translated*/
62{ return XO("%3d dB").Format(value); }
63
65{ return XO("%.2f secs").Format( value ); }
66
68{ return XO("%.1f secs").Format( value ); }
69
70TranslatableString RatioTextFormat( int sliderValue, double value )
71{
72 auto format = (sliderValue % 10 == 0)
73 /* i18n-hint: Unless your language has a different convention for ratios,
74 * like 8:1, leave as is.*/
75 ? XO("%.0f:1")
76 /* i18n-hint: Unless your language has a different convention for ratios,
77 * like 8:1, leave as is.*/
78 : XO("%.1f:1");
79 return format.Format( value );
80}
81
82TranslatableString RatioLabelFormat( int sliderValue, double value )
83{
84 auto format = (sliderValue % 10 == 0)
85 ? XO("Ratio %.0f to 1")
86 : XO("Ratio %.1f to 1");
87 return format.Format( value );
88}
89
90}
91
92std::unique_ptr<EffectEditor> EffectLegacyCompressor::PopulateOrExchange(
94{
95 mUIParent = S.GetParent();
96 S.SetBorder(5);
97
98 S.StartHorizontalLay(wxEXPAND, true);
99 {
100 S.SetBorder(10);
102 S.GetParent(), wxID_ANY, mThresholdDB, mNoiseFloorDB, mRatio);
103 S.Prop(true)
104 .Position(wxEXPAND | wxALL)
105 .MinSize( { 400, 200 } )
106 .AddWindow(mPanel);
107 S.SetBorder(5);
108 }
109 S.EndHorizontalLay();
110
111 S.StartStatic( {} );
112 {
113 S.StartMultiColumn(3, wxEXPAND);
114 {
115 S.SetStretchyCol(1);
116 mThresholdLabel = S.AddVariableText(XO("&Threshold:"), true,
117 wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
119 .Name(XO("Threshold"))
120 .Style(wxSL_HORIZONTAL)
121 .AddSlider( {},
125 mThresholdText = S.AddVariableText(ThresholdFormat(999), true,
126 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
127
128 mNoiseFloorLabel = S.AddVariableText(XO("&Noise Floor:"), true,
129 wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
131 .Name(XO("Noise Floor"))
132 .Style(wxSL_HORIZONTAL)
133 .AddSlider( {},
137 mNoiseFloorText = S.AddVariableText(ThresholdFormat(999),
138 true, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
139
140 mRatioLabel = S.AddVariableText(XO("&Ratio:"), true,
141 wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
143 .Name(XO("Ratio"))
144 .Style(wxSL_HORIZONTAL)
145 .AddSlider( {},
148 Ratio.min * Ratio.scale);
149 mRatioSlider->SetPageSize(5);
150 mRatioText = S.AddVariableText(RatioTextFormat( 1, 99.9 ), true,
151 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
152
153 /* i18n-hint: Particularly in percussion, sounds can be regarded as having
154 * an 'attack' phase where the sound builds up and a 'decay' where the
155 * sound dies away. So this means 'onset duration'. */
156 mAttackLabel = S.AddVariableText(XO("&Attack Time:"), true,
157 wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
159 /* i18n-hint: Particularly in percussion, sounds can be regarded as having
160 * an 'attack' phase where the sound builds up and a 'decay' where the
161 * sound dies away. So this means 'onset duration'. */
162 .Name(XO("Attack Time"))
163 .Style(wxSL_HORIZONTAL)
164 .AddSlider( {},
168 mAttackText = S.AddVariableText(
169 AttackTimeFormat(9.99),
170 true, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
171
172 /* i18n-hint: Particularly in percussion, sounds can be regarded as having
173 * an 'attack' phase where the sound builds up and a 'decay' or 'release' where the
174 * sound dies away. */
175 mDecayLabel = S.AddVariableText(XO("R&elease Time:"), true,
176 wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
178 /* i18n-hint: Particularly in percussion, sounds can be regarded as having
179 * an 'attack' phase where the sound builds up and a 'decay' or 'release' where the
180 * sound dies away. */
181 .Name(XO("Release Time"))
182 .Style(wxSL_HORIZONTAL)
183 .AddSlider( {},
187
188 mDecayText = S.AddVariableText(
189 DecayTimeFormat(99.9),
190 true, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
191 }
192 S.EndMultiColumn();
193 }
194 S.EndStatic();
195
196 S.StartHorizontalLay(wxCENTER, false);
197 {
198 /* i18n-hint: Make-up, i.e. correct for any reduction, rather than fabricate it.*/
199 mGainCheckBox = S.AddCheckBox(XXO("Ma&ke-up gain for 0 dB after compressing"),
200 Normalize.def);
201 /* i18n-hint: "Compress" here means reduce variations of sound volume,
202 NOT related to file-size compression; Peaks means extremes in volume */
203 mPeakCheckBox = S.AddCheckBox(XXO("C&ompress based on Peaks"),
204 UsePeak.def);
205 }
206 S.EndHorizontalLay();
207 return nullptr;
208}
209
211{
214 mRatioSlider->SetValue(lrint(mRatio * Ratio.scale));
217 mGainCheckBox->SetValue(mNormalize);
218 mPeakCheckBox->SetValue(mUsePeak);
219
220 UpdateUI();
221
222 return true;
223}
224
226{
227 if (!mUIParent->Validate())
228 {
229 return false;
230 }
232}
233
235{
236 // To do: eliminate this by using control validators instead
237 mThresholdDB = (double) mThresholdSlider->GetValue();
238 mNoiseFloorDB = (double) mNoiseFloorSlider->GetValue() / NoiseFloor.scale;
239 mRatio = (double) mRatioSlider->GetValue() / Ratio.scale;
240 mAttackTime = (double) mAttackSlider->GetValue() / 100.0; //AttackTime.scale;
241 mDecayTime = (double) mDecaySlider->GetValue() / ReleaseTime.scale;
242 mNormalize = mGainCheckBox->GetValue();
243 mUsePeak = mPeakCheckBox->GetValue();
244
245 return true;
246}
247
248void EffectLegacyCompressor::OnSlider(wxCommandEvent& WXUNUSED(evt))
249{
251 UpdateUI();
252}
253
255{
256 mThresholdLabel->SetName(wxString::Format(_("Threshold %d dB"), (int) mThresholdDB));
257 mThresholdText->SetLabel(ThresholdFormat((int) mThresholdDB).Translation());
258 mThresholdText->SetName(mThresholdText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
259
260 mNoiseFloorLabel->SetName(wxString::Format(_("Noise Floor %d dB"), (int) mNoiseFloorDB));
261 mNoiseFloorText->SetLabel(ThresholdFormat((int) mNoiseFloorDB).Translation());
262 mNoiseFloorText->SetName(mNoiseFloorText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
263
264 mRatioLabel->SetName(
266 mRatioText->SetLabel(
268 mRatioText->SetName(mRatioText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
269
270 mAttackLabel->SetName(wxString::Format(_("Attack Time %.2f secs"), mAttackTime));
271 mAttackText->SetLabel(AttackTimeFormat(mAttackTime).Translation());
272 mAttackText->SetName(mAttackText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
273
274 mDecayLabel->SetName(wxString::Format(_("Release Time %.1f secs"), mDecayTime));
275 mDecayText->SetLabel(DecayTimeFormat(mDecayTime).Translation());
276 mDecayText->SetName(mDecayText->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
277
278 mPanel->Refresh(false);
279
280 return;
281}
282
283//----------------------------------------------------------------------------
284// EffectLegacyCompressorPanel
285//----------------------------------------------------------------------------
286
291
293 wxWindow* parent, wxWindowID winid, double& threshold, double& noiseFloor,
294 double& ratio)
295 : wxPanelWrapper(parent, winid)
296 , threshold(threshold)
297 , noiseFloor(noiseFloor)
298 , ratio(ratio)
299 {
300}
301
302void EffectLegacyCompressorPanel::OnPaint(wxPaintEvent& WXUNUSED(evt))
303{
304 wxPaintDC dc(this);
305
306 int width, height;
307 GetSize(&width, &height);
308
309 double rangeDB = 60;
310
311 // Ruler
312 int w = 0;
313 int h = 0;
314
316 vRuler.SetBounds(0, 0, width, height);
317 vRuler.SetOrientation(wxVERTICAL);
318 vRuler.SetRange(0, -rangeDB);
319 vRuler.SetUnits(XO("dB"));
320 vRuler.GetMaxSize(&w, NULL);
321
323 hRuler.SetBounds(0, 0, width, height);
324 hRuler.SetOrientation(wxHORIZONTAL);
325 hRuler.SetRange(-rangeDB, 0);
326 hRuler.SetUnits(XO("dB"));
327 hRuler.SetFlip(true);
328 hRuler.GetMaxSize(NULL, &h);
329
330 vRuler.SetBounds(0, 0, w, height - h);
331 hRuler.SetBounds(w, height - h, width, height);
332
333 vRuler.SetTickColour( theTheme.Colour( clrGraphLabels ));
334 hRuler.SetTickColour( theTheme.Colour( clrGraphLabels ));
335
336#if defined(__WXMSW__)
337 dc.Clear();
338#endif
339
340 wxRect border;
341 border.x = w;
342 border.y = 0;
343 border.width = width - w;
344 border.height = height - h + 1;
345
346 dc.SetBrush(*wxWHITE_BRUSH);
347 dc.SetPen(*wxBLACK_PEN);
348 dc.DrawRectangle(border);
349
350 wxRect envRect = border;
351 envRect.Deflate( 2, 2 );
352
353 int kneeX = lrint((rangeDB+threshold)*envRect.width/rangeDB);
354 int kneeY = lrint((rangeDB+threshold/ratio)*envRect.height/rangeDB);
355
356 int finalY = envRect.height;
357 int startY = lrint((threshold*(1.0/ratio-1.0))*envRect.height/rangeDB);
358
359 // Yellow line for threshold
360/* dc.SetPen(wxPen(wxColour(220, 220, 0), 1, wxSOLID));
361 AColor::Line(dc,
362 envRect.x,
363 envRect.y + envRect.height - kneeY,
364 envRect.x + envRect.width - 1,
365 envRect.y + envRect.height - kneeY);*/
366
367 // Was: Nice dark red line for the compression diagram
368// dc.SetPen(wxPen(wxColour(180, 40, 40), 3, wxSOLID));
369
370 // Nice blue line for compressor, same color as used in the waveform envelope.
371 dc.SetPen( AColor::WideEnvelopePen) ;
372
373 AColor::Line(dc,
374 envRect.x,
375 envRect.y + envRect.height - startY,
376 envRect.x + kneeX - 1,
377 envRect.y + envRect.height - kneeY);
378
379 AColor::Line(dc,
380 envRect.x + kneeX,
381 envRect.y + envRect.height - kneeY,
382 envRect.x + envRect.width - 1,
383 envRect.y + envRect.height - finalY);
384
385 // Paint border again
386 dc.SetBrush(*wxTRANSPARENT_BRUSH);
387 dc.SetPen(*wxBLACK_PEN);
388 dc.DrawRectangle(border);
389
390 vRuler.Draw(dc);
391 hRuler.Draw(dc);
392}
393
394void EffectLegacyCompressorPanel::OnSize(wxSizeEvent& WXUNUSED(evt))
395{
396 Refresh(false);
397}
END_EVENT_TABLE()
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
@ ID_Ratio
@ ID_Threshold
@ ID_Attack
@ ID_NoiseFloor
@ ID_Decay
#define safenew
Definition: MemoryX.h:10
THEME_API Theme theTheme
Definition: Theme.cpp:82
#define S(N)
Definition: ToChars.cpp:64
static wxPen WideEnvelopePen
Definition: AColor.h:117
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:194
Performs effect computation.
bool TransferDataFromWindow(EffectSettings &settings) override
wxStaticText * mRatioLabel
wxWeakRef< wxWindow > mUIParent
wxStaticText * mRatioText
bool TransferDataToWindow(const EffectSettings &settings) override
wxStaticText * mNoiseFloorText
wxStaticText * mAttackLabel
wxStaticText * mDecayLabel
wxStaticText * mThresholdText
wxStaticText * mNoiseFloorLabel
wxStaticText * mAttackText
std::unique_ptr< EffectEditor > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
void OnSlider(wxCommandEvent &evt)
wxStaticText * mThresholdLabel
EffectLegacyCompressorPanel * mPanel
wxStaticText * mDecayText
void OnPaint(wxPaintEvent &evt)
void OnSize(wxSizeEvent &evt)
Hold values to send to effect output meters.
static constexpr EffectParameter ReleaseTime
static constexpr EffectParameter Ratio
static constexpr EffectParameter Normalize
static constexpr EffectParameter AttackTime
static constexpr EffectParameter NoiseFloor
static constexpr EffectParameter UsePeak
static constexpr EffectParameter Threshold
static const LinearDBFormat & Instance()
static const LinearUpdater & Instance()
Used to display a Ruler.
Definition: Ruler.h:34
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
wxColour & Colour(int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
#define lrint(dbl)
Definition: float_cast.h:169
TranslatableString RatioLabelFormat(int sliderValue, double value)
TranslatableString AttackTimeFormat(double value)
TranslatableString DecayTimeFormat(double value)
TranslatableString RatioTextFormat(int sliderValue, double value)
TranslatableString ThresholdFormat(int value)
BuiltinEffectsModule::Registration< EffectLegacyCompressor > reg
const Type scale
Scaling factor, for slider control.
const Type def
Default value.
const Type min
Minimum value.
const Type max
Maximum value.
Externalized state of a plug-in.