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