Audacity  2.2.2
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 *******************************************************************/
19 #include "../Audacity.h"
20 #include "AutoDuck.h"
21 
22 #include <math.h>
23 #include <float.h>
24 
25 #include <wx/dcclient.h>
26 #include <wx/dcmemory.h>
27 #include <wx/dynarray.h>
28 #include <wx/intl.h>
29 
30 #include "../AColor.h"
31 #include "../AllThemeResources.h"
32 #include "../Internat.h"
33 #include "../Prefs.h"
34 #include "../ShuttleGui.h"
35 #include "../Theme.h"
36 #include "../widgets/valnum.h"
37 
38 #include "../WaveTrack.h"
39 #include "../widgets/ErrorDialog.h"
40 
41 // Define keys, defaults, minimums, and maximums for the effect parameters
42 //
43 // Name Type Key Def Min Max Scale
44 Param( DuckAmountDb, double, wxT("DuckAmountDb"), -12.0, -24.0, 0.0, 1 );
45 Param( InnerFadeDownLen, double, wxT("InnerFadeDownLen"), 0.0, 0.0, 3.0, 1 );
46 Param( InnerFadeUpLen, double, wxT("InnerFadeUpLen"), 0.0, 0.0, 3.0, 1 );
47 Param( OuterFadeDownLen, double, wxT("OuterFadeDownLen"), 0.5, 0.0, 3.0, 1 );
48 Param( OuterFadeUpLen, double, wxT("OuterFadeUpLen"), 0.5, 0.0, 3.0, 1 );
49 Param( ThresholdDb, double, wxT("ThresholdDb"), -30.0, -100.0, 0.0, 1 );
50 Param( MaximumPause, double, wxT("MaximumPause"), 1.0, 0.0, DBL_MAX, 1 );
51 
52 /*
53  * Common constants
54  */
55 
56 static const size_t kBufSize = 131072u; // number of samples to process at once
57 static const size_t kRMSWindowSize = 100u; // samples in circular RMS window buffer
58 
59 /*
60  * A auto duck region and an array of auto duck regions
61  */
62 
64 {
65  AutoDuckRegion(double t0, double t1)
66  {
67  this->t0 = t0;
68  this->t1 = t1;
69  }
70 
71  double t0;
72  double t1;
73 };
74 
75 #include <wx/arrimpl.cpp>
76 
77 WX_DECLARE_OBJARRAY(AutoDuckRegion, AutoDuckRegionArray);
78 WX_DEFINE_OBJARRAY(AutoDuckRegionArray);
79 
80 /*
81  * Effect implementation
82  */
83 
84 BEGIN_EVENT_TABLE(EffectAutoDuck, wxEvtHandler)
85  EVT_TEXT(wxID_ANY, EffectAutoDuck::OnValueChanged)
87 
89 {
90  mDuckAmountDb = DEF_DuckAmountDb;
91  mInnerFadeDownLen = DEF_InnerFadeDownLen;
92  mInnerFadeUpLen = DEF_InnerFadeUpLen;
93  mOuterFadeDownLen = DEF_OuterFadeDownLen;
94  mOuterFadeUpLen = DEF_OuterFadeUpLen;
95  mThresholdDb = DEF_ThresholdDb;
96  mMaximumPause = DEF_MaximumPause;
97 
98  SetLinearEffectFlag(true);
99 
100  mControlTrack = NULL;
101 
102  mPanel = NULL;
103 }
104 
106 {
107 }
108 
109 // IdentInterface implementation
110 
112 {
113  return AUTODUCK_PLUGIN_SYMBOL;
114 }
115 
117 {
118  return _("Reduces (ducks) the volume of one or more tracks whenever the volume of a specified \"control\" track reaches a particular level");
119 }
120 
122 {
123  return wxT("Auto_Duck");
124 }
125 
126 // EffectIdentInterface implementation
127 
129 {
130  return EffectTypeProcess;
131 }
132 
133 // EffectClientInterface implementation
134 
135 bool EffectAutoDuck::GetAutomationParameters(EffectAutomationParameters & parms)
136 {
137  parms.Write(KEY_DuckAmountDb, mDuckAmountDb);
138  parms.Write(KEY_InnerFadeDownLen, mInnerFadeDownLen);
139  parms.Write(KEY_InnerFadeUpLen, mInnerFadeUpLen);
140  parms.Write(KEY_OuterFadeDownLen, mOuterFadeDownLen);
141  parms.Write(KEY_OuterFadeUpLen, mOuterFadeUpLen);
142  parms.Write(KEY_ThresholdDb, mThresholdDb);
143  parms.Write(KEY_MaximumPause, mMaximumPause);
144 
145  return true;
146 }
147 
148 bool EffectAutoDuck::SetAutomationParameters(EffectAutomationParameters & parms)
149 {
150  ReadAndVerifyDouble(DuckAmountDb);
151  ReadAndVerifyDouble(InnerFadeDownLen);
152  ReadAndVerifyDouble(InnerFadeUpLen);
153  ReadAndVerifyDouble(OuterFadeDownLen);
154  ReadAndVerifyDouble(OuterFadeUpLen);
155  ReadAndVerifyDouble(ThresholdDb);
156  ReadAndVerifyDouble(MaximumPause);
157 
158  mDuckAmountDb = DuckAmountDb;
159  mInnerFadeDownLen = InnerFadeDownLen;
160  mInnerFadeUpLen = InnerFadeUpLen;
161  mOuterFadeDownLen = OuterFadeDownLen;
162  mOuterFadeUpLen = OuterFadeUpLen;
163  mThresholdDb = ThresholdDb;
164  mMaximumPause = MaximumPause;
165 
166  return true;
167 }
168 
169 // Effect implementation
170 
172 {
173  wxString base = wxT("/Effects/AutoDuck/");
174 
175  // Migrate settings from 2.1.0 or before
176 
177  // Already migrated, so bail
178  if (gPrefs->Exists(base + wxT("Migrated")))
179  {
180  return true;
181  }
182 
183  // Load the old "current" settings
184  if (gPrefs->Exists(base))
185  {
186  gPrefs->Read(base + wxT("DuckAmountDb"), &mDuckAmountDb, DEF_DuckAmountDb);
187  gPrefs->Read(base + wxT("InnerFadeDownLen"), &mInnerFadeDownLen, DEF_InnerFadeDownLen);
188  gPrefs->Read(base + wxT("InnerFadeUpLen"), &mInnerFadeUpLen, DEF_InnerFadeUpLen);
189  gPrefs->Read(base + wxT("OuterFadeDownLen"), &mOuterFadeDownLen, DEF_OuterFadeDownLen);
190  gPrefs->Read(base + wxT("OuterFadeUpLen"), &mOuterFadeUpLen, DEF_OuterFadeUpLen);
191  gPrefs->Read(base + wxT("ThresholdDb"), &mThresholdDb, DEF_ThresholdDb);
192  gPrefs->Read(base + wxT("MaximumPause"), &mMaximumPause, DEF_MaximumPause);
193 
195 
196  // Do not migrate again
197  gPrefs->Write(base + wxT("Migrated"), true);
198  gPrefs->Flush();
199  }
200 
201  return true;
202 }
203 
205 {
206  mControlTrack = NULL;
207 
209  Track *t = iter.First();
210 
211  bool lastWasSelectedWaveTrack = false;
212  const WaveTrack *controlTrackCandidate = NULL;
213 
214  while(t)
215  {
216  if (lastWasSelectedWaveTrack && !t->GetSelected() &&
217  t->GetKind() == Track::Wave)
218  {
219  // This could be the control track, so remember it
220  controlTrackCandidate = (WaveTrack*)t;
221  }
222 
223  lastWasSelectedWaveTrack = false;
224 
225  if (t->GetSelected())
226  {
227  if (t->GetKind() == Track::Wave)
228  {
229  lastWasSelectedWaveTrack = true;
230  }
231  else
232  {
234  _("You selected a track which does not contain audio. AutoDuck can only process audio tracks."),
235  /* i18n-hint: Auto duck is the name of an effect that 'ducks' (reduces the volume)
236  * of the audio automatically when there is sound on another track. Not as
237  * in 'Donald-Duck'!*/
238  wxICON_ERROR);
239  return false;
240  }
241  }
242 
243  t = iter.Next();
244  }
245 
246  if (!controlTrackCandidate)
247  {
249  _("Auto Duck needs a control track which must be placed below the selected track(s)."),
250  wxICON_ERROR);
251  return false;
252  }
253 
254  mControlTrack = controlTrackCandidate;
255 
256  return true;
257 }
258 
260 {
261  mControlTrack = NULL;
262 }
263 
265 {
266  if (GetNumWaveTracks() == 0 || !mControlTrack)
267  return false;
268 
269  bool cancel = false;
270 
271  auto start =
273  auto end =
275 
276  if (end <= start)
277  return false;
278 
279  // the minimum number of samples we have to wait until the maximum
280  // pause has been exceeded
281  double maxPause = mMaximumPause;
282 
283  // We don't fade in until we have time enough to actually fade out again
284  if (maxPause < mOuterFadeDownLen + mOuterFadeUpLen)
285  maxPause = mOuterFadeDownLen + mOuterFadeUpLen;
286 
287  auto minSamplesPause =
288  mControlTrack->TimeToLongSamples(maxPause);
289 
290  double threshold = DB_TO_LINEAR(mThresholdDb);
291 
292  // adjust the threshold so we can compare it to the rmsSum value
293  threshold = threshold * threshold * kRMSWindowSize;
294 
295  int rmsPos = 0;
296  float rmsSum = 0;
297  // to make the progress bar appear more natural, we first look for all
298  // duck regions and apply them all at once afterwards
299  AutoDuckRegionArray regions;
300  bool inDuckRegion = false;
301  {
302  Floats rmsWindow{ kRMSWindowSize, true };
303 
304  Floats buf{ kBufSize };
305 
306  // initialize the following two variables to prevent compiler warning
307  double duckRegionStart = 0;
308  sampleCount curSamplesPause = 0;
309 
310  auto pos = start;
311 
312  while (pos < end)
313  {
314  const auto len = limitSampleBufferSize( kBufSize, end - pos );
315 
316  mControlTrack->Get((samplePtr)buf.get(), floatSample, pos, len);
317 
318  for (auto i = pos; i < pos + len; i++)
319  {
320  rmsSum -= rmsWindow[rmsPos];
321  // i - pos is bounded by len:
322  auto index = ( i - pos ).as_size_t();
323  rmsWindow[rmsPos] = buf[ index ] * buf[ index ];
324  rmsSum += rmsWindow[rmsPos];
325  rmsPos = (rmsPos + 1) % kRMSWindowSize;
326 
327  bool thresholdExceeded = rmsSum > threshold;
328 
329  if (thresholdExceeded)
330  {
331  // everytime the threshold is exceeded, reset our count for
332  // the number of pause samples
333  curSamplesPause = 0;
334 
335  if (!inDuckRegion)
336  {
337  // the threshold has been exceeded for the first time, so
338  // let the duck region begin here
339  inDuckRegion = true;
340  duckRegionStart = mControlTrack->LongSamplesToTime(i);
341  }
342  }
343 
344  if (!thresholdExceeded && inDuckRegion)
345  {
346  // the threshold has not been exceeded and we are in a duck
347  // region, but only fade in if the maximum pause has been
348  // exceeded
349  curSamplesPause += 1;
350 
351  if (curSamplesPause >= minSamplesPause)
352  {
353  // do the actual duck fade and reset all values
354  double duckRegionEnd =
355  mControlTrack->LongSamplesToTime(i - curSamplesPause);
356 
357  regions.Add(AutoDuckRegion(
358  duckRegionStart - mOuterFadeDownLen,
359  duckRegionEnd + mOuterFadeUpLen));
360 
361  inDuckRegion = false;
362  }
363  }
364  }
365 
366  pos += len;
367 
368  if (TotalProgress(
369  (pos - start).as_double() /
370  (end - start).as_double() /
371  (GetNumWaveTracks() + 1)
372  ))
373  {
374  cancel = true;
375  break;
376  }
377  }
378 
379  // apply last duck fade, if any
380  if (inDuckRegion)
381  {
382  double duckRegionEnd =
383  mControlTrack->LongSamplesToTime(end - curSamplesPause);
384  regions.Add(AutoDuckRegion(
385  duckRegionStart - mOuterFadeDownLen,
386  duckRegionEnd + mOuterFadeUpLen));
387  }
388  }
389 
390  if (!cancel)
391  {
392  CopyInputTracks(); // Set up mOutputTracks.
394  Track *iterTrack = iter.First();
395 
396  int trackNumber = 0;
397 
398  while (iterTrack)
399  {
400  WaveTrack* t = (WaveTrack*)iterTrack;
401 
402  for (size_t i = 0; i < regions.GetCount(); i++)
403  {
404  const AutoDuckRegion& region = regions[i];
405  if (ApplyDuckFade(trackNumber, t, region.t0, region.t1))
406  {
407  cancel = true;
408  break;
409  }
410  }
411 
412  if (cancel)
413  break;
414 
415  iterTrack = iter.Next();
416  trackNumber++;
417  }
418  }
419 
420  ReplaceProcessedTracks(!cancel);
421  return !cancel;
422 }
423 
425 {
426  S.SetBorder(5);
427  S.StartVerticalLay(true);
428  {
429  S.AddSpace(0, 5);
430 
432  S.AddWindow(mPanel);
433 
434  S.AddSpace(0, 5);
435 
436  S.StartMultiColumn(6, wxCENTER);
437  {
438  FloatingPointValidator<double> vldDuckAmountDb(1, &mDuckAmountDb, NUM_VAL_NO_TRAILING_ZEROES);
439  vldDuckAmountDb.SetRange(MIN_DuckAmountDb, MAX_DuckAmountDb);
440  mDuckAmountDbBox = S.AddTextBox(_("Duck amount:"), wxT(""), 10);
441  mDuckAmountDbBox->SetValidator(vldDuckAmountDb);
442  S.AddUnits(_("dB"));
443 
444  FloatingPointValidator<double> vldMaximumPause(2, &mMaximumPause, NUM_VAL_NO_TRAILING_ZEROES);
445  vldMaximumPause.SetRange(MIN_MaximumPause, MAX_MaximumPause);
446  mMaximumPauseBox = S.AddTextBox(_("Maximum pause:"), wxT(""), 10);
447  mMaximumPauseBox->SetValidator(vldMaximumPause);
448  S.AddUnits(_("seconds"));
449 
450  FloatingPointValidator<double> vldOuterFadeDownLen(2, &mOuterFadeDownLen, NUM_VAL_NO_TRAILING_ZEROES);
451  vldOuterFadeDownLen.SetRange(MIN_OuterFadeDownLen, MAX_OuterFadeDownLen);
452  mOuterFadeDownLenBox = S.AddTextBox(_("Outer fade down length:"), wxT(""), 10);
453  mOuterFadeDownLenBox->SetValidator(vldOuterFadeDownLen);
454  S.AddUnits(_("seconds"));
455 
456  FloatingPointValidator<double> vldOuterFadeUpLen(2, &mOuterFadeUpLen, NUM_VAL_NO_TRAILING_ZEROES);
457  vldOuterFadeUpLen.SetRange(MIN_OuterFadeUpLen, MAX_OuterFadeUpLen);
458  mOuterFadeUpLenBox = S.AddTextBox(_("Outer fade up length:"), wxT(""), 10);
459  mOuterFadeUpLenBox->SetValidator(vldOuterFadeUpLen);
460  S.AddUnits(_("seconds"));
461 
462  FloatingPointValidator<double> vldInnerFadeDownLen(2, &mInnerFadeDownLen, NUM_VAL_NO_TRAILING_ZEROES);
463  vldInnerFadeDownLen.SetRange(MIN_InnerFadeDownLen, MAX_InnerFadeDownLen);
464  mInnerFadeDownLenBox = S.AddTextBox(_("Inner fade down length:"), wxT(""), 10);
465  mInnerFadeDownLenBox->SetValidator(vldInnerFadeDownLen);
466  S.AddUnits(_("seconds"));
467 
468  FloatingPointValidator<double> vldInnerFadeUpLen(2, &mInnerFadeUpLen, NUM_VAL_NO_TRAILING_ZEROES);
469  vldInnerFadeUpLen.SetRange(MIN_InnerFadeUpLen, MAX_InnerFadeUpLen);
470  mInnerFadeUpLenBox = S.AddTextBox(_("Inner fade up length:"), wxT(""), 10);
471  mInnerFadeUpLenBox->SetValidator(vldInnerFadeUpLen);
472  S.AddUnits(_("seconds"));
473  }
474  S.EndMultiColumn();
475 
476  S.StartMultiColumn(3, wxCENTER);
477  {
478  FloatingPointValidator<double> vldThresholdDb(2, &mThresholdDb, NUM_VAL_NO_TRAILING_ZEROES);
479  vldThresholdDb.SetRange(MIN_ThresholdDb, MAX_ThresholdDb);
480  mThresholdDbBox = S.AddTextBox(_("Threshold:"), wxT(""), 10);
481  mThresholdDbBox->SetValidator(vldThresholdDb);
482  S.AddUnits(_("dB"));
483  }
484  S.EndMultiColumn();
485 
486  }
487  S.EndVerticalLay();
488 
489  return;
490 }
491 
493 {
494  if (!mUIParent->TransferDataToWindow())
495  {
496  return false;
497  }
498 
499  mPanel->Refresh(false);
500 
501  return true;
502 }
503 
505 {
506  if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
507  {
508  return false;
509  }
510 
511  return true;
512 }
513 
514 // EffectAutoDuck implementation
515 
516 // this currently does an exponential fade
517 bool EffectAutoDuck::ApplyDuckFade(int trackNumber, WaveTrack* t,
518  double t0, double t1)
519 {
520  bool cancel = false;
521 
522  auto start = t->TimeToLongSamples(t0);
523  auto end = t->TimeToLongSamples(t1);
524 
525  Floats buf{ kBufSize };
526  auto pos = start;
527 
528  auto fadeDownSamples = t->TimeToLongSamples(
530  if (fadeDownSamples < 1)
531  fadeDownSamples = 1;
532 
533  auto fadeUpSamples = t->TimeToLongSamples(
535  if (fadeUpSamples < 1)
536  fadeUpSamples = 1;
537 
538  float fadeDownStep = mDuckAmountDb / fadeDownSamples.as_double();
539  float fadeUpStep = mDuckAmountDb / fadeUpSamples.as_double();
540 
541  while (pos < end)
542  {
543  const auto len = limitSampleBufferSize( kBufSize, end - pos );
544 
545  t->Get((samplePtr)buf.get(), floatSample, pos, len);
546 
547  for (auto i = pos; i < pos + len; i++)
548  {
549  float gainDown = fadeDownStep * (i - start).as_float();
550  float gainUp = fadeUpStep * (end - i).as_float();
551 
552  float gain;
553  if (gainDown > gainUp)
554  gain = gainDown;
555  else
556  gain = gainUp;
557  if (gain < mDuckAmountDb)
558  gain = mDuckAmountDb;
559 
560  // i - pos is bounded by len:
561  buf[ ( i - pos ).as_size_t() ] *= DB_TO_LINEAR(gain);
562  }
563 
564  t->Set((samplePtr)buf.get(), floatSample, pos, len);
565 
566  pos += len;
567 
568  float curTime = t->LongSamplesToTime(pos);
569  float fractionFinished = (curTime - mT0) / (mT1 - mT0);
570  if (TotalProgress( (trackNumber + 1 + fractionFinished) /
571  (GetNumWaveTracks() + 1) ))
572  {
573  cancel = true;
574  break;
575  }
576  }
577 
578  return cancel;
579 }
580 
581 void EffectAutoDuck::OnValueChanged(wxCommandEvent & WXUNUSED(evt))
582 {
583  mPanel->Refresh(false);
584 }
585 
586 /*
587  * EffectAutoDuckPanel implementation
588  */
589 
590 #define CONTROL_POINT_REGION 10 // pixel distance to click on a control point
591 #define CONTROL_POINT_MIN_MOVE 5 // min mouse move until value is changed
592 
593 #define TEXT_DISTANCE 15 // pixel distance text <-> center of control point
594 
595 #define FADE_DOWN_START 150 // x coordinate
596 #define FADE_UP_START 450 // x coordinate
597 #define DUCK_AMOUNT_START 50 // y coordinate
598 
599 #define FADE_SCALE 40 // scale factor for second -> pixel conversion
600 #define DUCK_AMOUNT_SCALE 8 // scale factor for db -> pixel conversion
601 
602 static int GetDistance(const wxPoint& first, const wxPoint& second)
603 {
604  int distanceX = abs(first.x - second.x);
605  int distanceY = abs(first.y - second.y);
606  if (distanceX > distanceY)
607  return distanceX;
608  else
609  return distanceY;
610 }
611 
612 BEGIN_EVENT_TABLE(EffectAutoDuckPanel, wxPanelWrapper)
613  EVT_PAINT(EffectAutoDuckPanel::OnPaint)
614  EVT_MOUSE_CAPTURE_CHANGED(EffectAutoDuckPanel::OnMouseCaptureChanged)
615  EVT_MOUSE_CAPTURE_LOST(EffectAutoDuckPanel::OnMouseCaptureLost)
616  EVT_LEFT_DOWN(EffectAutoDuckPanel::OnLeftDown)
617  EVT_LEFT_UP(EffectAutoDuckPanel::OnLeftUp)
618  EVT_MOTION(EffectAutoDuckPanel::OnMotion)
620 
622 : wxPanelWrapper(parent, wxID_ANY, wxDefaultPosition, wxSize(600, 300))
623 {
624  mParent = parent;
625  mEffect = effect;
626  mCurrentControlPoint = none;
627  mBackgroundBitmap = NULL;
628 
629  ResetControlPoints();
630 }
631 
633 {
634  if(HasCapture())
635  ReleaseMouse();
636 }
637 
639 {
640  mControlPoints[innerFadeDown] = wxPoint(-100,-100);
641  mControlPoints[innerFadeUp] = wxPoint(-100,-100);
642  mControlPoints[outerFadeDown] = wxPoint(-100,-100);
643  mControlPoints[outerFadeUp] = wxPoint(-100,-100);
644  mControlPoints[duckAmount] = wxPoint(-100,-100);
645 }
646 
647 void EffectAutoDuckPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
648 {
649  int clientWidth, clientHeight;
650  GetSize(&clientWidth, &clientHeight);
651 
652  if (!mBackgroundBitmap || mBackgroundBitmap->GetWidth() != clientWidth ||
653  mBackgroundBitmap->GetHeight() != clientHeight)
654  {
655  mBackgroundBitmap = std::make_unique<wxBitmap>(clientWidth, clientHeight);
656  }
657 
658  wxMemoryDC dc;
659  dc.SelectObject(*mBackgroundBitmap);
660 
661  dc.SetBrush(*wxWHITE_BRUSH);
662  dc.SetPen(*wxBLACK_PEN);
663  dc.DrawRectangle(0, 0, clientWidth, clientHeight);
664 
665  dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
666  wxFONTWEIGHT_NORMAL));
667  dc.SetTextForeground(*wxBLACK);
668  dc.SetTextBackground(*wxWHITE);
669 
670  double duckAmountDb = 0;
671  double innerFadeDownLen = 0;
672  double innerFadeUpLen = 0;
673  double outerFadeDownLen = 0;
674  double outerFadeUpLen = 0;
675  mEffect->mDuckAmountDbBox->GetValue().ToDouble(&duckAmountDb);
676  mEffect->mInnerFadeDownLenBox->GetValue().ToDouble(&innerFadeDownLen);
677  mEffect->mInnerFadeUpLenBox->GetValue().ToDouble(&innerFadeUpLen);
678  mEffect->mOuterFadeDownLenBox->GetValue().ToDouble(&outerFadeDownLen);
679  mEffect->mOuterFadeUpLenBox->GetValue().ToDouble(&outerFadeUpLen);
680 
681  if (innerFadeDownLen < MIN_InnerFadeDownLen || innerFadeDownLen > MAX_InnerFadeDownLen ||
682  innerFadeUpLen < MIN_InnerFadeUpLen || innerFadeUpLen > MAX_InnerFadeUpLen ||
683  outerFadeDownLen < MIN_OuterFadeDownLen || outerFadeDownLen > MAX_OuterFadeDownLen ||
684  outerFadeUpLen < MIN_OuterFadeUpLen || outerFadeUpLen > MAX_OuterFadeUpLen ||
685  duckAmountDb < MIN_DuckAmountDb || duckAmountDb > MAX_DuckAmountDb)
686  {
687  // values are out of range, no preview available
688  wxString message = _("Preview not available");
689  int textWidth = 0, textHeight = 0;
690  dc.GetTextExtent(message, &textWidth, &textHeight);
691  dc.DrawText(message, (clientWidth - textWidth) / 2,
692  (clientHeight - textHeight) / 2);
693 
695  } else
696  {
697  // draw preview
698  dc.SetBrush(*wxTRANSPARENT_BRUSH);
699  dc.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxSOLID));
700 
701  wxPoint points[6];
702 
703  points[0].x = 10;
704  points[0].y = DUCK_AMOUNT_START;
705 
706  points[1].x = FADE_DOWN_START - (int)(outerFadeDownLen * FADE_SCALE);
707  points[1].y = DUCK_AMOUNT_START;
708 
709  points[2].x = FADE_DOWN_START + (int)(innerFadeDownLen * FADE_SCALE);
710  points[2].y = DUCK_AMOUNT_START -
711  (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
712 
713  points[3].x = FADE_UP_START - (int)(innerFadeUpLen * FADE_SCALE);
714  points[3].y = DUCK_AMOUNT_START -
715  (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
716 
717  points[4].x = FADE_UP_START + (int)(outerFadeUpLen * FADE_SCALE);
718  points[4].y = DUCK_AMOUNT_START;
719 
720  points[5].x = clientWidth - 10;
721  points[5].y = DUCK_AMOUNT_START;
722 
723  dc.DrawLines(6, points);
724 
725  dc.SetPen(wxPen(*wxBLACK, 1, wxDOT));
726 
727  AColor::Line(dc, FADE_DOWN_START, 10, FADE_DOWN_START, clientHeight - 10);
728  AColor::Line(dc, FADE_UP_START, 10, FADE_UP_START, clientHeight - 10);
729 
730  dc.SetPen(AColor::envelopePen);
731  dc.SetBrush(*wxWHITE_BRUSH);
732 
733  mControlPoints[outerFadeDown] = points[1];
734  mControlPoints[innerFadeDown] = points[2];
735  mControlPoints[innerFadeUp] = points[3];
736  mControlPoints[outerFadeUp] = points[4];
737  mControlPoints[duckAmount] = wxPoint(
738  (points[2].x + points[3].x) / 2, points[2].y);
739 
740  for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
741  {
743  int digits;
744  float value;
745 
746  if (cp == innerFadeDown)
747  {
748  value = innerFadeDownLen;
749  digits = 2;
750  }
751  else if (cp == innerFadeUp)
752  {
753  value = innerFadeUpLen;
754  digits = 2;
755  }
756  else if (cp == outerFadeDown)
757  {
758  value = outerFadeDownLen;
759  digits = 2;
760  } else if (cp == outerFadeUp)
761  {
762  value = outerFadeUpLen;
763  digits = 2;
764  }
765  else
766  {
767  value = duckAmountDb;
768  digits = 1;
769  }
770 
771  wxString valueStr = Internat::ToDisplayString(value, digits);
772  valueStr += wxT(" ");
773 
774  if (cp == duckAmount)
775  /* i18n-hint: short form of 'decibels'.*/
776  valueStr += _("dB");
777  else
778  /* i18n-hint: short form of 'seconds'.*/
779  valueStr += _("s");
780 
781  int textWidth = 0, textHeight = 0;
782  GetTextExtent(valueStr, &textWidth, &textHeight);
783 
784  int textPosX = mControlPoints[i].x - textWidth / 2;
785  int textPosY = mControlPoints[i].y;
786 
787  if (cp == duckAmount || cp == outerFadeDown || cp == outerFadeUp)
788  textPosY -= TEXT_DISTANCE + textHeight;
789  else
790  textPosY += TEXT_DISTANCE;
791 
792  dc.DrawText(valueStr, textPosX, textPosY);
793 
794  dc.DrawEllipse(mControlPoints[i].x - 3,
795  mControlPoints[i].y - 3, 6, 6);
796  }
797  }
798 
799  // copy background buffer to paint dc
800  wxPaintDC paintDC(this);
801  paintDC.Blit(0, 0, clientWidth, clientHeight, &dc, 0, 0);
802 
803  // clean up: necessary to free resources on Windows
804  dc.SetPen(wxNullPen);
805  dc.SetBrush(wxNullBrush);
806  dc.SetFont(wxNullFont);
807  dc.SelectObject(wxNullBitmap);
808 }
809 
811  wxMouseCaptureChangedEvent & WXUNUSED(evt))
812 {
813  SetCursor(wxNullCursor);
815 }
816 
818  wxMouseCaptureLostEvent & WXUNUSED(evt))
819 {
821 
822  if (HasCapture())
823  {
824  ReleaseMouse();
825  }
826 }
827 
830 {
832  int i;
833 
834  for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
835  dist[i] = GetDistance(pt, mControlPoints[i]);
836 
837  int curMinimum = 0;
838  for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
839  if (dist[i] < dist[curMinimum])
840  curMinimum = i;
841 
842  if (dist[curMinimum] <= CONTROL_POINT_REGION)
843  return (EControlPoint)curMinimum;
844  else
845  return none;
846 }
847 
848 void EffectAutoDuckPanel::OnLeftDown(wxMouseEvent & evt)
849 {
850  EControlPoint nearest = GetNearestControlPoint(evt.GetPosition());
851 
852  if (nearest != none)
853  {
854  // this control point has been clicked
855  mMouseDownPoint = evt.GetPosition();
856 
857  mCurrentControlPoint = nearest;
859 
860  for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
862 
863  if( !HasCapture() )
864  CaptureMouse();
865  }
866 }
867 
868 void EffectAutoDuckPanel::OnLeftUp(wxMouseEvent & WXUNUSED(evt))
869 {
870  if (mCurrentControlPoint != none)
871  {
873  ReleaseMouse();
874  }
875 }
876 
877 void EffectAutoDuckPanel::OnMotion(wxMouseEvent & evt)
878 {
879  switch (GetNearestControlPoint(evt.GetPosition()))
880  {
881  case none:
882  SetCursor(wxNullCursor);
883  break;
884  case innerFadeDown:
885  case innerFadeUp:
886  case outerFadeDown:
887  case outerFadeUp:
888  SetCursor(wxCursor(wxCURSOR_SIZEWE));
889  break;
890  case duckAmount:
891  SetCursor(wxCursor(wxCURSOR_SIZENS));
892  break;
893  }
894 
895  if (mCurrentControlPoint != none)
896  {
898  {
899  int dist;
900 
902  dist = abs(evt.GetY() - mMouseDownPoint.y);
903  else
904  dist = abs(evt.GetX() - mMouseDownPoint.x);
905 
906  if (dist >= CONTROL_POINT_MIN_MOVE)
908  }
909 
911  {
912  float newValue;
913 
914  switch (mCurrentControlPoint)
915  {
916  case outerFadeDown:
917  newValue = ((double)(FADE_DOWN_START - evt.GetX())) / FADE_SCALE;
918  mEffect->mOuterFadeDownLen = TrapDouble(newValue, MIN_OuterFadeDownLen, MAX_OuterFadeDownLen);
919  break;
920  case outerFadeUp:
921  newValue = ((double)(evt.GetX() - FADE_UP_START)) / FADE_SCALE;
922  mEffect->mOuterFadeUpLen = TrapDouble(newValue, MIN_OuterFadeUpLen, MAX_OuterFadeUpLen);
923  break;
924  case innerFadeDown:
925  newValue = ((double)(evt.GetX() - FADE_DOWN_START)) / FADE_SCALE;
926  mEffect->mInnerFadeDownLen = TrapDouble(newValue, MIN_InnerFadeDownLen, MAX_InnerFadeDownLen);
927  break;
928  case innerFadeUp:
929  newValue = ((double)(FADE_UP_START - evt.GetX())) / FADE_SCALE;
930  mEffect->mInnerFadeUpLen = TrapDouble(newValue, MIN_InnerFadeUpLen, MAX_InnerFadeUpLen);
931  break;
932  case duckAmount:
933  newValue = ((double)(DUCK_AMOUNT_START - evt.GetY())) / DUCK_AMOUNT_SCALE;
934  mEffect->mDuckAmountDb = TrapDouble(newValue, MIN_DuckAmountDb, MAX_DuckAmountDb);
935  break;
936  case none:
937  wxASSERT(false); // should not happen
938  }
940  Refresh(false);
941  }
942  }
943 }
static int GetDistance(const wxPoint &first, const wxPoint &second)
Definition: AutoDuck.cpp:602
Implements the Auto Ducking effect.
Definition: AutoDuck.h:31
double mT1
Definition: Effect.h:460
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:215
EControlPoint GetNearestControlPoint(const wxPoint &pt)
Definition: AutoDuck.cpp:829
bool SaveUserPreset(const wxString &name) override
Definition: Effect.cpp:615
void OnValueChanged(wxCommandEvent &evt)
Definition: AutoDuck.cpp:581
int MessageBox(const wxString &message, long style=DefaultMessageBoxStyle, const wxString &titleStr=wxString{})
Definition: Effect.cpp:2655
#define AUTO_DUCK_PANEL_NUM_CONTROL_POINTS
Definition: AutoDuck.h:27
bool TotalProgress(double frac)
Definition: Effect.cpp:1970
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
void OnPaint(wxPaintEvent &evt)
Definition: AutoDuck.cpp:647
wxString ManualPage() override
Definition: AutoDuck.cpp:121
wxTextCtrl * mInnerFadeUpLenBox
Definition: AutoDuck.h:82
EffectType GetType() override
Definition: AutoDuck.cpp:128
wxPoint mMouseDownPoint
Definition: AutoDuck.h:133
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true) const
Definition: WaveTrack.cpp:1950
wxString GetCurrentSettingsGroup() override
Definition: Effect.cpp:814
double mInnerFadeDownLen
Definition: AutoDuck.h:71
void CopyInputTracks()
Definition: Effect.cpp:2029
bool GetSelected() const
Definition: Track.h:217
wxWindow * AddWindow(wxWindow *pWindow, int Flags=wxALIGN_CENTRE|wxALL)
Definition: ShuttleGui.cpp:257
bool TransferDataFromWindow() override
Definition: AutoDuck.cpp:504
Param(DuckAmountDb, double, wxT("DuckAmountDb"),-12.0,-24.0, 0.0, 1)
AutoDuckRegion(double t0, double t1)
Definition: AutoDuck.cpp:65
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2160
void EndMultiColumn()
void OnMotion(wxMouseEvent &evt)
Definition: AutoDuck.cpp:877
EffectAutoDuck * mEffect
Definition: AutoDuck.h:128
wxTextCtrl * mThresholdDbBox
Definition: AutoDuck.h:85
static const size_t kRMSWindowSize
Definition: AutoDuck.cpp:57
#define AUTODUCK_PLUGIN_SYMBOL
Definition: AutoDuck.h:29
double mMaximumPause
Definition: AutoDuck.h:76
double mOuterFadeUpLen
Definition: AutoDuck.h:74
bool mControlPointMoveActivated
Definition: AutoDuck.h:134
void PopulateOrExchange(ShuttleGui &S) override
Definition: AutoDuck.cpp:424
double mDuckAmountDb
Definition: AutoDuck.h:70
TrackList * inputTracks() const
Definition: Effect.h:457
#define safenew
Definition: Audacity.h:223
virtual int GetKind() const
Definition: Track.h:267
#define CONTROL_POINT_REGION
Definition: AutoDuck.cpp:590
void ResetControlPoints()
Definition: AutoDuck.cpp:638
void AddUnits(const wxString &Prompt)
Left aligned text string.
Definition: ShuttleGui.cpp:229
#define TEXT_DISTANCE
Definition: AutoDuck.cpp:593
static wxPen envelopePen
Definition: AColor.h:122
double mThresholdDb
Definition: AutoDuck.h:75
void EndVerticalLay()
Definition: ShuttleGui.cpp:991
std::unique_ptr< wxBitmap > mBackgroundBitmap
Definition: AutoDuck.h:129
bool ApplyDuckFade(int trackNumber, WaveTrack *t, double t0, double t1)
Definition: AutoDuck.cpp:517
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:493
#define DUCK_AMOUNT_START
Definition: AutoDuck.cpp:597
bool Startup() override
Definition: AutoDuck.cpp:171
wxTextCtrl * mInnerFadeDownLenBox
Definition: AutoDuck.h:81
void Set(samplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveTrack.cpp:2027
virtual ~EffectAutoDuckPanel()
Definition: AutoDuck.cpp:632
wxWindow * GetParent()
Definition: ShuttleGui.h:259
wxTextCtrl * mOuterFadeUpLenBox
Definition: AutoDuck.h:84
#define CONTROL_POINT_MIN_MOVE
Definition: AutoDuck.cpp:591
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:998
friend class EffectAutoDuckPanel
Definition: AutoDuck.h:91
bool SetAutomationParameters(EffectAutomationParameters &parms) override
Definition: AutoDuck.cpp:148
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:67
wxTextCtrl * mDuckAmountDbBox
Definition: AutoDuck.h:80
#define ReadAndVerifyDouble(name)
Definition: Effect.h:787
WX_DECLARE_OBJARRAY(AutoDuckRegion, AutoDuckRegionArray)
#define FADE_UP_START
Definition: AutoDuck.cpp:596
wxString GetDescription() override
Definition: AutoDuck.cpp:116
virtual Track * First(TrackList *val=nullptr)
Definition: Track.cpp:355
virtual ~EffectAutoDuck()
Definition: AutoDuck.cpp:105
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
void OnMouseCaptureChanged(wxMouseCaptureChangedEvent &evt)
Definition: AutoDuck.cpp:810
wxWindow * mUIParent
Definition: Effect.h:471
EControlPoint mCurrentControlPoint
Definition: AutoDuck.h:130
An iterator for a TrackList.
Definition: Track.h:339
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
bool GetAutomationParameters(EffectAutomationParameters &parms) override
Definition: AutoDuck.cpp:135
double TrapDouble(double x, double min, double max)
Definition: Effect.h:724
a struct that holds a start and end time.
Definition: AutoDuck.cpp:63
#define FADE_SCALE
Definition: AutoDuck.cpp:599
wxTextCtrl * mOuterFadeDownLenBox
Definition: AutoDuck.h:83
#define DUCK_AMOUNT_SCALE
Definition: AutoDuck.cpp:600
void End() override
Definition: AutoDuck.cpp:259
int GetNumWaveTracks()
Definition: Effect.h:343
void OnLeftDown(wxMouseEvent &evt)
Definition: AutoDuck.cpp:848
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1822
virtual Track * Next(bool skiplinked=false)
Definition: Track.cpp:396
wxPoint mControlPoints[AUTO_DUCK_PANEL_NUM_CONTROL_POINTS]
Definition: AutoDuck.h:131
void OnMouseCaptureLost(wxMouseCaptureLostEvent &evt)
Definition: AutoDuck.cpp:817
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:148
EffectAutoDuckPanel * mPanel
Definition: AutoDuck.h:87
bool TransferDataToWindow() override
Definition: AutoDuck.cpp:492
wxString GetSymbol() override
Definition: AutoDuck.cpp:111
bool Process() override
Definition: AutoDuck.cpp:264
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1214
WX_DEFINE_OBJARRAY(AutoDuckRegionArray)
END_EVENT_TABLE()
wxSizerItem * AddSpace(int width, int height)
void OnLeftUp(wxMouseEvent &evt)
Definition: AutoDuck.cpp:868
bool Init() override
Definition: AutoDuck.cpp:204
double LongSamplesToTime(sampleCount pos) const
Convert correctly between an number of samples and an (absolute) time in seconds. ...
Definition: WaveTrack.cpp:1827
const WaveTrack * mControlTrack
Definition: AutoDuck.h:78
wxTextCtrl * mMaximumPauseBox
Definition: AutoDuck.h:86
void SetBorder(int Border)
Definition: ShuttleGui.h:251
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
double mOuterFadeDownLen
Definition: AutoDuck.h:73
#define FADE_DOWN_START
Definition: AutoDuck.cpp:595
double mInnerFadeUpLen
Definition: AutoDuck.h:72
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:458
double mT0
Definition: Effect.h:459
wxPoint mMoveStartControlPoints[AUTO_DUCK_PANEL_NUM_CONTROL_POINTS]
Definition: AutoDuck.h:132
static const size_t kBufSize
Definition: AutoDuck.cpp:56
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:982