Audacity 3.2.0
AutoDuck.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 AutoDuck.cpp
6
7 Markus Meyer
8
9*******************************************************************/
10#include "AutoDuck.h"
11#include "EffectEditor.h"
12#include "LoadEffects.h"
13
14#include <wx/dcclient.h>
15#include <wx/dcmemory.h>
16
17#include "AColor.h"
18#include "AllThemeResources.h"
19#include "Prefs.h"
20#include "ShuttleGui.h"
21#include "Theme.h"
22#include "../widgets/valnum.h"
23
24/*
25 * Effect implementation
26 */
27
29
30BEGIN_EVENT_TABLE(EffectAutoDuck, wxEvtHandler)
31 EVT_TEXT(wxID_ANY, EffectAutoDuck::OnValueChanged)
33
34std::unique_ptr<EffectEditor> EffectAutoDuck::PopulateOrExchange(
36 const EffectOutputs *)
37{
38 mUIParent = S.GetParent();
39 S.SetBorder(5);
40 S.StartVerticalLay(true);
41 {
42 S.AddSpace(0, 5);
43
44 mPanel = safenew EffectAutoDuck::Panel(S.GetParent(), wxID_ANY, this);
45 S.AddWindow(mPanel);
46
47 S.AddSpace(0, 5);
48
49 S.StartMultiColumn(6, wxCENTER);
50 {
51 mDuckAmountDbBox = S.Validator<FloatingPointValidator<double>>(
52 1, &mDuckAmountDb, NumValidatorStyle::NO_TRAILING_ZEROES,
53 DuckAmountDb.min, DuckAmountDb.max )
54 .NameSuffix(XO("db"))
55 .AddTextBox(XXO("Duck &amount:"), wxT(""), 10);
56 S.AddUnits(XO("dB"));
57
58 mMaximumPauseBox = S.Validator<FloatingPointValidator<double>>(
59 2, &mMaximumPause, NumValidatorStyle::NO_TRAILING_ZEROES,
60 MaximumPause.min, MaximumPause.max )
61 .NameSuffix(XO("seconds"))
62 .AddTextBox(XXO("Ma&ximum pause:"), wxT(""), 10);
63 S.AddUnits(XO("seconds"));
64
65 mOuterFadeDownLenBox = S.Validator<FloatingPointValidator<double>>(
66 2, &mOuterFadeDownLen, NumValidatorStyle::NO_TRAILING_ZEROES,
67 OuterFadeDownLen.min, OuterFadeDownLen.max )
68 .NameSuffix(XO("seconds"))
69 .AddTextBox(XXO("Outer fade &down length:"), wxT(""), 10);
70 S.AddUnits(XO("seconds"));
71
72 mOuterFadeUpLenBox = S.Validator<FloatingPointValidator<double>>(
73 2, &mOuterFadeUpLen, NumValidatorStyle::NO_TRAILING_ZEROES,
74 OuterFadeUpLen.min, OuterFadeUpLen.max )
75 .NameSuffix(XO("seconds"))
76 .AddTextBox(XXO("Outer fade &up length:"), wxT(""), 10);
77 S.AddUnits(XO("seconds"));
78
79 mInnerFadeDownLenBox = S.Validator<FloatingPointValidator<double>>(
80 2, &mInnerFadeDownLen, NumValidatorStyle::NO_TRAILING_ZEROES,
81 InnerFadeDownLen.min, InnerFadeDownLen.max )
82 .NameSuffix(XO("seconds"))
83 .AddTextBox(XXO("Inner fade d&own length:"), wxT(""), 10);
84 S.AddUnits(XO("seconds"));
85
86 mInnerFadeUpLenBox = S.Validator<FloatingPointValidator<double>>(
87 2, &mInnerFadeUpLen, NumValidatorStyle::NO_TRAILING_ZEROES,
88 InnerFadeUpLen.min, InnerFadeUpLen.max )
89 .NameSuffix(XO("seconds"))
90 .AddTextBox(XXO("Inner &fade up length:"), wxT(""), 10);
91 S.AddUnits(XO("seconds"));
92 }
93 S.EndMultiColumn();
94
95 S.StartMultiColumn(3, wxCENTER);
96 {
97 mThresholdDbBox = S.Validator<FloatingPointValidator<double>>(
98 2, &mThresholdDb, NumValidatorStyle::NO_TRAILING_ZEROES,
99 ThresholdDb.min, ThresholdDb.max )
100 .NameSuffix(XO("db"))
101 .AddTextBox(XXO("&Threshold:"), wxT(""), 10);
102 S.AddUnits(XO("dB"));
103 }
104 S.EndMultiColumn();
105
106 }
107 S.EndVerticalLay();
108
109 return nullptr;
110}
111
113{
114 return DoTransferDataToWindow();
115}
116
118{
119 // Issue 2324: don't remove these two lines
120 if (!mUIParent->TransferDataToWindow())
121 return false;
122
123 mPanel->Refresh(false);
124
125 return true;
126}
127
129{
130 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
131 {
132 return false;
133 }
134
135 return true;
136}
137
138void EffectAutoDuck::OnValueChanged(wxCommandEvent & WXUNUSED(evt))
139{
140 mPanel->Refresh(false);
141}
142
143/*
144 * EffectAutoDuck::Panel implementation
145 */
146
147#define CONTROL_POINT_REGION 10 // pixel distance to click on a control point
148#define CONTROL_POINT_MIN_MOVE 5 // min mouse move until value is changed
149
150#define TEXT_DISTANCE 15 // pixel distance text <-> center of control point
151
152#define FADE_DOWN_START 150 // x coordinate
153#define FADE_UP_START 450 // x coordinate
154#define DUCK_AMOUNT_START 50 // y coordinate
155
156#define FADE_SCALE 40 // scale factor for second -> pixel conversion
157#define DUCK_AMOUNT_SCALE 8 // scale factor for db -> pixel conversion
158
159static int GetDistance(const wxPoint& first, const wxPoint& second)
160{
161 int distanceX = abs(first.x - second.x);
162 int distanceY = abs(first.y - second.y);
163 if (distanceX > distanceY)
164 return distanceX;
165 else
166 return distanceY;
167}
168
169BEGIN_EVENT_TABLE(EffectAutoDuck::Panel, wxPanelWrapper)
171 EVT_MOUSE_CAPTURE_CHANGED(EffectAutoDuck::Panel::OnMouseCaptureChanged)
172 EVT_MOUSE_CAPTURE_LOST(EffectAutoDuck::Panel::OnMouseCaptureLost)
177
179 wxWindow *parent, wxWindowID winid, EffectAutoDuck *effect)
180: wxPanelWrapper(parent, winid, wxDefaultPosition, wxSize(600, 300))
181{
182 mParent = parent;
183 mEffect = effect;
184 mCurrentControlPoint = none;
185 mBackgroundBitmap = NULL;
186
187 ResetControlPoints();
188}
189
191{
192 if(HasCapture())
193 ReleaseMouse();
194}
195
197{
198 mControlPoints[innerFadeDown] = wxPoint(-100,-100);
199 mControlPoints[innerFadeUp] = wxPoint(-100,-100);
200 mControlPoints[outerFadeDown] = wxPoint(-100,-100);
201 mControlPoints[outerFadeUp] = wxPoint(-100,-100);
202 mControlPoints[duckAmount] = wxPoint(-100,-100);
203}
204
205void EffectAutoDuck::Panel::OnPaint(wxPaintEvent & WXUNUSED(evt))
206{
207 int clientWidth, clientHeight;
208 GetSize(&clientWidth, &clientHeight);
209
210 if (!mBackgroundBitmap || mBackgroundBitmap->GetWidth() != clientWidth ||
211 mBackgroundBitmap->GetHeight() != clientHeight)
212 {
213 mBackgroundBitmap = std::make_unique<wxBitmap>(clientWidth, clientHeight,24);
214 }
215
216 wxMemoryDC dc;
217 dc.SelectObject(*mBackgroundBitmap);
218
219 dc.SetBrush(*wxWHITE_BRUSH);
220 dc.SetPen(*wxBLACK_PEN);
221 dc.DrawRectangle(0, 0, clientWidth, clientHeight);
222
223 dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
224 wxFONTWEIGHT_NORMAL));
225 dc.SetTextForeground(*wxBLACK);
226 dc.SetTextBackground(*wxWHITE);
227
228 double duckAmountDb = 0;
229 double innerFadeDownLen = 0;
230 double innerFadeUpLen = 0;
231 double outerFadeDownLen = 0;
232 double outerFadeUpLen = 0;
233 mEffect->mDuckAmountDbBox->GetValue().ToDouble(&duckAmountDb);
234 mEffect->mInnerFadeDownLenBox->GetValue().ToDouble(&innerFadeDownLen);
235 mEffect->mInnerFadeUpLenBox->GetValue().ToDouble(&innerFadeUpLen);
236 mEffect->mOuterFadeDownLenBox->GetValue().ToDouble(&outerFadeDownLen);
237 mEffect->mOuterFadeUpLenBox->GetValue().ToDouble(&outerFadeUpLen);
238
239 if (innerFadeDownLen < InnerFadeDownLen.min || innerFadeDownLen > InnerFadeDownLen.max ||
240 innerFadeUpLen < InnerFadeUpLen.min || innerFadeUpLen > InnerFadeUpLen.max ||
241 outerFadeDownLen < OuterFadeDownLen.min || outerFadeDownLen > OuterFadeDownLen.max ||
242 outerFadeUpLen < OuterFadeUpLen.min || outerFadeUpLen > OuterFadeUpLen.max ||
243 duckAmountDb < DuckAmountDb.min || duckAmountDb > DuckAmountDb.max)
244 {
245 // values are out of range, no preview available
246 wxString message = _("Preview not available");
247 int textWidth = 0, textHeight = 0;
248 dc.GetTextExtent(message, &textWidth, &textHeight);
249 dc.DrawText(message, (clientWidth - textWidth) / 2,
250 (clientHeight - textHeight) / 2);
251
252 ResetControlPoints();
253 } else
254 {
255 // draw preview
256 dc.SetBrush(*wxTRANSPARENT_BRUSH);
257 dc.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxPENSTYLE_SOLID));
258
259 wxPoint points[6];
260
261 points[0].x = 10;
262 points[0].y = DUCK_AMOUNT_START;
263
264 points[1].x = FADE_DOWN_START - (int)(outerFadeDownLen * FADE_SCALE);
265 points[1].y = DUCK_AMOUNT_START;
266
267 points[2].x = FADE_DOWN_START + (int)(innerFadeDownLen * FADE_SCALE);
268 points[2].y = DUCK_AMOUNT_START -
269 (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
270
271 points[3].x = FADE_UP_START - (int)(innerFadeUpLen * FADE_SCALE);
272 points[3].y = DUCK_AMOUNT_START -
273 (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
274
275 points[4].x = FADE_UP_START + (int)(outerFadeUpLen * FADE_SCALE);
276 points[4].y = DUCK_AMOUNT_START;
277
278 points[5].x = clientWidth - 10;
279 points[5].y = DUCK_AMOUNT_START;
280
281 AColor::Lines(dc, 6, points);
282
283 dc.SetPen(wxPen(*wxBLACK, 1, wxPENSTYLE_DOT));
284
285 AColor::Line(dc, FADE_DOWN_START, 10, FADE_DOWN_START, clientHeight - 10);
286 AColor::Line(dc, FADE_UP_START, 10, FADE_UP_START, clientHeight - 10);
287
288 dc.SetPen(AColor::envelopePen);
289 dc.SetBrush(*wxWHITE_BRUSH);
290
291 mControlPoints[outerFadeDown] = points[1];
292 mControlPoints[innerFadeDown] = points[2];
293 mControlPoints[innerFadeUp] = points[3];
294 mControlPoints[outerFadeUp] = points[4];
295 mControlPoints[duckAmount] = wxPoint(
296 (points[2].x + points[3].x) / 2, points[2].y);
297
298 for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
299 {
301 int digits;
302 float value;
303
304 if (cp == innerFadeDown)
305 {
306 value = innerFadeDownLen;
307 digits = 2;
308 }
309 else if (cp == innerFadeUp)
310 {
311 value = innerFadeUpLen;
312 digits = 2;
313 }
314 else if (cp == outerFadeDown)
315 {
316 value = outerFadeDownLen;
317 digits = 2;
318 } else if (cp == outerFadeUp)
319 {
320 value = outerFadeUpLen;
321 digits = 2;
322 }
323 else
324 {
325 value = duckAmountDb;
326 digits = 1;
327 }
328
329 wxString valueStr = Internat::ToDisplayString(value, digits);
330 valueStr += wxT(" ");
331
332 if (cp == duckAmount)
333 /* i18n-hint: short form of 'decibels'.*/
334 valueStr += _("dB");
335 else
336 /* i18n-hint: short form of 'seconds'.*/
337 valueStr += _("s");
338
339 int textWidth = 0, textHeight = 0;
340 GetTextExtent(valueStr, &textWidth, &textHeight);
341
342 int textPosX = mControlPoints[i].x - textWidth / 2;
343 int textPosY = mControlPoints[i].y;
344
345 if (cp == duckAmount || cp == outerFadeDown || cp == outerFadeUp)
346 textPosY -= TEXT_DISTANCE + textHeight;
347 else
348 textPosY += TEXT_DISTANCE;
349
350 dc.DrawText(valueStr, textPosX, textPosY);
351
352 dc.DrawEllipse(mControlPoints[i].x - 3,
353 mControlPoints[i].y - 3, 6, 6);
354 }
355 }
356
357 // copy background buffer to paint dc
358 wxPaintDC paintDC(this);
359 paintDC.Blit(0, 0, clientWidth, clientHeight, &dc, 0, 0);
360
361 // clean up: necessary to free resources on Windows
362 dc.SetPen(wxNullPen);
363 dc.SetBrush(wxNullBrush);
364 dc.SetFont(wxNullFont);
365 dc.SelectObject(wxNullBitmap);
366}
367
369 wxMouseCaptureChangedEvent & WXUNUSED(evt))
370{
371 SetCursor(wxNullCursor);
372 mCurrentControlPoint = none;
373}
374
376 wxMouseCaptureLostEvent & WXUNUSED(evt))
377{
378 mCurrentControlPoint = none;
379
380 if (HasCapture())
381 {
382 ReleaseMouse();
383 }
384}
385
388{
390 int i;
391
392 for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
393 dist[i] = GetDistance(pt, mControlPoints[i]);
394
395 int curMinimum = 0;
396 for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
397 if (dist[i] < dist[curMinimum])
398 curMinimum = i;
399
400 if (dist[curMinimum] <= CONTROL_POINT_REGION)
401 return (EControlPoint)curMinimum;
402 else
403 return none;
404}
405
406void EffectAutoDuck::Panel::OnLeftDown(wxMouseEvent & evt)
407{
408 EControlPoint nearest = GetNearestControlPoint(evt.GetPosition());
409
410 if (nearest != none)
411 {
412 // this control point has been clicked
413 mMouseDownPoint = evt.GetPosition();
414
415 mCurrentControlPoint = nearest;
416 mControlPointMoveActivated = false;
417
418 for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
419 mMoveStartControlPoints[i] = mControlPoints[i];
420
421 if( !HasCapture() )
422 CaptureMouse();
423 }
424}
425
426void EffectAutoDuck::Panel::OnLeftUp(wxMouseEvent & WXUNUSED(evt))
427{
428 if (mCurrentControlPoint != none)
429 {
430 mCurrentControlPoint = none;
431 ReleaseMouse();
432 }
433}
434
435void EffectAutoDuck::Panel::OnMotion(wxMouseEvent & evt)
436{
437 switch (GetNearestControlPoint(evt.GetPosition()))
438 {
439 case none:
440 SetCursor(wxNullCursor);
441 break;
442 case innerFadeDown:
443 case innerFadeUp:
444 case outerFadeDown:
445 case outerFadeUp:
446 SetCursor(wxCursor(wxCURSOR_SIZEWE));
447 break;
448 case duckAmount:
449 SetCursor(wxCursor(wxCURSOR_SIZENS));
450 break;
451 }
452
453 if (mCurrentControlPoint != none)
454 {
455 if (!mControlPointMoveActivated)
456 {
457 int dist;
458
459 if (mCurrentControlPoint == duckAmount)
460 dist = abs(evt.GetY() - mMouseDownPoint.y);
461 else
462 dist = abs(evt.GetX() - mMouseDownPoint.x);
463
464 if (dist >= CONTROL_POINT_MIN_MOVE)
465 mControlPointMoveActivated = true;
466 }
467
468 if (mControlPointMoveActivated)
469 {
470 float newValue;
471
472 switch (mCurrentControlPoint)
473 {
474 case outerFadeDown:
475 newValue = ((double)(FADE_DOWN_START - evt.GetX())) / FADE_SCALE;
476 mEffect->mOuterFadeDownLen = std::clamp<double>(newValue, OuterFadeDownLen.min, OuterFadeDownLen.max);
477 break;
478 case outerFadeUp:
479 newValue = ((double)(evt.GetX() - FADE_UP_START)) / FADE_SCALE;
480 mEffect->mOuterFadeUpLen = std::clamp<double>(newValue, OuterFadeUpLen.min, OuterFadeUpLen.max);
481 break;
482 case innerFadeDown:
483 newValue = ((double)(evt.GetX() - FADE_DOWN_START)) / FADE_SCALE;
484 mEffect->mInnerFadeDownLen = std::clamp<double>(newValue, InnerFadeDownLen.min, InnerFadeDownLen.max);
485 break;
486 case innerFadeUp:
487 newValue = ((double)(FADE_UP_START - evt.GetX())) / FADE_SCALE;
488 mEffect->mInnerFadeUpLen = std::clamp<double>(newValue, InnerFadeUpLen.min, InnerFadeUpLen.max);
489 break;
490 case duckAmount:
491 newValue = ((double)(DUCK_AMOUNT_START - evt.GetY())) / DUCK_AMOUNT_SCALE;
492 mEffect->mDuckAmountDb = std::clamp<double>(newValue, DuckAmountDb.min, DuckAmountDb.max);
493 break;
494 case none:
495 wxASSERT(false); // should not happen
496 }
497 mEffect->DoTransferDataToWindow();
498 Refresh(false);
499 }
500 }
501}
wxT("CloseDown"))
#define FADE_DOWN_START
Definition: AutoDuck.cpp:152
#define FADE_UP_START
Definition: AutoDuck.cpp:153
#define FADE_SCALE
Definition: AutoDuck.cpp:156
#define CONTROL_POINT_REGION
Definition: AutoDuck.cpp:147
static int GetDistance(const wxPoint &first, const wxPoint &second)
Definition: AutoDuck.cpp:159
#define DUCK_AMOUNT_SCALE
Definition: AutoDuck.cpp:157
#define DUCK_AMOUNT_START
Definition: AutoDuck.cpp:154
#define TEXT_DISTANCE
Definition: AutoDuck.cpp:150
#define CONTROL_POINT_MIN_MOVE
Definition: AutoDuck.cpp:148
#define AUTO_DUCK_PANEL_NUM_CONTROL_POINTS
Definition: AutoDuck.h:24
END_EVENT_TABLE()
@ none
Definition: Dither.h:20
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
THEME_API Theme theTheme
Definition: Theme.cpp:82
#define S(N)
Definition: ToChars.cpp:64
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:194
static void Lines(wxDC &dc, size_t nPoints, const wxPoint points[])
Definition: AColor.cpp:206
static wxPen envelopePen
Definition: AColor.h:116
static constexpr EffectParameter OuterFadeUpLen
Definition: AutoDuckBase.h:75
static constexpr EffectParameter InnerFadeDownLen
Definition: AutoDuckBase.h:66
static constexpr EffectParameter InnerFadeUpLen
Definition: AutoDuckBase.h:69
static constexpr EffectParameter DuckAmountDb
Definition: AutoDuckBase.h:63
static constexpr EffectParameter OuterFadeDownLen
Definition: AutoDuckBase.h:72
EControlPoint GetNearestControlPoint(const wxPoint &pt)
Definition: AutoDuck.cpp:387
void OnMotion(wxMouseEvent &evt)
Definition: AutoDuck.cpp:435
void OnLeftDown(wxMouseEvent &evt)
Definition: AutoDuck.cpp:406
void OnMouseCaptureChanged(wxMouseCaptureChangedEvent &evt)
Definition: AutoDuck.cpp:368
void OnLeftUp(wxMouseEvent &evt)
Definition: AutoDuck.cpp:426
void OnPaint(wxPaintEvent &evt)
Definition: AutoDuck.cpp:205
void OnMouseCaptureLost(wxMouseCaptureLostEvent &evt)
Definition: AutoDuck.cpp:375
Panel * mPanel
Definition: AutoDuck.h:54
bool DoTransferDataToWindow()
Definition: AutoDuck.cpp:117
wxWeakRef< wxWindow > mUIParent
Definition: AutoDuck.h:43
bool TransferDataToWindow(const EffectSettings &settings) override
Definition: AutoDuck.cpp:112
void OnValueChanged(wxCommandEvent &evt)
Definition: AutoDuck.cpp:138
bool TransferDataFromWindow(EffectSettings &settings) override
Definition: AutoDuck.cpp:128
Performs effect computation.
Hold values to send to effect output meters.
static wxString ToDisplayString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, uses the user's locale's decimal separator.
Definition: Internat.cpp:137
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
wxColour & Colour(int iIndex)
BuiltinEffectsModule::Registration< EffectAutoDuck > reg
Definition: AutoDuck.cpp:28
STL namespace.
const Type min
Minimum value.
const Type max
Maximum value.
Externalized state of a plug-in.